Commit | Line | Data |
---|---|---|
2a7a0216 JM |
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 | * External course functions unit tests | |
19 | * | |
20 | * @package core_course | |
21 | * @category external | |
22 | * @copyright 2012 Jerome Mouneyrac | |
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
24 | */ | |
25 | ||
26 | defined('MOODLE_INTERNAL') || die(); | |
27 | ||
28 | global $CFG; | |
29 | ||
30 | require_once($CFG->dirroot . '/webservice/tests/helpers.php'); | |
31 | ||
32 | /** | |
33 | * External course functions unit tests | |
34 | * | |
35 | * @package core_course | |
36 | * @category external | |
37 | * @copyright 2012 Jerome Mouneyrac | |
38 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
39 | */ | |
8252b7c2 | 40 | class core_course_externallib_testcase extends externallib_advanced_testcase { |
2a7a0216 JM |
41 | |
42 | /** | |
43 | * Tests set up | |
44 | */ | |
45 | protected function setUp() { | |
46 | global $CFG; | |
47 | require_once($CFG->dirroot . '/course/externallib.php'); | |
48 | } | |
49 | ||
50 | /** | |
51 | * Test create_categories | |
52 | */ | |
53 | public function test_create_categories() { | |
54 | ||
55 | global $DB; | |
56 | ||
57 | $this->resetAfterTest(true); | |
58 | ||
59 | // Set the required capabilities by the external function | |
60 | $contextid = context_system::instance()->id; | |
61 | $roleid = $this->assignUserCapability('moodle/category:manage', $contextid); | |
62 | ||
63 | // Create base categories. | |
64 | $category1 = new stdClass(); | |
65 | $category1->name = 'Root Test Category 1'; | |
66 | $category2 = new stdClass(); | |
67 | $category2->name = 'Root Test Category 2'; | |
68 | $category2->idnumber = 'rootcattest2'; | |
69 | $category2->desc = 'Description for root test category 1'; | |
e00f1c66 | 70 | $category2->theme = 'classic'; |
2a7a0216 JM |
71 | $categories = array( |
72 | array('name' => $category1->name, 'parent' => 0), | |
73 | array('name' => $category2->name, 'parent' => 0, 'idnumber' => $category2->idnumber, | |
74 | 'description' => $category2->desc, 'theme' => $category2->theme) | |
75 | ); | |
76 | ||
77 | $createdcats = core_course_external::create_categories($categories); | |
78 | ||
fb695f6e JM |
79 | // We need to execute the return values cleaning process to simulate the web service server. |
80 | $createdcats = external_api::clean_returnvalue(core_course_external::create_categories_returns(), $createdcats); | |
81 | ||
2a7a0216 JM |
82 | // Initially confirm that base data was inserted correctly. |
83 | $this->assertEquals($category1->name, $createdcats[0]['name']); | |
84 | $this->assertEquals($category2->name, $createdcats[1]['name']); | |
85 | ||
86 | // Save the ids. | |
87 | $category1->id = $createdcats[0]['id']; | |
88 | $category2->id = $createdcats[1]['id']; | |
89 | ||
90 | // Create on sub category. | |
91 | $category3 = new stdClass(); | |
92 | $category3->name = 'Sub Root Test Category 3'; | |
93 | $subcategories = array( | |
94 | array('name' => $category3->name, 'parent' => $category1->id) | |
95 | ); | |
96 | ||
97 | $createdsubcats = core_course_external::create_categories($subcategories); | |
98 | ||
fb695f6e JM |
99 | // We need to execute the return values cleaning process to simulate the web service server. |
100 | $createdsubcats = external_api::clean_returnvalue(core_course_external::create_categories_returns(), $createdsubcats); | |
101 | ||
2a7a0216 JM |
102 | // Confirm that sub categories were inserted correctly. |
103 | $this->assertEquals($category3->name, $createdsubcats[0]['name']); | |
104 | ||
105 | // Save the ids. | |
106 | $category3->id = $createdsubcats[0]['id']; | |
107 | ||
108 | // Calling the ws function should provide a new sortorder to give category1, | |
109 | // category2, category3. New course categories are ordered by id not name. | |
110 | $category1 = $DB->get_record('course_categories', array('id' => $category1->id)); | |
111 | $category2 = $DB->get_record('course_categories', array('id' => $category2->id)); | |
112 | $category3 = $DB->get_record('course_categories', array('id' => $category3->id)); | |
113 | ||
db1eed70 MG |
114 | // sortorder sequence (and sortorder) must be: |
115 | // category 1 | |
116 | // category 3 | |
117 | // category 2 | |
118 | $this->assertGreaterThan($category1->sortorder, $category3->sortorder); | |
119 | $this->assertGreaterThan($category3->sortorder, $category2->sortorder); | |
2a7a0216 JM |
120 | |
121 | // Call without required capability | |
122 | $this->unassignUserCapability('moodle/category:manage', $contextid, $roleid); | |
52f3e060 | 123 | $this->expectException('required_capability_exception'); |
2a7a0216 JM |
124 | $createdsubcats = core_course_external::create_categories($subcategories); |
125 | ||
126 | } | |
127 | ||
128 | /** | |
129 | * Test delete categories | |
130 | */ | |
131 | public function test_delete_categories() { | |
132 | global $DB; | |
133 | ||
134 | $this->resetAfterTest(true); | |
135 | ||
136 | // Set the required capabilities by the external function | |
137 | $contextid = context_system::instance()->id; | |
138 | $roleid = $this->assignUserCapability('moodle/category:manage', $contextid); | |
139 | ||
140 | $category1 = self::getDataGenerator()->create_category(); | |
141 | $category2 = self::getDataGenerator()->create_category( | |
142 | array('parent' => $category1->id)); | |
143 | $category3 = self::getDataGenerator()->create_category(); | |
144 | $category4 = self::getDataGenerator()->create_category( | |
145 | array('parent' => $category3->id)); | |
146 | $category5 = self::getDataGenerator()->create_category( | |
147 | array('parent' => $category4->id)); | |
148 | ||
149 | //delete category 1 and 2 + delete category 4, category 5 moved under category 3 | |
150 | core_course_external::delete_categories(array( | |
151 | array('id' => $category1->id, 'recursive' => 1), | |
152 | array('id' => $category4->id) | |
153 | )); | |
154 | ||
155 | //check $category 1 and 2 are deleted | |
156 | $notdeletedcount = $DB->count_records_select('course_categories', | |
157 | 'id IN ( ' . $category1->id . ',' . $category2->id . ',' . $category4->id . ')'); | |
158 | $this->assertEquals(0, $notdeletedcount); | |
159 | ||
160 | //check that $category5 as $category3 for parent | |
161 | $dbcategory5 = $DB->get_record('course_categories', array('id' => $category5->id)); | |
162 | $this->assertEquals($dbcategory5->path, $category3->path . '/' . $category5->id); | |
163 | ||
164 | // Call without required capability | |
165 | $this->unassignUserCapability('moodle/category:manage', $contextid, $roleid); | |
52f3e060 | 166 | $this->expectException('required_capability_exception'); |
2a7a0216 JM |
167 | $createdsubcats = core_course_external::delete_categories( |
168 | array(array('id' => $category3->id))); | |
169 | } | |
170 | ||
171 | /** | |
172 | * Test get categories | |
173 | */ | |
174 | public function test_get_categories() { | |
175 | global $DB; | |
176 | ||
177 | $this->resetAfterTest(true); | |
7d6c58bc JM |
178 | |
179 | $generatedcats = array(); | |
2a7a0216 JM |
180 | $category1data['idnumber'] = 'idnumbercat1'; |
181 | $category1data['name'] = 'Category 1 for PHPunit test'; | |
182 | $category1data['description'] = 'Category 1 description'; | |
183 | $category1data['descriptionformat'] = FORMAT_MOODLE; | |
184 | $category1 = self::getDataGenerator()->create_category($category1data); | |
7d6c58bc | 185 | $generatedcats[$category1->id] = $category1; |
2a7a0216 JM |
186 | $category2 = self::getDataGenerator()->create_category( |
187 | array('parent' => $category1->id)); | |
7d6c58bc | 188 | $generatedcats[$category2->id] = $category2; |
2a7a0216 JM |
189 | $category6 = self::getDataGenerator()->create_category( |
190 | array('parent' => $category1->id, 'visible' => 0)); | |
7d6c58bc | 191 | $generatedcats[$category6->id] = $category6; |
2a7a0216 | 192 | $category3 = self::getDataGenerator()->create_category(); |
7d6c58bc | 193 | $generatedcats[$category3->id] = $category3; |
2a7a0216 JM |
194 | $category4 = self::getDataGenerator()->create_category( |
195 | array('parent' => $category3->id)); | |
7d6c58bc | 196 | $generatedcats[$category4->id] = $category4; |
2a7a0216 JM |
197 | $category5 = self::getDataGenerator()->create_category( |
198 | array('parent' => $category4->id)); | |
7d6c58bc | 199 | $generatedcats[$category5->id] = $category5; |
2a7a0216 JM |
200 | |
201 | // Set the required capabilities by the external function. | |
202 | $context = context_system::instance(); | |
203 | $roleid = $this->assignUserCapability('moodle/category:manage', $context->id); | |
d80533be | 204 | $this->assignUserCapability('moodle/category:viewhiddencategories', $context->id, $roleid); |
2a7a0216 JM |
205 | |
206 | // Retrieve category1 + sub-categories except not visible ones | |
207 | $categories = core_course_external::get_categories(array( | |
208 | array('key' => 'id', 'value' => $category1->id), | |
209 | array('key' => 'visible', 'value' => 1)), 1); | |
210 | ||
fb695f6e JM |
211 | // We need to execute the return values cleaning process to simulate the web service server. |
212 | $categories = external_api::clean_returnvalue(core_course_external::get_categories_returns(), $categories); | |
213 | ||
2a7a0216 JM |
214 | // Check we retrieve the good total number of categories. |
215 | $this->assertEquals(2, count($categories)); | |
216 | ||
217 | // Check the return values | |
7d6c58bc JM |
218 | foreach ($categories as $category) { |
219 | $generatedcat = $generatedcats[$category['id']]; | |
220 | $this->assertEquals($category['idnumber'], $generatedcat->idnumber); | |
221 | $this->assertEquals($category['name'], $generatedcat->name); | |
46be1d58 MG |
222 | // Description was converted to the HTML format. |
223 | $this->assertEquals($category['description'], format_text($generatedcat->description, FORMAT_MOODLE, array('para' => false))); | |
7d6c58bc JM |
224 | $this->assertEquals($category['descriptionformat'], FORMAT_HTML); |
225 | } | |
2a7a0216 | 226 | |
c1da311a JL |
227 | // Check categories by ids. |
228 | $ids = implode(',', array_keys($generatedcats)); | |
229 | $categories = core_course_external::get_categories(array( | |
230 | array('key' => 'ids', 'value' => $ids)), 0); | |
231 | ||
232 | // We need to execute the return values cleaning process to simulate the web service server. | |
233 | $categories = external_api::clean_returnvalue(core_course_external::get_categories_returns(), $categories); | |
234 | ||
235 | // Check we retrieve the good total number of categories. | |
236 | $this->assertEquals(6, count($categories)); | |
237 | // Check ids. | |
238 | $returnedids = []; | |
239 | foreach ($categories as $category) { | |
240 | $returnedids[] = $category['id']; | |
241 | } | |
242 | // Sort the arrays upon comparision. | |
243 | $this->assertEquals(array_keys($generatedcats), $returnedids, '', 0.0, 10, true); | |
244 | ||
2a7a0216 JM |
245 | // Check different params. |
246 | $categories = core_course_external::get_categories(array( | |
247 | array('key' => 'id', 'value' => $category1->id), | |
c1da311a | 248 | array('key' => 'ids', 'value' => $category1->id), |
2a7a0216 JM |
249 | array('key' => 'idnumber', 'value' => $category1->idnumber), |
250 | array('key' => 'visible', 'value' => 1)), 0); | |
fb695f6e JM |
251 | |
252 | // We need to execute the return values cleaning process to simulate the web service server. | |
253 | $categories = external_api::clean_returnvalue(core_course_external::get_categories_returns(), $categories); | |
254 | ||
2a7a0216 JM |
255 | $this->assertEquals(1, count($categories)); |
256 | ||
b8b1be15 JL |
257 | // Same query, but forcing a parameters clean. |
258 | $categories = core_course_external::get_categories(array( | |
259 | array('key' => 'id', 'value' => "$category1->id"), | |
260 | array('key' => 'idnumber', 'value' => $category1->idnumber), | |
261 | array('key' => 'name', 'value' => $category1->name . "<br/>"), | |
262 | array('key' => 'visible', 'value' => '1')), 0); | |
263 | $categories = external_api::clean_returnvalue(core_course_external::get_categories_returns(), $categories); | |
264 | ||
265 | $this->assertEquals(1, count($categories)); | |
266 | ||
2a7a0216 JM |
267 | // Retrieve categories from parent. |
268 | $categories = core_course_external::get_categories(array( | |
269 | array('key' => 'parent', 'value' => $category3->id)), 1); | |
bdf9f4d4 JL |
270 | $categories = external_api::clean_returnvalue(core_course_external::get_categories_returns(), $categories); |
271 | ||
2a7a0216 JM |
272 | $this->assertEquals(2, count($categories)); |
273 | ||
274 | // Retrieve all categories. | |
275 | $categories = core_course_external::get_categories(); | |
fb695f6e JM |
276 | |
277 | // We need to execute the return values cleaning process to simulate the web service server. | |
278 | $categories = external_api::clean_returnvalue(core_course_external::get_categories_returns(), $categories); | |
279 | ||
2a7a0216 JM |
280 | $this->assertEquals($DB->count_records('course_categories'), count($categories)); |
281 | ||
d80533be | 282 | $this->unassignUserCapability('moodle/category:viewhiddencategories', $context->id, $roleid); |
7180cdc7 | 283 | |
d80533be MG |
284 | // Ensure maxdepthcategory is 2 and retrieve all categories without category:viewhiddencategories capability. |
285 | // It should retrieve all visible categories as well. | |
7180cdc7 PFO |
286 | set_config('maxcategorydepth', 2); |
287 | $categories = core_course_external::get_categories(); | |
288 | ||
289 | // We need to execute the return values cleaning process to simulate the web service server. | |
290 | $categories = external_api::clean_returnvalue(core_course_external::get_categories_returns(), $categories); | |
291 | ||
292 | $this->assertEquals($DB->count_records('course_categories', array('visible' => 1)), count($categories)); | |
293 | ||
294 | // Call without required capability (it will fail cause of the search on idnumber). | |
52f3e060 | 295 | $this->expectException('moodle_exception'); |
2a7a0216 JM |
296 | $categories = core_course_external::get_categories(array( |
297 | array('key' => 'id', 'value' => $category1->id), | |
298 | array('key' => 'idnumber', 'value' => $category1->idnumber), | |
299 | array('key' => 'visible', 'value' => 1)), 0); | |
300 | } | |
301 | ||
302 | /** | |
303 | * Test update_categories | |
304 | */ | |
305 | public function test_update_categories() { | |
306 | global $DB; | |
307 | ||
308 | $this->resetAfterTest(true); | |
309 | ||
310 | // Set the required capabilities by the external function | |
311 | $contextid = context_system::instance()->id; | |
312 | $roleid = $this->assignUserCapability('moodle/category:manage', $contextid); | |
313 | ||
314 | // Create base categories. | |
315 | $category1data['idnumber'] = 'idnumbercat1'; | |
316 | $category1data['name'] = 'Category 1 for PHPunit test'; | |
317 | $category1data['description'] = 'Category 1 description'; | |
318 | $category1data['descriptionformat'] = FORMAT_MOODLE; | |
319 | $category1 = self::getDataGenerator()->create_category($category1data); | |
320 | $category2 = self::getDataGenerator()->create_category( | |
321 | array('parent' => $category1->id)); | |
322 | $category3 = self::getDataGenerator()->create_category(); | |
323 | $category4 = self::getDataGenerator()->create_category( | |
324 | array('parent' => $category3->id)); | |
325 | $category5 = self::getDataGenerator()->create_category( | |
326 | array('parent' => $category4->id)); | |
327 | ||
328 | // We update all category1 attribut. | |
329 | // Then we move cat4 and cat5 parent: cat3 => cat1 | |
330 | $categories = array( | |
331 | array('id' => $category1->id, | |
332 | 'name' => $category1->name . '_updated', | |
333 | 'idnumber' => $category1->idnumber . '_updated', | |
334 | 'description' => $category1->description . '_updated', | |
335 | 'descriptionformat' => FORMAT_HTML, | |
336 | 'theme' => $category1->theme), | |
337 | array('id' => $category4->id, 'parent' => $category1->id)); | |
338 | ||
339 | core_course_external::update_categories($categories); | |
340 | ||
341 | // Check the values were updated. | |
342 | $dbcategories = $DB->get_records_select('course_categories', | |
343 | 'id IN (' . $category1->id . ',' . $category2->id . ',' . $category2->id | |
344 | . ',' . $category3->id . ',' . $category4->id . ',' . $category5->id .')'); | |
345 | $this->assertEquals($category1->name . '_updated', | |
346 | $dbcategories[$category1->id]->name); | |
347 | $this->assertEquals($category1->idnumber . '_updated', | |
348 | $dbcategories[$category1->id]->idnumber); | |
349 | $this->assertEquals($category1->description . '_updated', | |
350 | $dbcategories[$category1->id]->description); | |
351 | $this->assertEquals(FORMAT_HTML, $dbcategories[$category1->id]->descriptionformat); | |
352 | ||
353 | // Check that category4 and category5 have been properly moved. | |
354 | $this->assertEquals('/' . $category1->id . '/' . $category4->id, | |
355 | $dbcategories[$category4->id]->path); | |
356 | $this->assertEquals('/' . $category1->id . '/' . $category4->id . '/' . $category5->id, | |
357 | $dbcategories[$category5->id]->path); | |
358 | ||
359 | // Call without required capability. | |
360 | $this->unassignUserCapability('moodle/category:manage', $contextid, $roleid); | |
52f3e060 | 361 | $this->expectException('required_capability_exception'); |
2a7a0216 JM |
362 | core_course_external::update_categories($categories); |
363 | } | |
364 | ||
1dac440f EL |
365 | /** |
366 | * Test create_courses numsections | |
367 | */ | |
368 | public function test_create_course_numsections() { | |
369 | global $DB; | |
370 | ||
371 | $this->resetAfterTest(true); | |
372 | ||
373 | // Set the required capabilities by the external function. | |
374 | $contextid = context_system::instance()->id; | |
375 | $roleid = $this->assignUserCapability('moodle/course:create', $contextid); | |
376 | $this->assignUserCapability('moodle/course:visibility', $contextid, $roleid); | |
377 | ||
378 | $numsections = 10; | |
379 | $category = self::getDataGenerator()->create_category(); | |
380 | ||
381 | // Create base categories. | |
382 | $course1['fullname'] = 'Test course 1'; | |
383 | $course1['shortname'] = 'Testcourse1'; | |
384 | $course1['categoryid'] = $category->id; | |
385 | $course1['courseformatoptions'][] = array('name' => 'numsections', 'value' => $numsections); | |
386 | ||
387 | $courses = array($course1); | |
388 | ||
389 | $createdcourses = core_course_external::create_courses($courses); | |
390 | foreach ($createdcourses as $createdcourse) { | |
391 | $existingsections = $DB->get_records('course_sections', array('course' => $createdcourse['id'])); | |
392 | $modinfo = get_fast_modinfo($createdcourse['id']); | |
393 | $sections = $modinfo->get_section_info_all(); | |
394 | $this->assertEquals(count($sections), $numsections + 1); // Includes generic section. | |
395 | $this->assertEquals(count($existingsections), $numsections + 1); // Includes generic section. | |
396 | } | |
397 | } | |
398 | ||
2a7a0216 JM |
399 | /** |
400 | * Test create_courses | |
401 | */ | |
402 | public function test_create_courses() { | |
403 | global $DB; | |
404 | ||
405 | $this->resetAfterTest(true); | |
406 | ||
821676f5 JM |
407 | // Enable course completion. |
408 | set_config('enablecompletion', 1); | |
141e7d87 DP |
409 | // Enable course themes. |
410 | set_config('allowcoursethemes', 1); | |
821676f5 | 411 | |
bbf60b14 | 412 | // Custom fields. |
7a0162f1 DM |
413 | $fieldcategory = self::getDataGenerator()->create_custom_field_category(['name' => 'Other fields']); |
414 | ||
415 | $customfield = ['shortname' => 'test', 'name' => 'Custom field', 'type' => 'text', | |
416 | 'categoryid' => $fieldcategory->get('id'), | |
417 | 'configdata' => ['visibility' => \core_course\customfield\course_handler::VISIBLETOALL]]; | |
418 | $field = self::getDataGenerator()->create_custom_field($customfield); | |
419 | ||
2a7a0216 JM |
420 | // Set the required capabilities by the external function |
421 | $contextid = context_system::instance()->id; | |
422 | $roleid = $this->assignUserCapability('moodle/course:create', $contextid); | |
423 | $this->assignUserCapability('moodle/course:visibility', $contextid, $roleid); | |
4a9624af | 424 | $this->assignUserCapability('moodle/course:setforcedlanguage', $contextid, $roleid); |
2a7a0216 JM |
425 | |
426 | $category = self::getDataGenerator()->create_category(); | |
427 | ||
428 | // Create base categories. | |
429 | $course1['fullname'] = 'Test course 1'; | |
430 | $course1['shortname'] = 'Testcourse1'; | |
431 | $course1['categoryid'] = $category->id; | |
432 | $course2['fullname'] = 'Test course 2'; | |
433 | $course2['shortname'] = 'Testcourse2'; | |
434 | $course2['categoryid'] = $category->id; | |
435 | $course2['idnumber'] = 'testcourse2idnumber'; | |
436 | $course2['summary'] = 'Description for course 2'; | |
437 | $course2['summaryformat'] = FORMAT_MOODLE; | |
438 | $course2['format'] = 'weeks'; | |
439 | $course2['showgrades'] = 1; | |
440 | $course2['newsitems'] = 3; | |
fbcdb0d7 DNA |
441 | $course2['startdate'] = 1420092000; // 01/01/2015. |
442 | $course2['enddate'] = 1422669600; // 01/31/2015. | |
2a7a0216 JM |
443 | $course2['numsections'] = 4; |
444 | $course2['maxbytes'] = 100000; | |
445 | $course2['showreports'] = 1; | |
446 | $course2['visible'] = 0; | |
447 | $course2['hiddensections'] = 0; | |
448 | $course2['groupmode'] = 0; | |
449 | $course2['groupmodeforce'] = 0; | |
450 | $course2['defaultgroupingid'] = 0; | |
451 | $course2['enablecompletion'] = 1; | |
2a7a0216 JM |
452 | $course2['completionnotify'] = 1; |
453 | $course2['lang'] = 'en'; | |
e00f1c66 | 454 | $course2['forcetheme'] = 'classic'; |
a526c706 | 455 | $course2['courseformatoptions'][] = array('name' => 'automaticenddate', 'value' => 0); |
0e984d98 MG |
456 | $course3['fullname'] = 'Test course 3'; |
457 | $course3['shortname'] = 'Testcourse3'; | |
458 | $course3['categoryid'] = $category->id; | |
459 | $course3['format'] = 'topics'; | |
460 | $course3options = array('numsections' => 8, | |
461 | 'hiddensections' => 1, | |
462 | 'coursedisplay' => 1); | |
8d8d4da4 | 463 | $course3['courseformatoptions'] = array(); |
0e984d98 | 464 | foreach ($course3options as $key => $value) { |
8d8d4da4 | 465 | $course3['courseformatoptions'][] = array('name' => $key, 'value' => $value); |
0e984d98 | 466 | } |
7a0162f1 DM |
467 | $course4['fullname'] = 'Test course with custom fields'; |
468 | $course4['shortname'] = 'Testcoursecustomfields'; | |
469 | $course4['categoryid'] = $category->id; | |
470 | $course4['customfields'] = [['shortname' => $customfield['shortname'], 'value' => 'Test value']]; | |
471 | $courses = array($course4, $course1, $course2, $course3); | |
2a7a0216 JM |
472 | |
473 | $createdcourses = core_course_external::create_courses($courses); | |
474 | ||
fb695f6e JM |
475 | // We need to execute the return values cleaning process to simulate the web service server. |
476 | $createdcourses = external_api::clean_returnvalue(core_course_external::create_courses_returns(), $createdcourses); | |
477 | ||
2a7a0216 | 478 | // Check that right number of courses were created. |
7a0162f1 | 479 | $this->assertEquals(4, count($createdcourses)); |
2a7a0216 JM |
480 | |
481 | // Check that the courses were correctly created. | |
482 | foreach ($createdcourses as $createdcourse) { | |
850acb35 | 483 | $courseinfo = course_get_format($createdcourse['id'])->get_course(); |
2a7a0216 JM |
484 | |
485 | if ($createdcourse['shortname'] == $course2['shortname']) { | |
850acb35 MG |
486 | $this->assertEquals($courseinfo->fullname, $course2['fullname']); |
487 | $this->assertEquals($courseinfo->shortname, $course2['shortname']); | |
488 | $this->assertEquals($courseinfo->category, $course2['categoryid']); | |
489 | $this->assertEquals($courseinfo->idnumber, $course2['idnumber']); | |
490 | $this->assertEquals($courseinfo->summary, $course2['summary']); | |
491 | $this->assertEquals($courseinfo->summaryformat, $course2['summaryformat']); | |
492 | $this->assertEquals($courseinfo->format, $course2['format']); | |
493 | $this->assertEquals($courseinfo->showgrades, $course2['showgrades']); | |
494 | $this->assertEquals($courseinfo->newsitems, $course2['newsitems']); | |
495 | $this->assertEquals($courseinfo->startdate, $course2['startdate']); | |
fbcdb0d7 | 496 | $this->assertEquals($courseinfo->enddate, $course2['enddate']); |
89b909f6 | 497 | $this->assertEquals(course_get_format($createdcourse['id'])->get_last_section_number(), $course2['numsections']); |
850acb35 MG |
498 | $this->assertEquals($courseinfo->maxbytes, $course2['maxbytes']); |
499 | $this->assertEquals($courseinfo->showreports, $course2['showreports']); | |
500 | $this->assertEquals($courseinfo->visible, $course2['visible']); | |
501 | $this->assertEquals($courseinfo->hiddensections, $course2['hiddensections']); | |
502 | $this->assertEquals($courseinfo->groupmode, $course2['groupmode']); | |
503 | $this->assertEquals($courseinfo->groupmodeforce, $course2['groupmodeforce']); | |
504 | $this->assertEquals($courseinfo->defaultgroupingid, $course2['defaultgroupingid']); | |
505 | $this->assertEquals($courseinfo->completionnotify, $course2['completionnotify']); | |
506 | $this->assertEquals($courseinfo->lang, $course2['lang']); | |
141e7d87 | 507 | $this->assertEquals($courseinfo->theme, $course2['forcetheme']); |
2a7a0216 | 508 | |
821676f5 JM |
509 | // We enabled completion at the beginning of the test. |
510 | $this->assertEquals($courseinfo->enablecompletion, $course2['enablecompletion']); | |
2a7a0216 JM |
511 | |
512 | } else if ($createdcourse['shortname'] == $course1['shortname']) { | |
513 | $courseconfig = get_config('moodlecourse'); | |
850acb35 MG |
514 | $this->assertEquals($courseinfo->fullname, $course1['fullname']); |
515 | $this->assertEquals($courseinfo->shortname, $course1['shortname']); | |
516 | $this->assertEquals($courseinfo->category, $course1['categoryid']); | |
517 | $this->assertEquals($courseinfo->summaryformat, FORMAT_HTML); | |
518 | $this->assertEquals($courseinfo->format, $courseconfig->format); | |
519 | $this->assertEquals($courseinfo->showgrades, $courseconfig->showgrades); | |
520 | $this->assertEquals($courseinfo->newsitems, $courseconfig->newsitems); | |
521 | $this->assertEquals($courseinfo->maxbytes, $courseconfig->maxbytes); | |
522 | $this->assertEquals($courseinfo->showreports, $courseconfig->showreports); | |
523 | $this->assertEquals($courseinfo->groupmode, $courseconfig->groupmode); | |
524 | $this->assertEquals($courseinfo->groupmodeforce, $courseconfig->groupmodeforce); | |
525 | $this->assertEquals($courseinfo->defaultgroupingid, 0); | |
0e984d98 | 526 | } else if ($createdcourse['shortname'] == $course3['shortname']) { |
850acb35 MG |
527 | $this->assertEquals($courseinfo->fullname, $course3['fullname']); |
528 | $this->assertEquals($courseinfo->shortname, $course3['shortname']); | |
529 | $this->assertEquals($courseinfo->category, $course3['categoryid']); | |
530 | $this->assertEquals($courseinfo->format, $course3['format']); | |
531 | $this->assertEquals($courseinfo->hiddensections, $course3options['hiddensections']); | |
89b909f6 MG |
532 | $this->assertEquals(course_get_format($createdcourse['id'])->get_last_section_number(), |
533 | $course3options['numsections']); | |
850acb35 | 534 | $this->assertEquals($courseinfo->coursedisplay, $course3options['coursedisplay']); |
7a0162f1 DM |
535 | } else if ($createdcourse['shortname'] == $course4['shortname']) { |
536 | $this->assertEquals($courseinfo->fullname, $course4['fullname']); | |
537 | $this->assertEquals($courseinfo->shortname, $course4['shortname']); | |
538 | $this->assertEquals($courseinfo->category, $course4['categoryid']); | |
539 | ||
540 | $handler = core_course\customfield\course_handler::create(); | |
541 | $customfields = $handler->export_instance_data_object($createdcourse['id']); | |
542 | $this->assertEquals((object)['test' => 'Test value'], $customfields); | |
2a7a0216 | 543 | } else { |
75c597da | 544 | throw new moodle_exception('Unexpected shortname'); |
2a7a0216 JM |
545 | } |
546 | } | |
547 | ||
548 | // Call without required capability | |
549 | $this->unassignUserCapability('moodle/course:create', $contextid, $roleid); | |
52f3e060 | 550 | $this->expectException('required_capability_exception'); |
2a7a0216 JM |
551 | $createdsubcats = core_course_external::create_courses($courses); |
552 | } | |
553 | ||
554 | /** | |
555 | * Test delete_courses | |
556 | */ | |
557 | public function test_delete_courses() { | |
558 | global $DB, $USER; | |
559 | ||
560 | $this->resetAfterTest(true); | |
561 | ||
562 | // Admin can delete a course. | |
563 | $this->setAdminUser(); | |
564 | // Validate_context() will fail as the email is not set by $this->setAdminUser(). | |
0fe86bbd | 565 | $USER->email = 'emailtopass@example.com'; |
2a7a0216 JM |
566 | |
567 | $course1 = self::getDataGenerator()->create_course(); | |
568 | $course2 = self::getDataGenerator()->create_course(); | |
569 | $course3 = self::getDataGenerator()->create_course(); | |
570 | ||
571 | // Delete courses. | |
70f37963 JH |
572 | $result = core_course_external::delete_courses(array($course1->id, $course2->id)); |
573 | $result = external_api::clean_returnvalue(core_course_external::delete_courses_returns(), $result); | |
574 | // Check for 0 warnings. | |
575 | $this->assertEquals(0, count($result['warnings'])); | |
2a7a0216 JM |
576 | |
577 | // Check $course 1 and 2 are deleted. | |
578 | $notdeletedcount = $DB->count_records_select('course', | |
579 | 'id IN ( ' . $course1->id . ',' . $course2->id . ')'); | |
580 | $this->assertEquals(0, $notdeletedcount); | |
581 | ||
70f37963 JH |
582 | // Try to delete non-existent course. |
583 | $result = core_course_external::delete_courses(array($course1->id)); | |
584 | $result = external_api::clean_returnvalue(core_course_external::delete_courses_returns(), $result); | |
585 | // Check for 1 warnings. | |
586 | $this->assertEquals(1, count($result['warnings'])); | |
587 | ||
588 | // Try to delete Frontpage course. | |
589 | $result = core_course_external::delete_courses(array(0)); | |
590 | $result = external_api::clean_returnvalue(core_course_external::delete_courses_returns(), $result); | |
591 | // Check for 1 warnings. | |
592 | $this->assertEquals(1, count($result['warnings'])); | |
593 | ||
594 | // Fail when the user has access to course (enrolled) but does not have permission or is not admin. | |
595 | $student1 = self::getDataGenerator()->create_user(); | |
596 | $studentrole = $DB->get_record('role', array('shortname' => 'student')); | |
597 | $this->getDataGenerator()->enrol_user($student1->id, | |
598 | $course3->id, | |
599 | $studentrole->id); | |
600 | $this->setUser($student1); | |
601 | $result = core_course_external::delete_courses(array($course3->id)); | |
602 | $result = external_api::clean_returnvalue(core_course_external::delete_courses_returns(), $result); | |
603 | // Check for 1 warnings. | |
604 | $this->assertEquals(1, count($result['warnings'])); | |
605 | ||
2a7a0216 JM |
606 | // Fail when the user is not allow to access the course (enrolled) or is not admin. |
607 | $this->setGuestUser(); | |
52f3e060 | 608 | $this->expectException('require_login_exception'); |
70f37963 JH |
609 | |
610 | $result = core_course_external::delete_courses(array($course3->id)); | |
611 | $result = external_api::clean_returnvalue(core_course_external::delete_courses_returns(), $result); | |
2a7a0216 JM |
612 | } |
613 | ||
614 | /** | |
615 | * Test get_courses | |
616 | */ | |
617 | public function test_get_courses () { | |
618 | global $DB; | |
619 | ||
620 | $this->resetAfterTest(true); | |
621 | ||
7d6c58bc | 622 | $generatedcourses = array(); |
2a7a0216 | 623 | $coursedata['idnumber'] = 'idnumbercourse1'; |
d889b587 JL |
624 | // Adding tags here to check that format_string is applied. |
625 | $coursedata['fullname'] = '<b>Course 1 for PHPunit test</b>'; | |
626 | $coursedata['shortname'] = '<b>Course 1 for PHPunit test</b>'; | |
2a7a0216 JM |
627 | $coursedata['summary'] = 'Course 1 description'; |
628 | $coursedata['summaryformat'] = FORMAT_MOODLE; | |
629 | $course1 = self::getDataGenerator()->create_course($coursedata); | |
245d354c | 630 | |
7a0162f1 DM |
631 | $fieldcategory = self::getDataGenerator()->create_custom_field_category( |
632 | ['name' => 'Other fields']); | |
633 | ||
634 | $customfield = ['shortname' => 'test', 'name' => 'Custom field', 'type' => 'text', | |
635 | 'categoryid' => $fieldcategory->get('id')]; | |
636 | $field = self::getDataGenerator()->create_custom_field($customfield); | |
637 | ||
638 | $customfieldvalue = ['shortname' => 'test', 'value' => 'Test value']; | |
639 | ||
7d6c58bc | 640 | $generatedcourses[$course1->id] = $course1; |
2a7a0216 | 641 | $course2 = self::getDataGenerator()->create_course(); |
7d6c58bc | 642 | $generatedcourses[$course2->id] = $course2; |
0e984d98 | 643 | $course3 = self::getDataGenerator()->create_course(array('format' => 'topics')); |
7d6c58bc | 644 | $generatedcourses[$course3->id] = $course3; |
7a0162f1 DM |
645 | $course4 = self::getDataGenerator()->create_course(['customfields' => [$customfieldvalue]]); |
646 | $generatedcourses[$course4->id] = $course4; | |
2a7a0216 JM |
647 | |
648 | // Set the required capabilities by the external function. | |
649 | $context = context_system::instance(); | |
650 | $roleid = $this->assignUserCapability('moodle/course:view', $context->id); | |
651 | $this->assignUserCapability('moodle/course:update', | |
652 | context_course::instance($course1->id)->id, $roleid); | |
653 | $this->assignUserCapability('moodle/course:update', | |
654 | context_course::instance($course2->id)->id, $roleid); | |
655 | $this->assignUserCapability('moodle/course:update', | |
656 | context_course::instance($course3->id)->id, $roleid); | |
7a0162f1 DM |
657 | $this->assignUserCapability('moodle/course:update', |
658 | context_course::instance($course4->id)->id, $roleid); | |
2a7a0216 JM |
659 | |
660 | $courses = core_course_external::get_courses(array('ids' => | |
7a0162f1 | 661 | array($course1->id, $course2->id, $course4->id))); |
2a7a0216 | 662 | |
fb695f6e JM |
663 | // We need to execute the return values cleaning process to simulate the web service server. |
664 | $courses = external_api::clean_returnvalue(core_course_external::get_courses_returns(), $courses); | |
665 | ||
7a0162f1 DM |
666 | // Check we retrieve the good total number of courses. |
667 | $this->assertEquals(3, count($courses)); | |
2a7a0216 | 668 | |
7d6c58bc | 669 | foreach ($courses as $course) { |
d889b587 | 670 | $coursecontext = context_course::instance($course['id']); |
7d6c58bc JM |
671 | $dbcourse = $generatedcourses[$course['id']]; |
672 | $this->assertEquals($course['idnumber'], $dbcourse->idnumber); | |
d889b587 JL |
673 | $this->assertEquals($course['fullname'], external_format_string($dbcourse->fullname, $coursecontext->id)); |
674 | $this->assertEquals($course['displayname'], external_format_string(get_course_display_name_for_list($dbcourse), | |
675 | $coursecontext->id)); | |
46be1d58 MG |
676 | // Summary was converted to the HTML format. |
677 | $this->assertEquals($course['summary'], format_text($dbcourse->summary, FORMAT_MOODLE, array('para' => false))); | |
7d6c58bc | 678 | $this->assertEquals($course['summaryformat'], FORMAT_HTML); |
d889b587 | 679 | $this->assertEquals($course['shortname'], external_format_string($dbcourse->shortname, $coursecontext->id)); |
7d6c58bc JM |
680 | $this->assertEquals($course['categoryid'], $dbcourse->category); |
681 | $this->assertEquals($course['format'], $dbcourse->format); | |
682 | $this->assertEquals($course['showgrades'], $dbcourse->showgrades); | |
683 | $this->assertEquals($course['newsitems'], $dbcourse->newsitems); | |
684 | $this->assertEquals($course['startdate'], $dbcourse->startdate); | |
fbcdb0d7 | 685 | $this->assertEquals($course['enddate'], $dbcourse->enddate); |
89b909f6 | 686 | $this->assertEquals($course['numsections'], course_get_format($dbcourse)->get_last_section_number()); |
7d6c58bc JM |
687 | $this->assertEquals($course['maxbytes'], $dbcourse->maxbytes); |
688 | $this->assertEquals($course['showreports'], $dbcourse->showreports); | |
689 | $this->assertEquals($course['visible'], $dbcourse->visible); | |
690 | $this->assertEquals($course['hiddensections'], $dbcourse->hiddensections); | |
691 | $this->assertEquals($course['groupmode'], $dbcourse->groupmode); | |
692 | $this->assertEquals($course['groupmodeforce'], $dbcourse->groupmodeforce); | |
693 | $this->assertEquals($course['defaultgroupingid'], $dbcourse->defaultgroupingid); | |
694 | $this->assertEquals($course['completionnotify'], $dbcourse->completionnotify); | |
695 | $this->assertEquals($course['lang'], $dbcourse->lang); | |
696 | $this->assertEquals($course['forcetheme'], $dbcourse->theme); | |
7d6c58bc | 697 | $this->assertEquals($course['enablecompletion'], $dbcourse->enablecompletion); |
0e984d98 | 698 | if ($dbcourse->format === 'topics') { |
8d8d4da4 | 699 | $this->assertEquals($course['courseformatoptions'], array( |
8d8d4da4 MG |
700 | array('name' => 'hiddensections', 'value' => $dbcourse->hiddensections), |
701 | array('name' => 'coursedisplay', 'value' => $dbcourse->coursedisplay), | |
0e984d98 MG |
702 | )); |
703 | } | |
7a0162f1 DM |
704 | if ($dbcourse->id == 4) { |
705 | $this->assertEquals($course['customfields'], [array_merge($customfield, $customfieldvalue)]); | |
706 | } | |
7d6c58bc | 707 | } |
2a7a0216 JM |
708 | |
709 | // Get all courses in the DB | |
710 | $courses = core_course_external::get_courses(array()); | |
fb695f6e JM |
711 | |
712 | // We need to execute the return values cleaning process to simulate the web service server. | |
713 | $courses = external_api::clean_returnvalue(core_course_external::get_courses_returns(), $courses); | |
714 | ||
2a7a0216 JM |
715 | $this->assertEquals($DB->count_records('course'), count($courses)); |
716 | } | |
717 | ||
a0cf7ee8 MG |
718 | /** |
719 | * Test get_courses without capability | |
720 | */ | |
721 | public function test_get_courses_without_capability() { | |
722 | $this->resetAfterTest(true); | |
723 | ||
724 | $course1 = $this->getDataGenerator()->create_course(); | |
725 | $this->setUser($this->getDataGenerator()->create_user()); | |
726 | ||
727 | // No permissions are required to get the site course. | |
728 | $courses = core_course_external::get_courses(array('ids' => [SITEID])); | |
729 | $courses = external_api::clean_returnvalue(core_course_external::get_courses_returns(), $courses); | |
730 | ||
731 | $this->assertEquals(1, count($courses)); | |
732 | $this->assertEquals('PHPUnit test site', $courses[0]['fullname']); | |
733 | $this->assertEquals('site', $courses[0]['format']); | |
734 | ||
735 | // Requesting course without being enrolled or capability to view it will throw an exception. | |
736 | try { | |
737 | core_course_external::get_courses(array('ids' => [$course1->id])); | |
738 | $this->fail('Exception expected'); | |
739 | } catch (moodle_exception $e) { | |
740 | $this->assertEquals(1, preg_match('/Course or activity not accessible. \(Not enrolled\)/', $e->getMessage())); | |
741 | } | |
742 | } | |
743 | ||
740c354f JL |
744 | /** |
745 | * Test search_courses | |
746 | */ | |
747 | public function test_search_courses () { | |
748 | ||
74fa9f76 | 749 | global $DB; |
740c354f JL |
750 | |
751 | $this->resetAfterTest(true); | |
752 | $this->setAdminUser(); | |
753 | $generatedcourses = array(); | |
754 | $coursedata1['fullname'] = 'FIRST COURSE'; | |
755 | $course1 = self::getDataGenerator()->create_course($coursedata1); | |
245d354c DW |
756 | |
757 | $page = new moodle_page(); | |
758 | $page->set_course($course1); | |
759 | $page->blocks->add_blocks([BLOCK_POS_LEFT => ['news_items'], BLOCK_POS_RIGHT => []], 'course-view-*'); | |
760 | ||
740c354f JL |
761 | $coursedata2['fullname'] = 'SECOND COURSE'; |
762 | $course2 = self::getDataGenerator()->create_course($coursedata2); | |
245d354c DW |
763 | |
764 | $page = new moodle_page(); | |
765 | $page->set_course($course2); | |
766 | $page->blocks->add_blocks([BLOCK_POS_LEFT => ['news_items'], BLOCK_POS_RIGHT => []], 'course-view-*'); | |
7a0162f1 | 767 | |
740c354f JL |
768 | // Search by name. |
769 | $results = core_course_external::search_courses('search', 'FIRST'); | |
770 | $results = external_api::clean_returnvalue(core_course_external::search_courses_returns(), $results); | |
771 | $this->assertEquals($coursedata1['fullname'], $results['courses'][0]['fullname']); | |
772 | $this->assertCount(1, $results['courses']); | |
773 | ||
774 | // Create the forum. | |
775 | $record = new stdClass(); | |
776 | $record->introformat = FORMAT_HTML; | |
777 | $record->course = $course2->id; | |
778 | // Set Aggregate type = Average of ratings. | |
779 | $forum = self::getDataGenerator()->create_module('forum', $record); | |
780 | ||
781 | // Search by module. | |
782 | $results = core_course_external::search_courses('modulelist', 'forum'); | |
783 | $results = external_api::clean_returnvalue(core_course_external::search_courses_returns(), $results); | |
784 | $this->assertEquals(1, $results['total']); | |
785 | ||
786 | // Enable coursetag option. | |
787 | set_config('block_tags_showcoursetags', true); | |
788 | // Add tag 'TAG-LABEL ON SECOND COURSE' to Course2. | |
74fa9f76 MG |
789 | core_tag_tag::set_item_tags('core', 'course', $course2->id, context_course::instance($course2->id), |
790 | array('TAG-LABEL ON SECOND COURSE')); | |
791 | $taginstance = $DB->get_record('tag_instance', | |
792 | array('itemtype' => 'course', 'itemid' => $course2->id), '*', MUST_EXIST); | |
7a0162f1 | 793 | |
740c354f JL |
794 | // Search by tagid. |
795 | $results = core_course_external::search_courses('tagid', $taginstance->tagid); | |
796 | $results = external_api::clean_returnvalue(core_course_external::search_courses_returns(), $results); | |
797 | $this->assertEquals($coursedata2['fullname'], $results['courses'][0]['fullname']); | |
798 | ||
799 | // Search by block (use news_items default block). | |
800 | $blockid = $DB->get_field('block', 'id', array('name' => 'news_items')); | |
801 | $results = core_course_external::search_courses('blocklist', $blockid); | |
802 | $results = external_api::clean_returnvalue(core_course_external::search_courses_returns(), $results); | |
803 | $this->assertEquals(2, $results['total']); | |
804 | ||
805 | // Now as a normal user. | |
806 | $user = self::getDataGenerator()->create_user(); | |
935ee1c6 EM |
807 | |
808 | // Add a 3rd, hidden, course we shouldn't see, even when enrolled as student. | |
809 | $coursedata3['fullname'] = 'HIDDEN COURSE'; | |
810 | $coursedata3['visible'] = 0; | |
811 | $course3 = self::getDataGenerator()->create_course($coursedata3); | |
812 | $this->getDataGenerator()->enrol_user($user->id, $course3->id, 'student'); | |
813 | ||
814 | $this->getDataGenerator()->enrol_user($user->id, $course2->id, 'student'); | |
740c354f JL |
815 | $this->setUser($user); |
816 | ||
817 | $results = core_course_external::search_courses('search', 'FIRST'); | |
818 | $results = external_api::clean_returnvalue(core_course_external::search_courses_returns(), $results); | |
819 | $this->assertCount(1, $results['courses']); | |
820 | $this->assertEquals(1, $results['total']); | |
821 | $this->assertEquals($coursedata1['fullname'], $results['courses'][0]['fullname']); | |
822 | ||
7a0162f1 | 823 | // Check that we can see all courses without the limit to enrolled setting. |
935ee1c6 EM |
824 | $results = core_course_external::search_courses('search', 'COURSE', 0, 0, array(), 0); |
825 | $results = external_api::clean_returnvalue(core_course_external::search_courses_returns(), $results); | |
826 | $this->assertCount(2, $results['courses']); | |
827 | $this->assertEquals(2, $results['total']); | |
828 | ||
829 | // Check that we only see our enrolled course when limiting. | |
830 | $results = core_course_external::search_courses('search', 'COURSE', 0, 0, array(), 1); | |
831 | $results = external_api::clean_returnvalue(core_course_external::search_courses_returns(), $results); | |
832 | $this->assertCount(1, $results['courses']); | |
833 | $this->assertEquals(1, $results['total']); | |
834 | $this->assertEquals($coursedata2['fullname'], $results['courses'][0]['fullname']); | |
835 | ||
740c354f | 836 | // Search by block (use news_items default block). Should fail (only admins allowed). |
52f3e060 | 837 | $this->expectException('required_capability_exception'); |
740c354f | 838 | $results = core_course_external::search_courses('blocklist', $blockid); |
740c354f JL |
839 | } |
840 | ||
2a7a0216 | 841 | /** |
8a5346a7 JL |
842 | * Create a course with contents |
843 | * @return array A list with the course object and course modules objects | |
2a7a0216 | 844 | */ |
8a5346a7 | 845 | private function prepare_get_course_contents_test() { |
10b88bf2 JL |
846 | global $DB, $CFG; |
847 | ||
848 | $CFG->allowstealth = 1; // Allow stealth activities. | |
1206a487 | 849 | $CFG->enablecompletion = true; |
1206a487 | 850 | $course = self::getDataGenerator()->create_course(['numsections' => 4, 'enablecompletion' => 1]); |
10b88bf2 | 851 | |
487bc1b6 JM |
852 | $forumdescription = 'This is the forum description'; |
853 | $forum = $this->getDataGenerator()->create_module('forum', | |
1206a487 JL |
854 | array('course' => $course->id, 'intro' => $forumdescription, 'trackingtype' => 2), |
855 | array('showdescription' => true, 'completion' => COMPLETION_TRACKING_MANUAL)); | |
2a7a0216 | 856 | $forumcm = get_coursemodule_from_id('forum', $forum->cmid); |
1206a487 JL |
857 | // Add discussions to the tracking forced forum. |
858 | $record = new stdClass(); | |
859 | $record->course = $course->id; | |
860 | $record->userid = 0; | |
861 | $record->forum = $forum->id; | |
862 | $discussionforce = $this->getDataGenerator()->get_plugin_generator('mod_forum')->create_discussion($record); | |
863 | $data = $this->getDataGenerator()->create_module('data', | |
864 | array('assessed' => 1, 'scale' => 100, 'course' => $course->id, 'completion' => 2, 'completionentries' => 3)); | |
865 | $datacm = get_coursemodule_from_instance('data', $data->id); | |
8a5346a7 | 866 | $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id)); |
2a7a0216 | 867 | $pagecm = get_coursemodule_from_instance('page', $page->id); |
10b88bf2 JL |
868 | // This is an stealth page (set by visibleoncoursepage). |
869 | $pagestealth = $this->getDataGenerator()->create_module('page', array('course' => $course->id, 'visibleoncoursepage' => 0)); | |
487bc1b6 JM |
870 | $labeldescription = 'This is a very long label to test if more than 50 characters are returned. |
871 | So bla bla bla bla <b>bold bold bold</b> bla bla bla bla.'; | |
872 | $label = $this->getDataGenerator()->create_module('label', array('course' => $course->id, | |
76724712 | 873 | 'intro' => $labeldescription, 'completion' => COMPLETION_TRACKING_MANUAL)); |
487bc1b6 | 874 | $labelcm = get_coursemodule_from_instance('label', $label->id); |
428f7864 | 875 | $tomorrow = time() + DAYSECS; |
935429e2 | 876 | // Module with availability restrictions not met. |
76724712 MJ |
877 | $availability = '{"op":"&","c":[{"type":"date","d":">=","t":' . $tomorrow . '},' |
878 | .'{"type":"completion","cm":' . $label->cmid .',"e":1}],"showc":[true,true]}'; | |
935429e2 | 879 | $url = $this->getDataGenerator()->create_module('url', |
1206a487 JL |
880 | array('course' => $course->id, 'name' => 'URL: % & $ ../', 'section' => 2, 'display' => RESOURCELIB_DISPLAY_POPUP, |
881 | 'popupwidth' => 100, 'popupheight' => 100), | |
76724712 | 882 | array('availability' => $availability)); |
8a5346a7 | 883 | $urlcm = get_coursemodule_from_instance('url', $url->id); |
935429e2 JL |
884 | // Module for the last section. |
885 | $this->getDataGenerator()->create_module('url', | |
886 | array('course' => $course->id, 'name' => 'URL for last section', 'section' => 3)); | |
887 | // Module for section 1 with availability restrictions met. | |
888 | $yesterday = time() - DAYSECS; | |
889 | $this->getDataGenerator()->create_module('url', | |
890 | array('course' => $course->id, 'name' => 'URL restrictions met', 'section' => 1), | |
891 | array('availability' => '{"op":"&","c":[{"type":"date","d":">=","t":'. $yesterday .'}],"showc":[true]}')); | |
2a7a0216 JM |
892 | |
893 | // Set the required capabilities by the external function. | |
894 | $context = context_course::instance($course->id); | |
895 | $roleid = $this->assignUserCapability('moodle/course:view', $context->id); | |
896 | $this->assignUserCapability('moodle/course:update', $context->id, $roleid); | |
12306a9f | 897 | $this->assignUserCapability('mod/data:view', $context->id, $roleid); |
2a7a0216 | 898 | |
6a1131e2 JL |
899 | $conditions = array('course' => $course->id, 'section' => 2); |
900 | $DB->set_field('course_sections', 'summary', 'Text with iframe <iframe src="https://moodle.org"></iframe>', $conditions); | |
935429e2 | 901 | |
10b88bf2 | 902 | // Add date availability condition not met for section 3. |
428f7864 | 903 | $availability = '{"op":"&","c":[{"type":"date","d":">=","t":' . $tomorrow . '}],"showc":[true]}'; |
935429e2 JL |
904 | $DB->set_field('course_sections', 'availability', $availability, |
905 | array('course' => $course->id, 'section' => 3)); | |
10b88bf2 JL |
906 | |
907 | // Create resource for last section. | |
908 | $pageinhiddensection = $this->getDataGenerator()->create_module('page', | |
909 | array('course' => $course->id, 'name' => 'Page in hidden section', 'section' => 4)); | |
910 | // Set not visible last section. | |
911 | $DB->set_field('course_sections', 'visible', 0, | |
912 | array('course' => $course->id, 'section' => 4)); | |
913 | ||
6a1131e2 JL |
914 | rebuild_course_cache($course->id, true); |
915 | ||
8a5346a7 JL |
916 | return array($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm); |
917 | } | |
918 | ||
919 | /** | |
920 | * Test get_course_contents | |
921 | */ | |
922 | public function test_get_course_contents() { | |
716c103d | 923 | global $CFG; |
8a5346a7 | 924 | $this->resetAfterTest(true); |
2a7a0216 | 925 | |
716c103d | 926 | $CFG->forum_allowforcedreadtracking = 1; |
8a5346a7 JL |
927 | list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test(); |
928 | ||
10b88bf2 JL |
929 | // We first run the test as admin. |
930 | $this->setAdminUser(); | |
8a5346a7 | 931 | $sections = core_course_external::get_course_contents($course->id, array()); |
fb695f6e | 932 | // We need to execute the return values cleaning process to simulate the web service server. |
487bc1b6 JM |
933 | $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections); |
934 | ||
487bc1b6 JM |
935 | $modinfo = get_fast_modinfo($course); |
936 | $testexecuted = 0; | |
935429e2 | 937 | foreach ($sections[0]['modules'] as $module) { |
487bc1b6 JM |
938 | if ($module['id'] == $forumcm->id and $module['modname'] == 'forum') { |
939 | $cm = $modinfo->cms[$forumcm->id]; | |
73ee2fda | 940 | $formattedtext = format_text($cm->content, FORMAT_HTML, |
487bc1b6 JM |
941 | array('noclean' => true, 'para' => false, 'filter' => false)); |
942 | $this->assertEquals($formattedtext, $module['description']); | |
ca4154ce | 943 | $this->assertEquals($forumcm->instance, $module['instance']); |
1206a487 | 944 | $this->assertContains('1 unread post', $module['afterlink']); |
62b40d27 JL |
945 | $this->assertFalse($module['noviewlink']); |
946 | $this->assertNotEmpty($module['description']); // Module showdescription is on. | |
1206a487 | 947 | $testexecuted = $testexecuted + 2; |
487bc1b6 JM |
948 | } else if ($module['id'] == $labelcm->id and $module['modname'] == 'label') { |
949 | $cm = $modinfo->cms[$labelcm->id]; | |
73ee2fda | 950 | $formattedtext = format_text($cm->content, FORMAT_HTML, |
487bc1b6 JM |
951 | array('noclean' => true, 'para' => false, 'filter' => false)); |
952 | $this->assertEquals($formattedtext, $module['description']); | |
ca4154ce | 953 | $this->assertEquals($labelcm->instance, $module['instance']); |
62b40d27 JL |
954 | $this->assertTrue($module['noviewlink']); |
955 | $this->assertNotEmpty($module['description']); // Label always prints the description. | |
487bc1b6 | 956 | $testexecuted = $testexecuted + 1; |
1206a487 JL |
957 | } else if ($module['id'] == $datacm->id and $module['modname'] == 'data') { |
958 | $this->assertContains('customcompletionrules', $module['customdata']); | |
62b40d27 JL |
959 | $this->assertFalse($module['noviewlink']); |
960 | $this->assertArrayNotHasKey('description', $module); | |
1206a487 | 961 | $testexecuted = $testexecuted + 1; |
487bc1b6 JM |
962 | } |
963 | } | |
1206a487 JL |
964 | foreach ($sections[2]['modules'] as $module) { |
965 | if ($module['id'] == $urlcm->id and $module['modname'] == 'url') { | |
966 | $this->assertContains('width=100,height=100', $module['onclick']); | |
967 | $testexecuted = $testexecuted + 1; | |
968 | } | |
969 | } | |
970 | ||
716c103d JL |
971 | $CFG->forum_allowforcedreadtracking = 0; // Recover original value. |
972 | forum_tp_count_forum_unread_posts($forumcm, $course, true); // Reset static cache for further tests. | |
973 | ||
1206a487 | 974 | $this->assertEquals(5, $testexecuted); |
935429e2 | 975 | $this->assertEquals(0, $sections[0]['section']); |
fb695f6e | 976 | |
10b88bf2 | 977 | $this->assertCount(5, $sections[0]['modules']); |
935429e2 JL |
978 | $this->assertCount(1, $sections[1]['modules']); |
979 | $this->assertCount(1, $sections[2]['modules']); | |
10b88bf2 JL |
980 | $this->assertCount(1, $sections[3]['modules']); // One module for the section with availability restrictions. |
981 | $this->assertCount(1, $sections[4]['modules']); // One module for the hidden section with a visible activity. | |
935429e2 JL |
982 | $this->assertNotEmpty($sections[3]['availabilityinfo']); |
983 | $this->assertEquals(1, $sections[1]['section']); | |
984 | $this->assertEquals(2, $sections[2]['section']); | |
985 | $this->assertEquals(3, $sections[3]['section']); | |
10b88bf2 | 986 | $this->assertEquals(4, $sections[4]['section']); |
935429e2 JL |
987 | $this->assertContains('<iframe', $sections[2]['summary']); |
988 | $this->assertContains('</iframe>', $sections[2]['summary']); | |
935429e2 | 989 | $this->assertNotEmpty($sections[2]['modules'][0]['availabilityinfo']); |
8a5346a7 JL |
990 | try { |
991 | $sections = core_course_external::get_course_contents($course->id, | |
992 | array(array("name" => "invalid", "value" => 1))); | |
993 | $this->fail('Exception expected due to invalid option.'); | |
994 | } catch (moodle_exception $e) { | |
995 | $this->assertEquals('errorinvalidparam', $e->errorcode); | |
996 | } | |
997 | } | |
998 | ||
999 | ||
10b88bf2 JL |
1000 | /** |
1001 | * Test get_course_contents as student | |
1002 | */ | |
1003 | public function test_get_course_contents_student() { | |
1004 | global $DB; | |
1005 | $this->resetAfterTest(true); | |
1006 | ||
1007 | list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test(); | |
1008 | ||
1009 | $studentroleid = $DB->get_field('role', 'id', array('shortname' => 'student')); | |
1010 | $user = self::getDataGenerator()->create_user(); | |
1011 | self::getDataGenerator()->enrol_user($user->id, $course->id, $studentroleid); | |
1012 | $this->setUser($user); | |
1013 | ||
1014 | $sections = core_course_external::get_course_contents($course->id, array()); | |
1015 | // We need to execute the return values cleaning process to simulate the web service server. | |
1016 | $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections); | |
1017 | ||
1018 | $this->assertCount(4, $sections); // Nothing for the not visible section. | |
1019 | $this->assertCount(5, $sections[0]['modules']); | |
1020 | $this->assertCount(1, $sections[1]['modules']); | |
1021 | $this->assertCount(1, $sections[2]['modules']); | |
1022 | $this->assertCount(0, $sections[3]['modules']); // No modules for the section with availability restrictions. | |
1023 | ||
1024 | $this->assertNotEmpty($sections[3]['availabilityinfo']); | |
1025 | $this->assertEquals(1, $sections[1]['section']); | |
1026 | $this->assertEquals(2, $sections[2]['section']); | |
1027 | $this->assertEquals(3, $sections[3]['section']); | |
1028 | // The module with the availability restriction met is returning contents. | |
1029 | $this->assertNotEmpty($sections[1]['modules'][0]['contents']); | |
1030 | // The module with the availability restriction not met is not returning contents. | |
1031 | $this->assertArrayNotHasKey('contents', $sections[2]['modules'][0]); | |
1032 | ||
1033 | // Now include flag for returning stealth information (fake section). | |
1034 | $sections = core_course_external::get_course_contents($course->id, | |
1035 | array(array("name" => "includestealthmodules", "value" => 1))); | |
1036 | // We need to execute the return values cleaning process to simulate the web service server. | |
1037 | $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections); | |
1038 | ||
1039 | $this->assertCount(5, $sections); // Include fake section with stealth activities. | |
1040 | $this->assertCount(5, $sections[0]['modules']); | |
1041 | $this->assertCount(1, $sections[1]['modules']); | |
1042 | $this->assertCount(1, $sections[2]['modules']); | |
1043 | $this->assertCount(0, $sections[3]['modules']); // No modules for the section with availability restrictions. | |
1044 | $this->assertCount(1, $sections[4]['modules']); // One stealh module. | |
1045 | $this->assertEquals(-1, $sections[4]['id']); | |
1046 | } | |
1047 | ||
8a5346a7 JL |
1048 | /** |
1049 | * Test get_course_contents excluding modules | |
1050 | */ | |
1051 | public function test_get_course_contents_excluding_modules() { | |
1052 | $this->resetAfterTest(true); | |
1053 | ||
1054 | list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test(); | |
1055 | ||
1056 | // Test exclude modules. | |
1057 | $sections = core_course_external::get_course_contents($course->id, array(array("name" => "excludemodules", "value" => 1))); | |
1058 | ||
1059 | // We need to execute the return values cleaning process to simulate the web service server. | |
1060 | $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections); | |
1061 | ||
935429e2 JL |
1062 | $this->assertEmpty($sections[0]['modules']); |
1063 | $this->assertEmpty($sections[1]['modules']); | |
8a5346a7 JL |
1064 | } |
1065 | ||
1066 | /** | |
1067 | * Test get_course_contents excluding contents | |
1068 | */ | |
1069 | public function test_get_course_contents_excluding_contents() { | |
1070 | $this->resetAfterTest(true); | |
1071 | ||
1072 | list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test(); | |
1073 | ||
1074 | // Test exclude modules. | |
1075 | $sections = core_course_external::get_course_contents($course->id, array(array("name" => "excludecontents", "value" => 1))); | |
1076 | ||
1077 | // We need to execute the return values cleaning process to simulate the web service server. | |
1078 | $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections); | |
1079 | ||
1080 | foreach ($sections as $section) { | |
1081 | foreach ($section['modules'] as $module) { | |
1082 | // Only resources return contents. | |
1083 | if (isset($module['contents'])) { | |
1084 | $this->assertEmpty($module['contents']); | |
1085 | } | |
1086 | } | |
1087 | } | |
1088 | } | |
1089 | ||
1090 | /** | |
1091 | * Test get_course_contents filtering by section number | |
1092 | */ | |
1093 | public function test_get_course_contents_section_number() { | |
1094 | $this->resetAfterTest(true); | |
1095 | ||
1096 | list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test(); | |
1097 | ||
1098 | // Test exclude modules. | |
1099 | $sections = core_course_external::get_course_contents($course->id, array(array("name" => "sectionnumber", "value" => 0))); | |
1100 | ||
1101 | // We need to execute the return values cleaning process to simulate the web service server. | |
1102 | $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections); | |
1103 | ||
1104 | $this->assertCount(1, $sections); | |
10b88bf2 | 1105 | $this->assertCount(5, $sections[0]['modules']); |
8a5346a7 JL |
1106 | } |
1107 | ||
1108 | /** | |
1109 | * Test get_course_contents filtering by cmid | |
1110 | */ | |
1111 | public function test_get_course_contents_cmid() { | |
1112 | $this->resetAfterTest(true); | |
1113 | ||
1114 | list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test(); | |
1115 | ||
1116 | // Test exclude modules. | |
1117 | $sections = core_course_external::get_course_contents($course->id, array(array("name" => "cmid", "value" => $forumcm->id))); | |
1118 | ||
1119 | // We need to execute the return values cleaning process to simulate the web service server. | |
1120 | $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections); | |
1121 | ||
935429e2 | 1122 | $this->assertCount(4, $sections); |
8a5346a7 JL |
1123 | $this->assertCount(1, $sections[0]['modules']); |
1124 | $this->assertEquals($forumcm->id, $sections[0]['modules'][0]["id"]); | |
1125 | } | |
1126 | ||
1127 | ||
1128 | /** | |
1129 | * Test get_course_contents filtering by cmid and section | |
1130 | */ | |
1131 | public function test_get_course_contents_section_cmid() { | |
1132 | $this->resetAfterTest(true); | |
1133 | ||
1134 | list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test(); | |
1135 | ||
1136 | // Test exclude modules. | |
1137 | $sections = core_course_external::get_course_contents($course->id, array( | |
1138 | array("name" => "cmid", "value" => $forumcm->id), | |
1139 | array("name" => "sectionnumber", "value" => 0) | |
1140 | )); | |
1141 | ||
1142 | // We need to execute the return values cleaning process to simulate the web service server. | |
1143 | $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections); | |
1144 | ||
1145 | $this->assertCount(1, $sections); | |
1146 | $this->assertCount(1, $sections[0]['modules']); | |
1147 | $this->assertEquals($forumcm->id, $sections[0]['modules'][0]["id"]); | |
1148 | } | |
1149 | ||
1150 | /** | |
1151 | * Test get_course_contents filtering by modname | |
1152 | */ | |
1153 | public function test_get_course_contents_modname() { | |
1154 | $this->resetAfterTest(true); | |
1155 | ||
1156 | list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test(); | |
1157 | ||
1158 | // Test exclude modules. | |
1159 | $sections = core_course_external::get_course_contents($course->id, array(array("name" => "modname", "value" => "forum"))); | |
1160 | ||
1161 | // We need to execute the return values cleaning process to simulate the web service server. | |
1162 | $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections); | |
1163 | ||
935429e2 | 1164 | $this->assertCount(4, $sections); |
8a5346a7 JL |
1165 | $this->assertCount(1, $sections[0]['modules']); |
1166 | $this->assertEquals($forumcm->id, $sections[0]['modules'][0]["id"]); | |
1167 | } | |
1168 | ||
1169 | /** | |
1170 | * Test get_course_contents filtering by modname | |
1171 | */ | |
1172 | public function test_get_course_contents_modid() { | |
1173 | $this->resetAfterTest(true); | |
1174 | ||
1175 | list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test(); | |
1176 | ||
1177 | // Test exclude modules. | |
1178 | $sections = core_course_external::get_course_contents($course->id, array( | |
1179 | array("name" => "modname", "value" => "page"), | |
1180 | array("name" => "modid", "value" => $pagecm->instance), | |
1181 | )); | |
1182 | ||
1183 | // We need to execute the return values cleaning process to simulate the web service server. | |
1184 | $sections = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $sections); | |
1185 | ||
935429e2 | 1186 | $this->assertCount(4, $sections); |
8a5346a7 JL |
1187 | $this->assertCount(1, $sections[0]['modules']); |
1188 | $this->assertEquals("page", $sections[0]['modules'][0]["modname"]); | |
1189 | $this->assertEquals($pagecm->instance, $sections[0]['modules'][0]["instance"]); | |
2a7a0216 JM |
1190 | } |
1191 | ||
1de51367 JL |
1192 | /** |
1193 | * Test get course contents completion | |
1194 | */ | |
1195 | public function test_get_course_contents_completion() { | |
1196 | global $CFG; | |
1197 | $this->resetAfterTest(true); | |
1198 | ||
1199 | list($course, $forumcm, $datacm, $pagecm, $labelcm, $urlcm) = $this->prepare_get_course_contents_test(); | |
76724712 | 1200 | availability_completion\condition::wipe_static_cache(); |
1de51367 JL |
1201 | |
1202 | // Test activity not completed yet. | |
1203 | $result = core_course_external::get_course_contents($course->id, array( | |
1204 | array("name" => "modname", "value" => "forum"), array("name" => "modid", "value" => $forumcm->instance))); | |
1205 | // We need to execute the return values cleaning process to simulate the web service server. | |
1206 | $result = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $result); | |
1207 | ||
1208 | $this->assertCount(1, $result[0]['modules']); | |
1209 | $this->assertEquals("forum", $result[0]['modules'][0]["modname"]); | |
1210 | $this->assertEquals(COMPLETION_TRACKING_MANUAL, $result[0]['modules'][0]["completion"]); | |
1211 | $this->assertEquals(0, $result[0]['modules'][0]["completiondata"]['state']); | |
1212 | $this->assertEquals(0, $result[0]['modules'][0]["completiondata"]['timecompleted']); | |
1213 | $this->assertEmpty($result[0]['modules'][0]["completiondata"]['overrideby']); | |
76724712 | 1214 | $this->assertFalse($result[0]['modules'][0]["completiondata"]['valueused']); |
1de51367 JL |
1215 | |
1216 | // Set activity completed. | |
1217 | core_completion_external::update_activity_completion_status_manually($forumcm->id, true); | |
1218 | ||
1219 | $result = core_course_external::get_course_contents($course->id, array( | |
1220 | array("name" => "modname", "value" => "forum"), array("name" => "modid", "value" => $forumcm->instance))); | |
1221 | // We need to execute the return values cleaning process to simulate the web service server. | |
1222 | $result = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $result); | |
1223 | ||
1224 | $this->assertEquals(COMPLETION_COMPLETE, $result[0]['modules'][0]["completiondata"]['state']); | |
1225 | $this->assertNotEmpty($result[0]['modules'][0]["completiondata"]['timecompleted']); | |
1226 | $this->assertEmpty($result[0]['modules'][0]["completiondata"]['overrideby']); | |
1227 | ||
76724712 MJ |
1228 | // Test activity with completion value that is used in an availability condition. |
1229 | $result = core_course_external::get_course_contents($course->id, array( | |
1230 | array("name" => "modname", "value" => "label"), array("name" => "modid", "value" => $labelcm->instance))); | |
1231 | // We need to execute the return values cleaning process to simulate the web service server. | |
1232 | $result = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $result); | |
1233 | ||
1234 | $this->assertCount(1, $result[0]['modules']); | |
1235 | $this->assertEquals("label", $result[0]['modules'][0]["modname"]); | |
1236 | $this->assertEquals(COMPLETION_TRACKING_MANUAL, $result[0]['modules'][0]["completion"]); | |
1237 | $this->assertEquals(0, $result[0]['modules'][0]["completiondata"]['state']); | |
1238 | $this->assertEquals(0, $result[0]['modules'][0]["completiondata"]['timecompleted']); | |
1239 | $this->assertEmpty($result[0]['modules'][0]["completiondata"]['overrideby']); | |
1240 | $this->assertTrue($result[0]['modules'][0]["completiondata"]['valueused']); | |
1241 | ||
1de51367 JL |
1242 | // Disable completion. |
1243 | $CFG->enablecompletion = 0; | |
1244 | $result = core_course_external::get_course_contents($course->id, array( | |
1245 | array("name" => "modname", "value" => "forum"), array("name" => "modid", "value" => $forumcm->instance))); | |
1246 | // We need to execute the return values cleaning process to simulate the web service server. | |
1247 | $result = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $result); | |
1248 | ||
1249 | $this->assertArrayNotHasKey('completiondata', $result[0]['modules'][0]); | |
1250 | } | |
1251 | ||
34ad1a01 JL |
1252 | /** |
1253 | * Test mimetype is returned for resources with showtype set. | |
1254 | */ | |
1255 | public function test_get_course_contents_including_mimetype() { | |
1256 | $this->resetAfterTest(true); | |
1257 | ||
1258 | $this->setAdminUser(); | |
1259 | $course = self::getDataGenerator()->create_course(); | |
1260 | ||
1261 | $record = new stdClass(); | |
1262 | $record->course = $course->id; | |
1263 | $record->showtype = 1; | |
1264 | $resource = self::getDataGenerator()->create_module('resource', $record); | |
1265 | ||
1266 | $result = core_course_external::get_course_contents($course->id); | |
1267 | $result = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $result); | |
1268 | $this->assertCount(1, $result[0]['modules']); // One module, first section. | |
1269 | $customdata = unserialize(json_decode($result[0]['modules'][0]['customdata'])); | |
1270 | $this->assertEquals('text/plain', $customdata['filedetails']['mimetype']); | |
1271 | } | |
1272 | ||
9b8aed89 JL |
1273 | /** |
1274 | * Test contents info is returned. | |
1275 | */ | |
1276 | public function test_get_course_contents_contentsinfo() { | |
1277 | global $USER; | |
1278 | ||
1279 | $this->resetAfterTest(true); | |
9b8aed89 | 1280 | $this->setAdminUser(); |
0ba8114b AN |
1281 | $timenow = time(); |
1282 | ||
9b8aed89 JL |
1283 | $course = self::getDataGenerator()->create_course(); |
1284 | ||
1285 | $record = new stdClass(); | |
1286 | $record->course = $course->id; | |
1287 | // One resource with one file. | |
1288 | $resource1 = self::getDataGenerator()->create_module('resource', $record); | |
1289 | ||
9b8aed89 JL |
1290 | // More type of files. |
1291 | $record->files = file_get_unused_draft_itemid(); | |
1292 | $usercontext = context_user::instance($USER->id); | |
1293 | $extensions = array('txt', 'png', 'pdf'); | |
acfd5e83 | 1294 | $fs = get_file_storage(); |
9b8aed89 JL |
1295 | foreach ($extensions as $key => $extension) { |
1296 | // Add actual file there. | |
1297 | $filerecord = array('component' => 'user', 'filearea' => 'draft', | |
1298 | 'contextid' => $usercontext->id, 'itemid' => $record->files, | |
1299 | 'filename' => 'resource' . $key . '.' . $extension, 'filepath' => '/'); | |
9b8aed89 JL |
1300 | $fs->create_file_from_string($filerecord, 'Test resource ' . $key . ' file'); |
1301 | } | |
1302 | ||
acfd5e83 JL |
1303 | // Create file reference. |
1304 | $repos = repository::get_instances(array('type' => 'user')); | |
1305 | $userrepository = reset($repos); | |
1306 | ||
1307 | // Create a user private file. | |
1308 | $userfilerecord = new stdClass; | |
1309 | $userfilerecord->contextid = $usercontext->id; | |
1310 | $userfilerecord->component = 'user'; | |
1311 | $userfilerecord->filearea = 'private'; | |
1312 | $userfilerecord->itemid = 0; | |
1313 | $userfilerecord->filepath = '/'; | |
1314 | $userfilerecord->filename = 'userfile.txt'; | |
1315 | $userfilerecord->source = 'test'; | |
1316 | $userfile = $fs->create_file_from_string($userfilerecord, 'User file content'); | |
1317 | $userfileref = $fs->pack_reference($userfilerecord); | |
1318 | ||
1319 | // Clone latest "normal" file. | |
1320 | $filerefrecord = clone (object) $filerecord; | |
1321 | $filerefrecord->filename = 'testref.txt'; | |
1322 | $fileref = $fs->create_file_from_reference($filerefrecord, $userrepository->id, $userfileref); | |
1323 | // Set main file pointing to the file reference. | |
1324 | file_set_sortorder($usercontext->id, 'user', 'draft', $record->files, $filerefrecord->filepath, | |
1325 | $filerefrecord->filename, 1); | |
1326 | ||
1327 | // Once the reference has been created, create the file resource. | |
9b8aed89 JL |
1328 | $resource2 = self::getDataGenerator()->create_module('resource', $record); |
1329 | ||
1330 | $result = core_course_external::get_course_contents($course->id); | |
1331 | $result = external_api::clean_returnvalue(core_course_external::get_course_contents_returns(), $result); | |
1332 | $this->assertCount(2, $result[0]['modules']); | |
1333 | foreach ($result[0]['modules'] as $module) { | |
1334 | if ($module['instance'] == $resource1->id) { | |
1335 | $this->assertEquals(1, $module['contentsinfo']['filescount']); | |
1336 | $this->assertGreaterThanOrEqual($timenow, $module['contentsinfo']['lastmodified']); | |
1337 | $this->assertEquals($module['contents'][0]['filesize'], $module['contentsinfo']['filessize']); | |
1338 | $this->assertEquals(array('text/plain'), $module['contentsinfo']['mimetypes']); | |
1339 | } else { | |
acfd5e83 | 1340 | $this->assertEquals(count($extensions) + 1, $module['contentsinfo']['filescount']); |
9b8aed89 | 1341 | $filessize = $module['contents'][0]['filesize'] + $module['contents'][1]['filesize'] + |
acfd5e83 | 1342 | $module['contents'][2]['filesize'] + $module['contents'][3]['filesize']; |
9b8aed89 | 1343 | $this->assertEquals($filessize, $module['contentsinfo']['filessize']); |
acfd5e83 | 1344 | $this->assertEquals('user', $module['contentsinfo']['repositorytype']); |
9b8aed89 JL |
1345 | $this->assertGreaterThanOrEqual($timenow, $module['contentsinfo']['lastmodified']); |
1346 | $this->assertEquals(array('text/plain', 'image/png', 'application/pdf'), $module['contentsinfo']['mimetypes']); | |
1347 | } | |
1348 | } | |
1349 | } | |
1350 | ||
2a7a0216 JM |
1351 | /** |
1352 | * Test duplicate_course | |
1353 | */ | |
1354 | public function test_duplicate_course() { | |
1355 | $this->resetAfterTest(true); | |
1356 | ||
1357 | // Create one course with three modules. | |
1358 | $course = self::getDataGenerator()->create_course(); | |
1359 | $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course->id)); | |
1360 | $forumcm = get_coursemodule_from_id('forum', $forum->cmid); | |
1361 | $forumcontext = context_module::instance($forum->cmid); | |
1362 | $data = $this->getDataGenerator()->create_module('data', array('assessed'=>1, 'scale'=>100, 'course'=>$course->id)); | |
1363 | $datacontext = context_module::instance($data->cmid); | |
1364 | $datacm = get_coursemodule_from_instance('page', $data->id); | |
1365 | $page = $this->getDataGenerator()->create_module('page', array('course'=>$course->id)); | |
1366 | $pagecontext = context_module::instance($page->cmid); | |
1367 | $pagecm = get_coursemodule_from_instance('page', $page->id); | |
1368 | ||
1369 | // Set the required capabilities by the external function. | |
1370 | $coursecontext = context_course::instance($course->id); | |
1371 | $categorycontext = context_coursecat::instance($course->category); | |
1372 | $roleid = $this->assignUserCapability('moodle/course:create', $categorycontext->id); | |
1373 | $this->assignUserCapability('moodle/course:view', $categorycontext->id, $roleid); | |
1374 | $this->assignUserCapability('moodle/restore:restorecourse', $categorycontext->id, $roleid); | |
1375 | $this->assignUserCapability('moodle/backup:backupcourse', $coursecontext->id, $roleid); | |
1376 | $this->assignUserCapability('moodle/backup:configure', $coursecontext->id, $roleid); | |
1377 | // Optional capabilities to copy user data. | |
1378 | $this->assignUserCapability('moodle/backup:userinfo', $coursecontext->id, $roleid); | |
1379 | $this->assignUserCapability('moodle/restore:userinfo', $categorycontext->id, $roleid); | |
1380 | ||
1381 | $newcourse['fullname'] = 'Course duplicate'; | |
1382 | $newcourse['shortname'] = 'courseduplicate'; | |
1383 | $newcourse['categoryid'] = $course->category; | |
1384 | $newcourse['visible'] = true; | |
1385 | $newcourse['options'][] = array('name' => 'users', 'value' => true); | |
1386 | ||
1387 | $duplicate = core_course_external::duplicate_course($course->id, $newcourse['fullname'], | |
1388 | $newcourse['shortname'], $newcourse['categoryid'], $newcourse['visible'], $newcourse['options']); | |
1389 | ||
fb695f6e JM |
1390 | // We need to execute the return values cleaning process to simulate the web service server. |
1391 | $duplicate = external_api::clean_returnvalue(core_course_external::duplicate_course_returns(), $duplicate); | |
1392 | ||
2a7a0216 JM |
1393 | // Check that the course has been duplicated. |
1394 | $this->assertEquals($newcourse['shortname'], $duplicate['shortname']); | |
1395 | } | |
791723c3 RT |
1396 | |
1397 | /** | |
1398 | * Test update_courses | |
1399 | */ | |
1400 | public function test_update_courses() { | |
a182f88f EL |
1401 | global $DB, $CFG, $USER, $COURSE; |
1402 | ||
1403 | // Get current $COURSE to be able to restore it later (defaults to $SITE). We need this | |
1404 | // trick because we are both updating and getting (for testing) course information | |
1405 | // in the same request and core_course_external::update_courses() | |
1406 | // is overwriting $COURSE all over the time with OLD values, so later | |
1407 | // use of get_course() fetches those OLD values instead of the updated ones. | |
1408 | // See MDL-39723 for more info. | |
1409 | $origcourse = clone($COURSE); | |
791723c3 RT |
1410 | |
1411 | $this->resetAfterTest(true); | |
1412 | ||
1413 | // Set the required capabilities by the external function. | |
1414 | $contextid = context_system::instance()->id; | |
1415 | $roleid = $this->assignUserCapability('moodle/course:update', $contextid); | |
1416 | $this->assignUserCapability('moodle/course:changecategory', $contextid, $roleid); | |
7a0162f1 | 1417 | $this->assignUserCapability('moodle/course:changelockedcustomfields', $contextid, $roleid); |
791723c3 RT |
1418 | $this->assignUserCapability('moodle/course:changefullname', $contextid, $roleid); |
1419 | $this->assignUserCapability('moodle/course:changeshortname', $contextid, $roleid); | |
1420 | $this->assignUserCapability('moodle/course:changeidnumber', $contextid, $roleid); | |
1421 | $this->assignUserCapability('moodle/course:changesummary', $contextid, $roleid); | |
1422 | $this->assignUserCapability('moodle/course:visibility', $contextid, $roleid); | |
1423 | $this->assignUserCapability('moodle/course:viewhiddencourses', $contextid, $roleid); | |
4a9624af | 1424 | $this->assignUserCapability('moodle/course:setforcedlanguage', $contextid, $roleid); |
791723c3 | 1425 | |
7a0162f1 | 1426 | // Create category and courses. |
791723c3 RT |
1427 | $category1 = self::getDataGenerator()->create_category(); |
1428 | $category2 = self::getDataGenerator()->create_category(); | |
7a0162f1 | 1429 | |
791723c3 RT |
1430 | $originalcourse1 = self::getDataGenerator()->create_course(); |
1431 | self::getDataGenerator()->enrol_user($USER->id, $originalcourse1->id, $roleid); | |
7a0162f1 | 1432 | |
791723c3 RT |
1433 | $originalcourse2 = self::getDataGenerator()->create_course(); |
1434 | self::getDataGenerator()->enrol_user($USER->id, $originalcourse2->id, $roleid); | |
1435 | ||
7a0162f1 DM |
1436 | // Course with custom fields. |
1437 | $fieldcategory = self::getDataGenerator()->create_custom_field_category(['name' => 'Other fields']); | |
1438 | $customfield = ['shortname' => 'test', 'name' => 'Custom field', 'type' => 'text', | |
1439 | 'categoryid' => $fieldcategory->get('id'), | |
1440 | 'configdata' => ['visibility' => \core_course\customfield\course_handler::VISIBLETOALL, 'locked' => 1]]; | |
1441 | $field = self::getDataGenerator()->create_custom_field($customfield); | |
1442 | ||
1443 | $originalcourse3 = self::getDataGenerator()->create_course(['customfield_test' => 'Test value']); | |
1444 | self::getDataGenerator()->enrol_user($USER->id, $originalcourse3->id, $roleid); | |
1445 | ||
791723c3 RT |
1446 | // Course values to be updated. |
1447 | $course1['id'] = $originalcourse1->id; | |
1448 | $course1['fullname'] = 'Updated test course 1'; | |
1449 | $course1['shortname'] = 'Udestedtestcourse1'; | |
1450 | $course1['categoryid'] = $category1->id; | |
7a0162f1 | 1451 | |
791723c3 RT |
1452 | $course2['id'] = $originalcourse2->id; |
1453 | $course2['fullname'] = 'Updated test course 2'; | |
1454 | $course2['shortname'] = 'Updestedtestcourse2'; | |
1455 | $course2['categoryid'] = $category2->id; | |
1456 | $course2['idnumber'] = 'Updatedidnumber2'; | |
1457 | $course2['summary'] = 'Updaated description for course 2'; | |
1458 | $course2['summaryformat'] = FORMAT_HTML; | |
1459 | $course2['format'] = 'topics'; | |
1460 | $course2['showgrades'] = 1; | |
1461 | $course2['newsitems'] = 3; | |
1462 | $course2['startdate'] = 1420092000; // 01/01/2015. | |
fbcdb0d7 | 1463 | $course2['enddate'] = 1422669600; // 01/31/2015. |
791723c3 RT |
1464 | $course2['maxbytes'] = 100000; |
1465 | $course2['showreports'] = 1; | |
1466 | $course2['visible'] = 0; | |
1467 | $course2['hiddensections'] = 0; | |
1468 | $course2['groupmode'] = 0; | |
1469 | $course2['groupmodeforce'] = 0; | |
1470 | $course2['defaultgroupingid'] = 0; | |
1471 | $course2['enablecompletion'] = 1; | |
1472 | $course2['lang'] = 'en'; | |
e00f1c66 | 1473 | $course2['forcetheme'] = 'classic'; |
7a0162f1 DM |
1474 | |
1475 | $course3['id'] = $originalcourse3->id; | |
1476 | $updatedcustomfieldvalue = ['shortname' => 'test', 'value' => 'Updated test value']; | |
1477 | $course3['customfields'] = [$updatedcustomfieldvalue]; | |
1478 | $courses = array($course1, $course2, $course3); | |
791723c3 RT |
1479 | |
1480 | $updatedcoursewarnings = core_course_external::update_courses($courses); | |
bdf9f4d4 | 1481 | $updatedcoursewarnings = external_api::clean_returnvalue(core_course_external::update_courses_returns(), |
7a0162f1 | 1482 | $updatedcoursewarnings); |
a182f88f | 1483 | $COURSE = $origcourse; // Restore $COURSE. Instead of using the OLD one set by the previous line. |
791723c3 RT |
1484 | |
1485 | // Check that right number of courses were created. | |
1486 | $this->assertEquals(0, count($updatedcoursewarnings['warnings'])); | |
1487 | ||
1488 | // Check that the courses were correctly created. | |
1489 | foreach ($courses as $course) { | |
1490 | $courseinfo = course_get_format($course['id'])->get_course(); | |
7a0162f1 | 1491 | $customfields = \core_course\customfield\course_handler::create()->export_instance_data_object($course['id']); |
791723c3 RT |
1492 | if ($course['id'] == $course2['id']) { |
1493 | $this->assertEquals($course2['fullname'], $courseinfo->fullname); | |
1494 | $this->assertEquals($course2['shortname'], $courseinfo->shortname); | |
1495 | $this->assertEquals($course2['categoryid'], $courseinfo->category); | |
1496 | $this->assertEquals($course2['idnumber'], $courseinfo->idnumber); | |
1497 | $this->assertEquals($course2['summary'], $courseinfo->summary); | |
1498 | $this->assertEquals($course2['summaryformat'], $courseinfo->summaryformat); | |
1499 | $this->assertEquals($course2['format'], $courseinfo->format); | |
1500 | $this->assertEquals($course2['showgrades'], $courseinfo->showgrades); | |
1501 | $this->assertEquals($course2['newsitems'], $courseinfo->newsitems); | |
1502 | $this->assertEquals($course2['startdate'], $courseinfo->startdate); | |
fbcdb0d7 | 1503 | $this->assertEquals($course2['enddate'], $courseinfo->enddate); |
791723c3 RT |
1504 | $this->assertEquals($course2['maxbytes'], $courseinfo->maxbytes); |
1505 | $this->assertEquals($course2['showreports'], $courseinfo->showreports); | |
1506 | $this->assertEquals($course2['visible'], $courseinfo->visible); | |
1507 | $this->assertEquals($course2['hiddensections'], $courseinfo->hiddensections); | |
1508 | $this->assertEquals($course2['groupmode'], $courseinfo->groupmode); | |
1509 | $this->assertEquals($course2['groupmodeforce'], $courseinfo->groupmodeforce); | |
1510 | $this->assertEquals($course2['defaultgroupingid'], $courseinfo->defaultgroupingid); | |
1511 | $this->assertEquals($course2['lang'], $courseinfo->lang); | |
1512 | ||
1513 | if (!empty($CFG->allowcoursethemes)) { | |
1514 | $this->assertEquals($course2['forcetheme'], $courseinfo->theme); | |
1515 | } | |
1516 | ||
8be9cffb | 1517 | $this->assertEquals($course2['enablecompletion'], $courseinfo->enablecompletion); |
7a0162f1 | 1518 | $this->assertEquals(['test' => null], (array)$customfields); |
791723c3 RT |
1519 | } else if ($course['id'] == $course1['id']) { |
1520 | $this->assertEquals($course1['fullname'], $courseinfo->fullname); | |
1521 | $this->assertEquals($course1['shortname'], $courseinfo->shortname); | |
1522 | $this->assertEquals($course1['categoryid'], $courseinfo->category); | |
1523 | $this->assertEquals(FORMAT_MOODLE, $courseinfo->summaryformat); | |
1524 | $this->assertEquals('topics', $courseinfo->format); | |
89b909f6 | 1525 | $this->assertEquals(5, course_get_format($course['id'])->get_last_section_number()); |
791723c3 RT |
1526 | $this->assertEquals(0, $courseinfo->newsitems); |
1527 | $this->assertEquals(FORMAT_MOODLE, $courseinfo->summaryformat); | |
7a0162f1 DM |
1528 | $this->assertEquals(['test' => null], (array)$customfields); |
1529 | } else if ($course['id'] == $course3['id']) { | |
1530 | $this->assertEquals(['test' => $updatedcustomfieldvalue['value']], (array)$customfields); | |
791723c3 | 1531 | } else { |
75c597da | 1532 | throw new moodle_exception('Unexpected shortname'); |
791723c3 RT |
1533 | } |
1534 | } | |
1535 | ||
1536 | $courses = array($course1); | |
1537 | // Try update course without update capability. | |
1538 | $user = self::getDataGenerator()->create_user(); | |
1539 | $this->setUser($user); | |
1540 | $this->unassignUserCapability('moodle/course:update', $contextid, $roleid); | |
1541 | self::getDataGenerator()->enrol_user($user->id, $course1['id'], $roleid); | |
1542 | $updatedcoursewarnings = core_course_external::update_courses($courses); | |
bdf9f4d4 JL |
1543 | $updatedcoursewarnings = external_api::clean_returnvalue(core_course_external::update_courses_returns(), |
1544 | $updatedcoursewarnings); | |
791723c3 RT |
1545 | $this->assertEquals(1, count($updatedcoursewarnings['warnings'])); |
1546 | ||
1547 | // Try update course category without capability. | |
1548 | $this->assignUserCapability('moodle/course:update', $contextid, $roleid); | |
1549 | $this->unassignUserCapability('moodle/course:changecategory', $contextid, $roleid); | |
1550 | $user = self::getDataGenerator()->create_user(); | |
1551 | $this->setUser($user); | |
1552 | self::getDataGenerator()->enrol_user($user->id, $course1['id'], $roleid); | |
1553 | $course1['categoryid'] = $category2->id; | |
1554 | $courses = array($course1); | |
1555 | $updatedcoursewarnings = core_course_external::update_courses($courses); | |
bdf9f4d4 JL |
1556 | $updatedcoursewarnings = external_api::clean_returnvalue(core_course_external::update_courses_returns(), |
1557 | $updatedcoursewarnings); | |
791723c3 RT |
1558 | $this->assertEquals(1, count($updatedcoursewarnings['warnings'])); |
1559 | ||
1560 | // Try update course fullname without capability. | |
1561 | $this->assignUserCapability('moodle/course:changecategory', $contextid, $roleid); | |
1562 | $this->unassignUserCapability('moodle/course:changefullname', $contextid, $roleid); | |
1563 | $user = self::getDataGenerator()->create_user(); | |
1564 | $this->setUser($user); | |
1565 | self::getDataGenerator()->enrol_user($user->id, $course1['id'], $roleid); | |
1566 | $updatedcoursewarnings = core_course_external::update_courses($courses); | |
bdf9f4d4 JL |
1567 | $updatedcoursewarnings = external_api::clean_returnvalue(core_course_external::update_courses_returns(), |
1568 | $updatedcoursewarnings); | |
791723c3 RT |
1569 | $this->assertEquals(0, count($updatedcoursewarnings['warnings'])); |
1570 | $course1['fullname'] = 'Testing fullname without permission'; | |
1571 | $courses = array($course1); | |
1572 | $updatedcoursewarnings = core_course_external::update_courses($courses); | |
bdf9f4d4 JL |
1573 | $updatedcoursewarnings = external_api::clean_returnvalue(core_course_external::update_courses_returns(), |
1574 | $updatedcoursewarnings); | |
791723c3 RT |
1575 | $this->assertEquals(1, count($updatedcoursewarnings['warnings'])); |
1576 | ||
1577 | // Try update course shortname without capability. | |
1578 | $this->assignUserCapability('moodle/course:changefullname', $contextid, $roleid); | |
1579 | $this->unassignUserCapability('moodle/course:changeshortname', $contextid, $roleid); | |
1580 | $user = self::getDataGenerator()->create_user(); | |
1581 | $this->setUser($user); | |
1582 | self::getDataGenerator()->enrol_user($user->id, $course1['id'], $roleid); | |
1583 | $updatedcoursewarnings = core_course_external::update_courses($courses); | |
bdf9f4d4 JL |
1584 | $updatedcoursewarnings = external_api::clean_returnvalue(core_course_external::update_courses_returns(), |
1585 | $updatedcoursewarnings); | |
791723c3 RT |
1586 | $this->assertEquals(0, count($updatedcoursewarnings['warnings'])); |
1587 | $course1['shortname'] = 'Testing shortname without permission'; | |
1588 | $courses = array($course1); | |
1589 | $updatedcoursewarnings = core_course_external::update_courses($courses); | |
bdf9f4d4 JL |
1590 | $updatedcoursewarnings = external_api::clean_returnvalue(core_course_external::update_courses_returns(), |
1591 | $updatedcoursewarnings); | |
791723c3 RT |
1592 | $this->assertEquals(1, count($updatedcoursewarnings['warnings'])); |
1593 | ||
1594 | // Try update course idnumber without capability. | |
1595 | $this->assignUserCapability('moodle/course:changeshortname', $contextid, $roleid); | |
1596 | $this->unassignUserCapability('moodle/course:changeidnumber', $contextid, $roleid); | |
1597 | $user = self::getDataGenerator()->create_user(); | |
1598 | $this->setUser($user); | |
1599 | self::getDataGenerator()->enrol_user($user->id, $course1['id'], $roleid); | |
1600 | $updatedcoursewarnings = core_course_external::update_courses($courses); | |
bdf9f4d4 JL |
1601 | $updatedcoursewarnings = external_api::clean_returnvalue(core_course_external::update_courses_returns(), |
1602 | $updatedcoursewarnings); | |
791723c3 RT |
1603 | $this->assertEquals(0, count($updatedcoursewarnings['warnings'])); |
1604 | $course1['idnumber'] = 'NEWIDNUMBER'; | |
1605 | $courses = array($course1); | |
1606 | $updatedcoursewarnings = core_course_external::update_courses($courses); | |
bdf9f4d4 JL |
1607 | $updatedcoursewarnings = external_api::clean_returnvalue(core_course_external::update_courses_returns(), |
1608 | $updatedcoursewarnings); | |
791723c3 RT |
1609 | $this->assertEquals(1, count($updatedcoursewarnings['warnings'])); |
1610 | ||
1611 | // Try update course summary without capability. | |
1612 | $this->assignUserCapability('moodle/course:changeidnumber', $contextid, $roleid); | |
1613 | $this->unassignUserCapability('moodle/course:changesummary', $contextid, $roleid); | |
1614 | $user = self::getDataGenerator()->create_user(); | |
1615 | $this->setUser($user); | |
1616 | self::getDataGenerator()->enrol_user($user->id, $course1['id'], $roleid); | |
1617 | $updatedcoursewarnings = core_course_external::update_courses($courses); | |
bdf9f4d4 JL |
1618 | $updatedcoursewarnings = external_api::clean_returnvalue(core_course_external::update_courses_returns(), |
1619 | $updatedcoursewarnings); | |
791723c3 RT |
1620 | $this->assertEquals(0, count($updatedcoursewarnings['warnings'])); |
1621 | $course1['summary'] = 'New summary'; | |
1622 | $courses = array($course1); | |
1623 | $updatedcoursewarnings = core_course_external::update_courses($courses); | |
bdf9f4d4 JL |
1624 | $updatedcoursewarnings = external_api::clean_returnvalue(core_course_external::update_courses_returns(), |
1625 | $updatedcoursewarnings); | |
791723c3 RT |
1626 | $this->assertEquals(1, count($updatedcoursewarnings['warnings'])); |
1627 | ||
1628 | // Try update course with invalid summary format. | |
1629 | $this->assignUserCapability('moodle/course:changesummary', $contextid, $roleid); | |
1630 | $user = self::getDataGenerator()->create_user(); | |
1631 | $this->setUser($user); | |
1632 | self::getDataGenerator()->enrol_user($user->id, $course1['id'], $roleid); | |
1633 | $updatedcoursewarnings = core_course_external::update_courses($courses); | |
bdf9f4d4 JL |
1634 | $updatedcoursewarnings = external_api::clean_returnvalue(core_course_external::update_courses_returns(), |
1635 | $updatedcoursewarnings); | |
791723c3 RT |
1636 | $this->assertEquals(0, count($updatedcoursewarnings['warnings'])); |
1637 | $course1['summaryformat'] = 10; | |
1638 | $courses = array($course1); | |
1639 | $updatedcoursewarnings = core_course_external::update_courses($courses); | |
bdf9f4d4 JL |
1640 | $updatedcoursewarnings = external_api::clean_returnvalue(core_course_external::update_courses_returns(), |
1641 | $updatedcoursewarnings); | |
791723c3 RT |
1642 | $this->assertEquals(1, count($updatedcoursewarnings['warnings'])); |
1643 | ||
1644 | // Try update course visibility without capability. | |
1645 | $this->unassignUserCapability('moodle/course:visibility', $contextid, $roleid); | |
1646 | $user = self::getDataGenerator()->create_user(); | |
1647 | $this->setUser($user); | |
1648 | self::getDataGenerator()->enrol_user($user->id, $course1['id'], $roleid); | |
1649 | $course1['summaryformat'] = FORMAT_MOODLE; | |
1650 | $courses = array($course1); | |
1651 | $updatedcoursewarnings = core_course_external::update_courses($courses); | |
bdf9f4d4 JL |
1652 | $updatedcoursewarnings = external_api::clean_returnvalue(core_course_external::update_courses_returns(), |
1653 | $updatedcoursewarnings); | |
791723c3 RT |
1654 | $this->assertEquals(0, count($updatedcoursewarnings['warnings'])); |
1655 | $course1['visible'] = 0; | |
1656 | $courses = array($course1); | |
1657 | $updatedcoursewarnings = core_course_external::update_courses($courses); | |
bdf9f4d4 JL |
1658 | $updatedcoursewarnings = external_api::clean_returnvalue(core_course_external::update_courses_returns(), |
1659 | $updatedcoursewarnings); | |
791723c3 | 1660 | $this->assertEquals(1, count($updatedcoursewarnings['warnings'])); |
7a0162f1 DM |
1661 | |
1662 | // Try update course custom fields without capability. | |
1663 | $this->unassignUserCapability('moodle/course:changelockedcustomfields', $contextid, $roleid); | |
1664 | $user = self::getDataGenerator()->create_user(); | |
1665 | $this->setUser($user); | |
1666 | self::getDataGenerator()->enrol_user($user->id, $course3['id'], $roleid); | |
1667 | ||
1668 | $newupdatedcustomfieldvalue = ['shortname' => 'test', 'value' => 'New updated value']; | |
1669 | $course3['customfields'] = [$newupdatedcustomfieldvalue]; | |
1670 | ||
1671 | core_course_external::update_courses([$course3]); | |
1672 | ||
1673 | // Custom field was not updated. | |
1674 | $customfields = \core_course\customfield\course_handler::create()->export_instance_data_object($course3['id']); | |
1675 | $this->assertEquals(['test' => $updatedcustomfieldvalue['value']], (array)$customfields); | |
791723c3 | 1676 | } |
05fc7ccc | 1677 | |
79949c1b MN |
1678 | /** |
1679 | * Test delete course_module. | |
1680 | */ | |
1681 | public function test_delete_modules() { | |
1682 | global $DB; | |
1683 | ||
1684 | // Ensure we reset the data after this test. | |
1685 | $this->resetAfterTest(true); | |
1686 | ||
1687 | // Create a user. | |
1688 | $user = self::getDataGenerator()->create_user(); | |
1689 | ||
1690 | // Set the tests to run as the user. | |
1691 | self::setUser($user); | |
1692 | ||
1693 | // Create a course to add the modules. | |
1694 | $course = self::getDataGenerator()->create_course(); | |
1695 | ||
1696 | // Create two test modules. | |
1697 | $record = new stdClass(); | |
1698 | $record->course = $course->id; | |
1699 | $module1 = self::getDataGenerator()->create_module('forum', $record); | |
40cb4879 | 1700 | $module2 = self::getDataGenerator()->create_module('assign', $record); |
79949c1b MN |
1701 | |
1702 | // Check the forum was correctly created. | |
1703 | $this->assertEquals(1, $DB->count_records('forum', array('id' => $module1->id))); | |
1704 | ||
1705 | // Check the assignment was correctly created. | |
40cb4879 | 1706 | $this->assertEquals(1, $DB->count_records('assign', array('id' => $module2->id))); |
79949c1b MN |
1707 | |
1708 | // Check data exists in the course modules table. | |
1709 | $this->assertEquals(2, $DB->count_records_select('course_modules', 'id = :module1 OR id = :module2', | |
1710 | array('module1' => $module1->cmid, 'module2' => $module2->cmid))); | |
1711 | ||
1712 | // Enrol the user in the course. | |
1713 | $enrol = enrol_get_plugin('manual'); | |
1714 | $enrolinstances = enrol_get_instances($course->id, true); | |
1715 | foreach ($enrolinstances as $courseenrolinstance) { | |
1716 | if ($courseenrolinstance->enrol == "manual") { | |
1717 | $instance = $courseenrolinstance; | |
1718 | break; | |
1719 | } | |
1720 | } | |
1721 | $enrol->enrol_user($instance, $user->id); | |
1722 | ||
1723 | // Assign capabilities to delete module 1. | |
1724 | $modcontext = context_module::instance($module1->cmid); | |
1725 | $this->assignUserCapability('moodle/course:manageactivities', $modcontext->id); | |
1726 | ||
1727 | // Assign capabilities to delete module 2. | |
1728 | $modcontext = context_module::instance($module2->cmid); | |
1729 | $newrole = create_role('Role 2', 'role2', 'Role 2 description'); | |
1730 | $this->assignUserCapability('moodle/course:manageactivities', $modcontext->id, $newrole); | |
1731 | ||
1732 | // Deleting these module instances. | |
1733 | core_course_external::delete_modules(array($module1->cmid, $module2->cmid)); | |
1734 | ||
1735 | // Check the forum was deleted. | |
1736 | $this->assertEquals(0, $DB->count_records('forum', array('id' => $module1->id))); | |
1737 | ||
1738 | // Check the assignment was deleted. | |
40cb4879 | 1739 | $this->assertEquals(0, $DB->count_records('assign', array('id' => $module2->id))); |
79949c1b MN |
1740 | |
1741 | // Check we retrieve no data in the course modules table. | |
1742 | $this->assertEquals(0, $DB->count_records_select('course_modules', 'id = :module1 OR id = :module2', | |
1743 | array('module1' => $module1->cmid, 'module2' => $module2->cmid))); | |
1744 | ||
1745 | // Call with non-existent course module id and ensure exception thrown. | |
1746 | try { | |
1747 | core_course_external::delete_modules(array('1337')); | |
1748 | $this->fail('Exception expected due to missing course module.'); | |
1749 | } catch (dml_missing_record_exception $e) { | |
affdc3b7 | 1750 | $this->assertEquals('invalidcoursemodule', $e->errorcode); |
79949c1b MN |
1751 | } |
1752 | ||
1753 | // Create two modules. | |
1754 | $module1 = self::getDataGenerator()->create_module('forum', $record); | |
40cb4879 | 1755 | $module2 = self::getDataGenerator()->create_module('assign', $record); |
79949c1b MN |
1756 | |
1757 | // Since these modules were recreated the user will not have capabilities | |
1758 | // to delete them, ensure exception is thrown if they try. | |
1759 | try { | |
1760 | core_course_external::delete_modules(array($module1->cmid, $module2->cmid)); | |
1761 | $this->fail('Exception expected due to missing capability.'); | |
1762 | } catch (moodle_exception $e) { | |
1763 | $this->assertEquals('nopermissions', $e->errorcode); | |
1764 | } | |
1765 | ||
1766 | // Unenrol user from the course. | |
1767 | $enrol->unenrol_user($instance, $user->id); | |
1768 | ||
1769 | // Try and delete modules from the course the user was unenrolled in, make sure exception thrown. | |
1770 | try { | |
1771 | core_course_external::delete_modules(array($module1->cmid, $module2->cmid)); | |
1772 | $this->fail('Exception expected due to being unenrolled from the course.'); | |
1773 | } catch (moodle_exception $e) { | |
1774 | $this->assertEquals('requireloginerror', $e->errorcode); | |
1775 | } | |
1776 | } | |
fce10644 DP |
1777 | |
1778 | /** | |
1779 | * Test import_course into an empty course | |
1780 | */ | |
1781 | public function test_import_course_empty() { | |
1782 | global $USER; | |
1783 | ||
1784 | $this->resetAfterTest(true); | |
1785 | ||
1786 | $course1 = self::getDataGenerator()->create_course(); | |
1787 | $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course1->id, 'name' => 'Forum test')); | |
1788 | $page = $this->getDataGenerator()->create_module('page', array('course' => $course1->id, 'name' => 'Page test')); | |
1789 | ||
1790 | $course2 = self::getDataGenerator()->create_course(); | |
1791 | ||
1792 | $course1cms = get_fast_modinfo($course1->id)->get_cms(); | |
1793 | $course2cms = get_fast_modinfo($course2->id)->get_cms(); | |
1794 | ||
1795 | // Verify the state of the courses before we do the import. | |
1796 | $this->assertCount(2, $course1cms); | |
1797 | $this->assertEmpty($course2cms); | |
1798 | ||
1799 | // Setup the user to run the operation (ugly hack because validate_context() will | |
1800 | // fail as the email is not set by $this->setAdminUser()). | |
1801 | $this->setAdminUser(); | |
0fe86bbd | 1802 | $USER->email = 'emailtopass@example.com'; |
fce10644 DP |
1803 | |
1804 | // Import from course1 to course2. | |
1805 | core_course_external::import_course($course1->id, $course2->id, 0); | |
1806 | ||
1807 | // Verify that now we have two modules in both courses. | |
1808 | $course1cms = get_fast_modinfo($course1->id)->get_cms(); | |
1809 | $course2cms = get_fast_modinfo($course2->id)->get_cms(); | |
1810 | $this->assertCount(2, $course1cms); | |
1811 | $this->assertCount(2, $course2cms); | |
1812 | ||
1813 | // Verify that the names transfered across correctly. | |
1814 | foreach ($course2cms as $cm) { | |
1815 | if ($cm->modname === 'page') { | |
1816 | $this->assertEquals($cm->name, $page->name); | |
1817 | } else if ($cm->modname === 'forum') { | |
1818 | $this->assertEquals($cm->name, $forum->name); | |
1819 | } else { | |
1820 | $this->fail('Unknown CM found.'); | |
1821 | } | |
1822 | } | |
fce10644 DP |
1823 | } |
1824 | ||
1825 | /** | |
1826 | * Test import_course into an filled course | |
1827 | */ | |
1828 | public function test_import_course_filled() { | |
1829 | global $USER; | |
1830 | ||
1831 | $this->resetAfterTest(true); | |
1832 | ||
1833 | // Add forum and page to course1. | |
1834 | $course1 = self::getDataGenerator()->create_course(); | |
1835 | $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course1->id, 'name' => 'Forum test')); | |
1836 | $page = $this->getDataGenerator()->create_module('page', array('course'=>$course1->id, 'name' => 'Page test')); | |
1837 | ||
1838 | // Add quiz to course 2. | |
1839 | $course2 = self::getDataGenerator()->create_course(); | |
1840 | $quiz = $this->getDataGenerator()->create_module('quiz', array('course'=>$course2->id, 'name' => 'Page test')); | |
1841 | ||
1842 | $course1cms = get_fast_modinfo($course1->id)->get_cms(); | |
1843 | $course2cms = get_fast_modinfo($course2->id)->get_cms(); | |
1844 | ||
1845 | // Verify the state of the courses before we do the import. | |
1846 | $this->assertCount(2, $course1cms); | |
1847 | $this->assertCount(1, $course2cms); | |
1848 | ||
1849 | // Setup the user to run the operation (ugly hack because validate_context() will | |
1850 | // fail as the email is not set by $this->setAdminUser()). | |
1851 | $this->setAdminUser(); | |
0fe86bbd | 1852 | $USER->email = 'emailtopass@example.com'; |
fce10644 DP |
1853 | |
1854 | // Import from course1 to course2 without deleting content. | |
1855 | core_course_external::import_course($course1->id, $course2->id, 0); | |
1856 | ||
1857 | $course2cms = get_fast_modinfo($course2->id)->get_cms(); | |
1858 | ||
1859 | // Verify that now we have three modules in course2. | |
1860 | $this->assertCount(3, $course2cms); | |
1861 | ||
1862 | // Verify that the names transfered across correctly. | |
1863 | foreach ($course2cms as $cm) { | |
1864 | if ($cm->modname === 'page') { | |
1865 | $this->assertEquals($cm->name, $page->name); | |
1866 | } else if ($cm->modname === 'forum') { | |
1867 | $this->assertEquals($cm->name, $forum->name); | |
1868 | } else if ($cm->modname === 'quiz') { | |
1869 | $this->assertEquals($cm->name, $quiz->name); | |
1870 | } else { | |
1871 | $this->fail('Unknown CM found.'); | |
1872 | } | |
1873 | } | |
fce10644 DP |
1874 | } |
1875 | ||
1876 | /** | |
1877 | * Test import_course with only blocks set to backup | |
1878 | */ | |
1879 | public function test_import_course_blocksonly() { | |
1880 | global $USER, $DB; | |
1881 | ||
1882 | $this->resetAfterTest(true); | |
1883 | ||
1884 | // Add forum and page to course1. | |
1885 | $course1 = self::getDataGenerator()->create_course(); | |
1886 | $course1ctx = context_course::instance($course1->id); | |
1887 | $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course1->id, 'name' => 'Forum test')); | |
1888 | $block = $this->getDataGenerator()->create_block('online_users', array('parentcontextid' => $course1ctx->id)); | |
1889 | ||
1890 | $course2 = self::getDataGenerator()->create_course(); | |
1891 | $course2ctx = context_course::instance($course2->id); | |
1892 | $initialblockcount = $DB->count_records('block_instances', array('parentcontextid' => $course2ctx->id)); | |
1893 | $initialcmcount = count(get_fast_modinfo($course2->id)->get_cms()); | |
1894 | ||
1895 | // Setup the user to run the operation (ugly hack because validate_context() will | |
1896 | // fail as the email is not set by $this->setAdminUser()). | |
1897 | $this->setAdminUser(); | |
0fe86bbd | 1898 | $USER->email = 'emailtopass@example.com'; |
fce10644 DP |
1899 | |
1900 | // Import from course1 to course2 without deleting content, but excluding | |
1901 | // activities. | |
1902 | $options = array( | |
1903 | array('name' => 'activities', 'value' => 0), | |
1904 | array('name' => 'blocks', 'value' => 1), | |
1905 | array('name' => 'filters', 'value' => 0), | |
1906 | ); | |
1907 | ||
1908 | core_course_external::import_course($course1->id, $course2->id, 0, $options); | |
1909 | ||
1910 | $newcmcount = count(get_fast_modinfo($course2->id)->get_cms()); | |
1911 | $newblockcount = $DB->count_records('block_instances', array('parentcontextid' => $course2ctx->id)); | |
1912 | // Check that course modules haven't changed, but that blocks have. | |
1913 | $this->assertEquals($initialcmcount, $newcmcount); | |
1914 | $this->assertEquals(($initialblockcount + 1), $newblockcount); | |
fce10644 DP |
1915 | } |
1916 | ||
1917 | /** | |
1918 | * Test import_course into an filled course, deleting content. | |
1919 | */ | |
1920 | public function test_import_course_deletecontent() { | |
1921 | global $USER; | |
1922 | $this->resetAfterTest(true); | |
1923 | ||
1924 | // Add forum and page to course1. | |
1925 | $course1 = self::getDataGenerator()->create_course(); | |
1926 | $forum = $this->getDataGenerator()->create_module('forum', array('course'=>$course1->id, 'name' => 'Forum test')); | |
1927 | $page = $this->getDataGenerator()->create_module('page', array('course'=>$course1->id, 'name' => 'Page test')); | |
1928 | ||
1929 | // Add quiz to course 2. | |
1930 | $course2 = self::getDataGenerator()->create_course(); | |
1931 | $quiz = $this->getDataGenerator()->create_module('quiz', array('course'=>$course2->id, 'name' => 'Page test')); | |
1932 | ||
1933 | $course1cms = get_fast_modinfo($course1->id)->get_cms(); | |
1934 | $course2cms = get_fast_modinfo($course2->id)->get_cms(); | |
1935 | ||
1936 | // Verify the state of the courses before we do the import. | |
1937 | $this->assertCount(2, $course1cms); | |
1938 | $this->assertCount(1, $course2cms); | |
1939 | ||
1940 | // Setup the user to run the operation (ugly hack because validate_context() will | |
1941 | // fail as the email is not set by $this->setAdminUser()). | |
1942 | $this->setAdminUser(); | |
0fe86bbd | 1943 | $USER->email = 'emailtopass@example.com'; |
fce10644 DP |
1944 | |
1945 | // Import from course1 to course2, deleting content. | |
1946 | core_course_external::import_course($course1->id, $course2->id, 1); | |
1947 | ||
1948 | $course2cms = get_fast_modinfo($course2->id)->get_cms(); | |
1949 | ||
1950 | // Verify that now we have two modules in course2. | |
1951 | $this->assertCount(2, $course2cms); | |
1952 | ||
1953 | // Verify that the course only contains the imported modules. | |
1954 | foreach ($course2cms as $cm) { | |
1955 | if ($cm->modname === 'page') { | |
1956 | $this->assertEquals($cm->name, $page->name); | |
1957 | } else if ($cm->modname === 'forum') { | |
1958 | $this->assertEquals($cm->name, $forum->name); | |
1959 | } else { | |
1960 | $this->fail('Unknown CM found: '.$cm->name); | |
1961 | } | |
1962 | } | |
fce10644 DP |
1963 | } |
1964 | ||
1965 | /** | |
1966 | * Ensure import_course handles incorrect deletecontent option correctly. | |
1967 | */ | |
1968 | public function test_import_course_invalid_deletecontent_option() { | |
1969 | $this->resetAfterTest(true); | |
1970 | ||
1971 | $course1 = self::getDataGenerator()->create_course(); | |
1972 | $course2 = self::getDataGenerator()->create_course(); | |
1973 | ||
52f3e060 RT |
1974 | $this->expectException('moodle_exception'); |
1975 | $this->expectExceptionMessage(get_string('invalidextparam', 'webservice', -1)); | |
fce10644 DP |
1976 | // Import from course1 to course2, with invalid option |
1977 | core_course_external::import_course($course1->id, $course2->id, -1);; | |
1978 | } | |
e81f67ca JL |
1979 | |
1980 | /** | |
1981 | * Test view_course function | |
1982 | */ | |
1983 | public function test_view_course() { | |
1984 | ||
1985 | $this->resetAfterTest(); | |
1986 | ||
1987 | // Course without sections. | |
1988 | $course = $this->getDataGenerator()->create_course(array('numsections' => 5), array('createsections' => true)); | |
1989 | $this->setAdminUser(); | |
1990 | ||
1991 | // Redirect events to the sink, so we can recover them later. | |
1992 | $sink = $this->redirectEvents(); | |
1993 | ||
bdf9f4d4 JL |
1994 | $result = core_course_external::view_course($course->id, 1); |
1995 | $result = external_api::clean_returnvalue(core_course_external::view_course_returns(), $result); | |
e81f67ca JL |
1996 | $events = $sink->get_events(); |
1997 | $event = reset($events); | |
1998 | ||
1999 | // Check the event details are correct. | |
2000 | $this->assertInstanceOf('\core\event\course_viewed', $event); | |
2001 | $this->assertEquals(context_course::instance($course->id), $event->get_context()); | |
2002 | $this->assertEquals(1, $event->other['coursesectionnumber']); | |
2003 | ||
bdf9f4d4 JL |
2004 | $result = core_course_external::view_course($course->id); |
2005 | $result = external_api::clean_returnvalue(core_course_external::view_course_returns(), $result); | |
e81f67ca JL |
2006 | $events = $sink->get_events(); |
2007 | $event = array_pop($events); | |
2008 | $sink->close(); | |
2009 | ||
2010 | // Check the event details are correct. | |
2011 | $this->assertInstanceOf('\core\event\course_viewed', $event); | |
2012 | $this->assertEquals(context_course::instance($course->id), $event->get_context()); | |
2013 | $this->assertEmpty($event->other); | |
2014 | ||
2015 | } | |
c5158499 JL |
2016 | |
2017 | /** | |
2018 | * Test get_course_module | |
2019 | */ | |
2020 | public function test_get_course_module() { | |
2021 | global $DB; | |
2022 | ||
2023 | $this->resetAfterTest(true); | |
2024 | ||
2025 | $this->setAdminUser(); | |
2026 | $course = self::getDataGenerator()->create_course(); | |
2027 | $record = array( | |
2028 | 'course' => $course->id, | |
796876b0 | 2029 | 'name' => 'First Assignment' |
c5158499 JL |
2030 | ); |
2031 | $options = array( | |
2032 | 'idnumber' => 'ABC', | |
2033 | 'visible' => 0 | |
2034 | ); | |
2035 | // Hidden activity. | |
796876b0 | 2036 | $assign = self::getDataGenerator()->create_module('assign', $record, $options); |
c5158499 | 2037 | |
28ff87be PFO |
2038 | $outcomescale = 'Distinction, Very Good, Good, Pass, Fail'; |
2039 | ||
2040 | // Insert a custom grade scale to be used by an outcome. | |
2041 | $gradescale = new grade_scale(); | |
2042 | $gradescale->name = 'gettcoursemodulescale'; | |
2043 | $gradescale->courseid = $course->id; | |
2044 | $gradescale->userid = 0; | |
2045 | $gradescale->scale = $outcomescale; | |
2046 | $gradescale->description = 'This scale is used to mark standard assignments.'; | |
2047 | $gradescale->insert(); | |
2048 | ||
2049 | // Insert an outcome. | |
2050 | $data = new stdClass(); | |
2051 | $data->courseid = $course->id; | |
2052 | $data->fullname = 'Team work'; | |
2053 | $data->shortname = 'Team work'; | |
2054 | $data->scaleid = $gradescale->id; | |
2055 | $outcome = new grade_outcome($data, false); | |
2056 | $outcome->insert(); | |
2057 | ||
2058 | $outcomegradeitem = new grade_item(); | |
2059 | $outcomegradeitem->itemname = $outcome->shortname; | |
2060 | $outcomegradeitem->itemtype = 'mod'; | |
2061 | $outcomegradeitem->itemmodule = 'assign'; | |
2062 | $outcomegradeitem->iteminstance = $assign->id; | |
2063 | $outcomegradeitem->outcomeid = $outcome->id; | |
2064 | $outcomegradeitem->cmid = 0; | |
2065 | $outcomegradeitem->courseid = $course->id; | |
2066 | $outcomegradeitem->aggregationcoef = 0; | |
fcc88fdd | 2067 | $outcomegradeitem->itemnumber = 1000; // Outcomes start at 1000. |
28ff87be PFO |
2068 | $outcomegradeitem->gradetype = GRADE_TYPE_SCALE; |
2069 | $outcomegradeitem->scaleid = $outcome->scaleid; | |
2070 | $outcomegradeitem->insert(); | |
2071 | ||
2072 | $assignmentgradeitem = grade_item::fetch( | |
2073 | array( | |
2074 | 'itemtype' => 'mod', | |
2075 | 'itemmodule' => 'assign', | |
2076 | 'iteminstance' => $assign->id, | |
2077 | 'itemnumber' => 0, | |
2078 | 'courseid' => $course->id | |
2079 | ) | |
2080 | ); | |
2081 | $outcomegradeitem->set_parent($assignmentgradeitem->categoryid); | |
2082 | $outcomegradeitem->move_after_sortorder($assignmentgradeitem->sortorder); | |
2083 | ||
c5158499 | 2084 | // Test admin user can see the complete hidden activity. |
796876b0 | 2085 | $result = core_course_external::get_course_module($assign->cmid); |
c5158499 JL |
2086 | $result = external_api::clean_returnvalue(core_course_external::get_course_module_returns(), $result); |
2087 | ||
2088 | $this->assertCount(0, $result['warnings']); | |
2089 | // Test we retrieve all the fields. | |
8341055e | 2090 | $this->assertCount(28, $result['cm']); |
c5158499 JL |
2091 | $this->assertEquals($record['name'], $result['cm']['name']); |
2092 | $this->assertEquals($options['idnumber'], $result['cm']['idnumber']); | |
796876b0 JL |
2093 | $this->assertEquals(100, $result['cm']['grade']); |
2094 | $this->assertEquals(0.0, $result['cm']['gradepass']); | |
2095 | $this->assertEquals('submissions', $result['cm']['advancedgrading'][0]['area']); | |
2096 | $this->assertEmpty($result['cm']['advancedgrading'][0]['method']); | |
28ff87be | 2097 | $this->assertEquals($outcomescale, $result['cm']['outcomes'][0]['scale']); |
c5158499 JL |
2098 | |
2099 | $student = $this->getDataGenerator()->create_user(); | |
2100 | $studentrole = $DB->get_record('role', array('shortname' => 'student')); | |
2101 | ||
2102 | self::getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id); | |
2103 | $this->setUser($student); | |
2104 | ||
2105 | // The user shouldn't be able to see the activity. | |
2106 | try { | |
796876b0 | 2107 | core_course_external::get_course_module($assign->cmid); |
c5158499 JL |
2108 | $this->fail('Exception expected due to invalid permissions.'); |
2109 | } catch (moodle_exception $e) { | |
2110 | $this->assertEquals('requireloginerror', $e->errorcode); | |
2111 | } | |
2112 | ||
2113 | // Make module visible. | |
796876b0 | 2114 | set_coursemodule_visible($assign->cmid, 1); |
c5158499 JL |
2115 | |
2116 | // Test student user. | |
796876b0 | 2117 | $result = core_course_external::get_course_module($assign->cmid); |
c5158499 JL |
2118 | $result = external_api::clean_returnvalue(core_course_external::get_course_module_returns(), $result); |
2119 | ||
2120 | $this->assertCount(0, $result['warnings']); | |
2121 | // Test we retrieve only the few files we can see. | |
2122 | $this->assertCount(11, $result['cm']); | |
796876b0 | 2123 | $this->assertEquals($assign->cmid, $result['cm']['id']); |
c5158499 | 2124 | $this->assertEquals($course->id, $result['cm']['course']); |
796876b0 JL |
2125 | $this->assertEquals('assign', $result['cm']['modname']); |
2126 | $this->assertEquals($assign->id, $result['cm']['instance']); | |
c5158499 JL |
2127 | |
2128 | } | |
13bb6819 JL |
2129 | |
2130 | /** | |
2131 | * Test get_course_module_by_instance | |
2132 | */ | |
2133 | public function test_get_course_module_by_instance() { | |
2134 | global $DB; | |
2135 | ||
2136 | $this->resetAfterTest(true); | |
2137 | ||
2138 | $this->setAdminUser(); | |
2139 | $course = self::getDataGenerator()->create_course(); | |
2140 | $record = array( | |
2141 | 'course' => $course->id, | |
7ddb5f25 JL |
2142 | 'name' => 'First quiz', |
2143 | 'grade' => 90.00 | |
13bb6819 JL |
2144 | ); |
2145 | $options = array( | |
2146 | 'idnumber' => 'ABC', | |
2147 | 'visible' => 0 | |
2148 | ); | |
2149 | // Hidden activity. | |
7ddb5f25 | 2150 | $quiz = self::getDataGenerator()->create_module('quiz', $record, $options); |
13bb6819 JL |
2151 | |
2152 | // Test admin user can see the complete hidden activity. | |
7ddb5f25 | 2153 | $result = core_course_external::get_course_module_by_instance('quiz', $quiz->id); |
13bb6819 JL |
2154 | $result = external_api::clean_returnvalue(core_course_external::get_course_module_by_instance_returns(), $result); |
2155 | ||
2156 | $this->assertCount(0, $result['warnings']); | |
2157 | // Test we retrieve all the fields. | |
7ddb5f25 | 2158 | $this->assertCount(26, $result['cm']); |
13bb6819 | 2159 | $this->assertEquals($record['name'], $result['cm']['name']); |
7ddb5f25 | 2160 | $this->assertEquals($record['grade'], $result['cm']['grade']); |
13bb6819 JL |
2161 | $this->assertEquals($options['idnumber'], $result['cm']['idnumber']); |
2162 | ||
2163 | $student = $this->getDataGenerator()->create_user(); | |
2164 | $studentrole = $DB->get_record('role', array('shortname' => 'student')); | |
2165 | ||
2166 | self::getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id); | |
2167 | $this->setUser($student); | |
2168 | ||
2169 | // The user shouldn't be able to see the activity. | |
2170 | try { | |
7ddb5f25 | 2171 | core_course_external::get_course_module_by_instance('quiz', $quiz->id); |
13bb6819 JL |
2172 | $this->fail('Exception expected due to invalid permissions.'); |
2173 | } catch (moodle_exception $e) { | |
2174 | $this->assertEquals('requireloginerror', $e->errorcode); | |
2175 | } | |
2176 | ||
2177 | // Make module visible. | |
7ddb5f25 | 2178 | set_coursemodule_visible($quiz->cmid, 1); |
13bb6819 JL |
2179 | |
2180 | // Test student user. | |
7ddb5f25 | 2181 | $result = core_course_external::get_course_module_by_instance('quiz', $quiz->id); |
13bb6819 JL |
2182 | $result = external_api::clean_returnvalue(core_course_external::get_course_module_by_instance_returns(), $result); |
2183 | ||
2184 | $this->assertCount(0, $result['warnings']); | |
2185 | // Test we retrieve only the few files we can see. | |
2186 | $this->assertCount(11, $result['cm']); | |
7ddb5f25 | 2187 | $this->assertEquals($quiz->cmid, $result['cm']['id']); |
13bb6819 | 2188 | $this->assertEquals($course->id, $result['cm']['course']); |
7ddb5f25 JL |
2189 | $this->assertEquals('quiz', $result['cm']['modname']); |
2190 | $this->assertEquals($quiz->id, $result['cm']['instance']); | |
13bb6819 JL |
2191 | |
2192 | // Try with an invalid module name. | |
2193 | try { | |
7ddb5f25 | 2194 | core_course_external::get_course_module_by_instance('abc', $quiz->id); |
13bb6819 JL |
2195 | $this->fail('Exception expected due to invalid module name.'); |
2196 | } catch (dml_read_exception $e) { | |
2197 | $this->assertEquals('dmlreadexception', $e->errorcode); | |
2198 | } | |
2199 | ||
2200 | } | |
7c4e686f | 2201 | |
c115ff6a JL |
2202 | /** |
2203 | * Test get_user_navigation_options | |
2204 | */ | |
2205 | public function test_get_user_navigation_options() { | |
2206 | global $USER; | |
2207 | ||
2208 | $this->resetAfterTest(); | |
2209 | $course1 = self::getDataGenerator()->create_course(); | |
2210 | $course2 = self::getDataGenerator()->create_course(); | |
2211 | ||
2212 | // Create a viewer user. | |
2213 | $viewer = self::getDataGenerator()->create_user(); | |
2214 | $this->getDataGenerator()->enrol_user($viewer->id, $course1->id); | |
2215 | $this->getDataGenerator()->enrol_user($viewer->id, $course2->id); | |
2216 | ||
2217 | $this->setUser($viewer->id); | |
2218 | $courses = array($course1->id , $course2->id, SITEID); | |
2219 | ||
2220 | $result = core_course_external::get_user_navigation_options($courses); | |
2221 | $result = external_api::clean_returnvalue(core_course_external::get_user_navigation_options_returns(), $result); | |
2222 | ||
2223 | $this->assertCount(0, $result['warnings']); | |
2224 | $this->assertCount(3, $result['courses']); | |
2225 | ||
2226 | foreach ($result['courses'] as $course) { | |
2227 | $navoptions = new stdClass; | |
2228 | foreach ($course['options'] as $option) { | |
2229 | $navoptions->{$option['name']} = $option['available']; | |
2230 | } | |
203f51d6 | 2231 | $this->assertCount(9, $course['options']); |
c115ff6a | 2232 | if ($course['id'] == SITEID) { |
c115ff6a JL |
2233 | $this->assertTrue($navoptions->blogs); |
2234 | $this->assertFalse($navoptions->notes); | |
2235 | $this->assertFalse($navoptions->participants); | |
2236 | $this->assertTrue($navoptions->badges); | |
2237 | $this->assertTrue($navoptions->tags); | |
203f51d6 | 2238 | $this->assertFalse($navoptions->grades); |
c115ff6a JL |
2239 | $this->assertFalse($navoptions->search); |
2240 | $this->assertTrue($navoptions->calendar); | |
99061152 | 2241 | $this->assertTrue($navoptions->competencies); |
c115ff6a | 2242 | } else { |
c115ff6a JL |
2243 | $this->assertTrue($navoptions->blogs); |
2244 | $this->assertFalse($navoptions->notes); | |
2245 | $this->assertTrue($navoptions->participants); | |
2246 | $this->assertTrue($navoptions->badges); | |
203f51d6 | 2247 | $this->assertFalse($navoptions->tags); |
99061152 | 2248 | $this->assertTrue($navoptions->grades); |
203f51d6 DP |
2249 | $this->assertFalse($navoptions->search); |
2250 | $this->assertFalse($navoptions->calendar); | |
99061152 | 2251 | $this->assertTrue($navoptions->competencies); |
c115ff6a JL |
2252 | } |
2253 | } | |
2254 | } | |
b9050b10 JL |
2255 | |
2256 | /** | |
2257 | * Test get_user_administration_options | |
2258 | */ | |
2259 | public function test_get_user_administration_options() { | |
2260 | global $USER; | |
2261 | ||
2262 | $this->resetAfterTest(); | |
2263 | $course1 = self::getDataGenerator()->create_course(); | |
2264 | $course2 = self::getDataGenerator()->create_course(); | |
2265 | ||
2266 | // Create a viewer user. | |
2267 | $viewer = self::getDataGenerator()->create_user(); | |
2268 | $this->getDataGenerator()->enrol_user($viewer->id, $course1->id); | |
2269 | $this->getDataGenerator()->enrol_user($viewer->id, $course2->id); | |
2270 | ||
2271 | $this->setUser($viewer->id); | |
2272 | $courses = array($course1->id , $course2->id, SITEID); | |
2273 | ||
2274 | $result = core_course_external::get_user_administration_options($courses); | |
2275 | $result = external_api::clean_returnvalue(core_course_external::get_user_administration_options_returns(), $result); | |
2276 | ||
2277 | $this->assertCount(0, $result['warnings']); | |
2278 | $this->assertCount(3, $result['courses']); | |
2279 | ||
2280 | foreach ($result['courses'] as $course) { | |
2281 | $adminoptions = new stdClass; | |
2282 | foreach ($course['options'] as $option) { | |
2283 | $adminoptions->{$option['name']} = $option['available']; | |
2284 | } | |
2285 | if ($course['id'] == SITEID) { | |
0cbc248d | 2286 | $this->assertCount(16, $course['options']); |
b9050b10 JL |
2287 | $this->assertFalse($adminoptions->update); |
2288 | $this->assertFalse($adminoptions->filters); | |
2289 | $this->assertFalse($adminoptions->reports); | |
2290 | $this->assertFalse($adminoptions->backup); | |
2291 | $this->assertFalse($adminoptions->restore); | |
2292 | $this->assertFalse($adminoptions->files); | |
c874d9aa JL |
2293 | $this->assertFalse(!isset($adminoptions->tags)); |
2294 | $this->assertFalse($adminoptions->gradebook); | |
2295 | $this->assertFalse($adminoptions->outcomes); | |
2296 | $this->assertFalse($adminoptions->badges); | |
2297 | $this->assertFalse($adminoptions->import); | |
c874d9aa JL |
2298 | $this->assertFalse($adminoptions->reset); |
2299 | $this->assertFalse($adminoptions->roles); | |
0cbc248d | 2300 | $this->assertFalse($adminoptions->editcompletion); |
b9050b10 | 2301 | } else { |
020bad73 | 2302 | $this->assertCount(14, $course['options']); |
b9050b10 JL |
2303 | $this->assertFalse($adminoptions->update); |
2304 | $this->assertFalse($adminoptions->filters); | |
2305 | $this->assertFalse($adminoptions->reports); | |
2306 | $this->assertFalse($adminoptions->backup); | |
2307 | $this->assertFalse($adminoptions->restore); | |
2308 | $this->assertFalse($adminoptions->files); | |
2309 | $this->assertFalse($adminoptions->tags); | |
2310 | $this->assertFalse($adminoptions->gradebook); | |
2311 | $this->assertFalse($adminoptions->outcomes); | |
2312 | $this->assertTrue($adminoptions->badges); | |
2313 | $this->assertFalse($adminoptions->import); | |
b9050b10 JL |
2314 | $this->assertFalse($adminoptions->reset); |
2315 | $this->assertFalse($adminoptions->roles); | |
0cbc248d | 2316 | $this->assertFalse($adminoptions->editcompletion); |
b9050b10 JL |
2317 | } |
2318 | } | |
2319 | } | |
80adabef JL |
2320 | |
2321 | /** | |
2322 | * Test get_courses_by_fields | |
2323 | */ | |
2324 | public function test_get_courses_by_field() { | |
2325 | global $DB; | |
2326 | $this->resetAfterTest(true); | |
2327 | ||
8c9a1964 | 2328 | $category1 = self::getDataGenerator()->create_category(array('name' => 'Cat 1')); |
80adabef | 2329 | $category2 = self::getDataGenerator()->create_category(array('parent' => $category1->id)); |
cf58a2d5 JL |
2330 | $course1 = self::getDataGenerator()->create_course( |
2331 | array('category' => $category1->id, 'shortname' => 'c1', 'format' => 'topics')); | |
bfae6ca7 JL |
2332 | |
2333 | $fieldcategory = self::getDataGenerator()->create_custom_field_category(['name' => 'Other fields']); | |
2334 | $customfield = ['shortname' => 'test', 'name' => 'Custom field', 'type' => 'text', | |
2335 | 'categoryid' => $fieldcategory->get('id')]; | |
2336 | $field = self::getDataGenerator()->create_custom_field($customfield); | |
2337 | $customfieldvalue = ['shortname' => 'test', 'value' => 'Test value']; | |
2338 | $course2 = self::getDataGenerator()->create_course(array('visible' => 0, 'category' => $category2->id, 'idnumber' => 'i2', 'customfields' => [$customfieldvalue])); | |
80adabef JL |
2339 | |
2340 | $student1 = self::getDataGenerator()->create_user(); | |
2341 | $user1 = self::getDataGenerator()->create_user(); | |
2342 | $studentrole = $DB->get_record('role', array('shortname' => 'student')); | |
2343 | self::getDataGenerator()->enrol_user($student1->id, $course1->id, $studentrole->id); | |
2344 | self::getDataGenerator()->enrol_user($student1->id, $course2->id, $studentrole->id); | |
2345 | ||
2346 | self::setAdminUser(); | |
2347 | // As admins, we should be able to retrieve everything. | |
2348 | $result = core_course_external::get_courses_by_field(); | |
2349 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2350 | $this->assertCount(3, $result['courses']); | |
2351 | // Expect to receive all the fields. | |
bfae6ca7 JL |
2352 | $this->assertCount(38, $result['courses'][0]); |
2353 | $this->assertCount(39, $result['courses'][1]); // One more field because is not the site course. | |
2354 | $this->assertCount(39, $result['courses'][2]); // One more field because is not the site course. | |
80adabef JL |
2355 | |
2356 | $result = core_course_external::get_courses_by_field('id', $course1->id); | |
2357 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2358 | $this->assertCount(1, $result['courses']); | |
2359 | $this->assertEquals($course1->id, $result['courses'][0]['id']); | |
2360 | // Expect to receive all the fields. | |
bfae6ca7 | 2361 | $this->assertCount(39, $result['courses'][0]); |
cf58a2d5 JL |
2362 | // Check default values for course format topics. |
2363 | $this->assertCount(2, $result['courses'][0]['courseformatoptions']); | |
2364 | foreach ($result['courses'][0]['courseformatoptions'] as $option) { | |
2365 | if ($option['name'] == 'hiddensections') { | |
2366 | $this->assertEquals(0, $option['value']); | |
2367 | } else { | |
2368 | $this->assertEquals('coursedisplay', $option['name']); | |
2369 | $this->assertEquals(0, $option['value']); | |
2370 | } | |
2371 | } | |
80adabef JL |
2372 | |
2373 | $result = core_course_external::get_courses_by_field('id', $course2->id); | |
2374 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2375 | $this->assertCount(1, $result['courses']); | |
2376 | $this->assertEquals($course2->id, $result['courses'][0]['id']); | |
bfae6ca7 JL |
2377 | // Check custom fields properly returned. |
2378 | unset($customfield['categoryid']); | |
2379 | $this->assertEquals([array_merge($customfield, $customfieldvalue)], $result['courses'][0]['customfields']); | |
80adabef JL |
2380 | |
2381 | $result = core_course_external::get_courses_by_field('ids', "$course1->id,$course2->id"); | |
2382 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2383 | $this->assertCount(2, $result['courses']); | |
2384 | ||
e45fc71e | 2385 | // Check default filters. |
acae15c7 AA |
2386 | $this->assertCount(4, $result['courses'][0]['filters']); |
2387 | $this->assertCount(4, $result['courses'][1]['filters']); | |
e45fc71e | 2388 | |
80adabef JL |
2389 | $result = core_course_external::get_courses_by_field('category', $category1->id); |
2390 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2391 | $this->assertCount(1, $result['courses']); | |
2392 | $this->assertEquals($course1->id, $result['courses'][0]['id']); | |
8c9a1964 | 2393 | $this->assertEquals('Cat 1', $result['courses'][0]['categoryname']); |
80adabef JL |
2394 | |
2395 | $result = core_course_external::get_courses_by_field('shortname', 'c1'); | |
2396 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2397 | $this->assertCount(1, $result['courses']); | |
2398 | $this->assertEquals($course1->id, $result['courses'][0]['id']); | |
2399 | ||
2400 | $result = core_course_external::get_courses_by_field('idnumber', 'i2'); | |
2401 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2402 | $this->assertCount(1, $result['courses']); | |
2403 | $this->assertEquals($course2->id, $result['courses'][0]['id']); | |
2404 | ||
2405 | $result = core_course_external::get_courses_by_field('idnumber', 'x'); | |
2406 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2407 | $this->assertCount(0, $result['courses']); | |
2408 | ||
e45fc71e JL |
2409 | // Change filter value. |
2410 | filter_set_local_state('mediaplugin', context_course::instance($course1->id)->id, TEXTFILTER_OFF); | |
2411 | ||
80adabef JL |
2412 | self::setUser($student1); |
2413 | // All visible courses (including front page) for normal student. | |
2414 | $result = core_course_external::get_courses_by_field(); | |
2415 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2416 | $this->assertCount(2, $result['courses']); | |
bfae6ca7 JL |
2417 | $this->assertCount(31, $result['courses'][0]); |
2418 | $this->assertCount(32, $result['courses'][1]); // One field more (course format options), not present in site course. | |
80adabef JL |
2419 | |
2420 | $result = core_course_external::get_courses_by_field('id', $course1->id); | |
2421 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2422 | $this->assertCount(1, $result['courses']); | |
2423 | $this->assertEquals($course1->id, $result['courses'][0]['id']); | |
2424 | // Expect to receive all the files that a student can see. | |
bfae6ca7 | 2425 | $this->assertCount(32, $result['courses'][0]); |
e45fc71e JL |
2426 | |
2427 | // Check default filters. | |
2428 | $filters = $result['courses'][0]['filters']; | |
acae15c7 | 2429 | $this->assertCount(4, $filters); |
e45fc71e JL |
2430 | $found = false; |
2431 | foreach ($filters as $filter) { | |
2432 | if ($filter['filter'] == 'mediaplugin' and $filter['localstate'] == TEXTFILTER_OFF) { | |
2433 | $found = true; | |
2434 | } | |
2435 | } | |
2436 | $this->assertTrue($found); | |
80adabef JL |
2437 | |
2438 | // Course 2 is not visible. | |
2439 | $result = core_course_external::get_courses_by_field('id', $course2->id); | |
2440 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2441 | $this->assertCount(0, $result['courses']); | |
2442 | ||
2443 | $result = core_course_external::get_courses_by_field('ids', "$course1->id,$course2->id"); | |
2444 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2445 | $this->assertCount(1, $result['courses']); | |
2446 | ||
2447 | $result = core_course_external::get_courses_by_field('category', $category1->id); | |
2448 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2449 | $this->assertCount(1, $result['courses']); | |
2450 | $this->assertEquals($course1->id, $result['courses'][0]['id']); | |
2451 | ||
2452 | $result = core_course_external::get_courses_by_field('shortname', 'c1'); | |
2453 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2454 | $this->assertCount(1, $result['courses']); | |
2455 | $this->assertEquals($course1->id, $result['courses'][0]['id']); | |
2456 | ||
2457 | $result = core_course_external::get_courses_by_field('idnumber', 'i2'); | |
2458 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2459 | $this->assertCount(0, $result['courses']); | |
2460 | ||
2461 | $result = core_course_external::get_courses_by_field('idnumber', 'x'); | |
2462 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2463 | $this->assertCount(0, $result['courses']); | |
2464 | ||
2465 | self::setUser($user1); | |
2466 | // All visible courses (including front page) for authenticated user. | |
2467 | $result = core_course_external::get_courses_by_field(); | |
2468 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2469 | $this->assertCount(2, $result['courses']); | |
bfae6ca7 JL |
2470 | $this->assertCount(31, $result['courses'][0]); // Site course. |
2471 | $this->assertCount(14, $result['courses'][1]); // Only public information, not enrolled. | |
80adabef JL |
2472 | |
2473 | $result = core_course_external::get_courses_by_field('id', $course1->id); | |
2474 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2475 | $this->assertCount(1, $result['courses']); | |
2476 | $this->assertEquals($course1->id, $result['courses'][0]['id']); | |
2477 | // Expect to receive all the files that a authenticated can see. | |
bfae6ca7 | 2478 | $this->assertCount(14, $result['courses'][0]); |
80adabef JL |
2479 | |
2480 | // Course 2 is not visible. | |
2481 | $result = core_course_external::get_courses_by_field('id', $course2->id); | |
2482 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2483 | $this->assertCount(0, $result['courses']); | |
2484 | ||
2485 | $result = core_course_external::get_courses_by_field('ids', "$course1->id,$course2->id"); | |
2486 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2487 | $this->assertCount(1, $result['courses']); | |
2488 | ||
2489 | $result = core_course_external::get_courses_by_field('category', $category1->id); | |
2490 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2491 | $this->assertCount(1, $result['courses']); | |
2492 | $this->assertEquals($course1->id, $result['courses'][0]['id']); | |
2493 | ||
2494 | $result = core_course_external::get_courses_by_field('shortname', 'c1'); | |
2495 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2496 | $this->assertCount(1, $result['courses']); | |
2497 | $this->assertEquals($course1->id, $result['courses'][0]['id']); | |
2498 | ||
2499 | $result = core_course_external::get_courses_by_field('idnumber', 'i2'); | |
2500 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2501 | $this->assertCount(0, $result['courses']); | |
2502 | ||
2503 | $result = core_course_external::get_courses_by_field('idnumber', 'x'); | |
2504 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2505 | $this->assertCount(0, $result['courses']); | |
2506 | } | |
2507 | ||
2508 | public function test_get_courses_by_field_invalid_field() { | |
2509 | $this->expectException('invalid_parameter_exception'); | |
2510 | $result = core_course_external::get_courses_by_field('zyx', 'x'); | |
2511 | } | |
2512 | ||
2513 | public function test_get_courses_by_field_invalid_courses() { | |
2514 | $result = core_course_external::get_courses_by_field('id', '-1'); | |
2515 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2516 | $this->assertCount(0, $result['courses']); | |
2517 | } | |
26659f62 | 2518 | |
6db24235 JL |
2519 | /** |
2520 | * Test get_courses_by_field_invalid_theme_and_lang | |
2521 | */ | |
2522 | public function test_get_courses_by_field_invalid_theme_and_lang() { | |
2523 | $this->resetAfterTest(true); | |
2524 | $this->setAdminUser(); | |
2525 | ||
2526 | $course = self::getDataGenerator()->create_course(array('theme' => 'kkt', 'lang' => 'kkl')); | |
2527 | $result = core_course_external::get_courses_by_field('id', $course->id); | |
2528 | $result = external_api::clean_returnvalue(core_course_external::get_courses_by_field_returns(), $result); | |
2529 | $this->assertEmpty($result['courses']['0']['theme']); | |
2530 | $this->assertEmpty($result['courses']['0']['lang']); | |
2531 | } | |
2532 | ||
2533 | ||
26659f62 JL |
2534 | public function test_check_updates() { |
2535 | global $DB; | |
2536 | $this->resetAfterTest(true); | |
2537 | $this->setAdminUser(); | |
2538 | ||
2539 | // Create different types of activities. | |
2540 | $course = self::getDataGenerator()->create_course(); | |
2541 | $tocreate = array('assign', 'book', 'choice', 'folder', 'forum', 'glossary', 'imscp', 'label', 'lti', 'page', 'quiz', | |
2542 | 'resource', 'scorm', 'survey', 'url', 'wiki'); | |
2543 | ||
2544 | $modules = array(); | |
2545 | foreach ($tocreate as $modname) { | |
2546 | $modules[$modname]['instance'] = $this->getDataGenerator()->create_module($modname, array('course' => $course->id)); | |
2547 | $modules[$modname]['cm'] = get_coursemodule_from_id(false, $modules[$modname]['instance']->cmid); | |
2548 | $modules[$modname]['context'] = context_module::instance($modules[$modname]['instance']->cmid); | |
2549 | } | |
2550 | ||
2551 | $student = self::getDataGenerator()->create_user(); | |
2552 | $studentrole = $DB->get_record('role', array('shortname' => 'student')); | |
2553 | self::getDataGenerator()->enrol_user($student->id, $course->id, $studentrole->id); | |
2554 | $this->setUser($student); | |
2555 | ||
2556 | $since = time(); | |
2557 | $this->waitForSecond(); | |
2558 | $params = array(); | |
2559 | foreach ($modules as $modname => $data) { | |
2560 | $params[$data['cm']->id] = array( | |
2561 | 'contextlevel' => 'module', | |
2562 | 'id' => $data['cm']->id, | |
2563 | 'since' => $since | |
2564 | ); | |
2565 | } | |
2566 | ||
2567 | // Check there is nothing updated because modules are fresh new. | |
2568 | $result = core_course_external::check_updates($course->id, $params); | |
2569 | $result = external_api::clean_returnvalue(core_course_external::check_updates_returns(), $result); | |
25adfbaa | 2570 | $this->assertCount(0, $result['instances']); |
26659f62 | 2571 | $this->assertCount(0, $result['warnings']); |
26659f62 | 2572 | |
879a8f56 JL |
2573 | // Test with get_updates_since the same data. |
2574 | $result = core_course_external::get_updates_since($course->id, $since); | |
2575 | $result = external_api::clean_returnvalue(core_course_external::get_updates_since_returns(), $result); | |
2576 | $this->assertCount(0, $result['instances']); | |
2577 | $this->assertCount(0, $result['warnings']); | |
2578 | ||
26659f62 JL |
2579 | // Update a module after a second. |
2580 | $this->waitForSecond(); | |
2581 | set_coursemodule_name($modules['forum']['cm']->id, 'New forum name'); | |
2582 | ||
2583 | $found = false; | |
2584 | $result = core_course_external::check_updates($course->id, $params); | |
2585 | $result = external_api::clean_returnvalue(core_course_external::check_updates_returns(), $result); | |
25adfbaa | 2586 | $this->assertCount(1, $result['instances']); |
26659f62 JL |
2587 | $this->assertCount(0, $result['warnings']); |
2588 | foreach ($result['instances'] as $module) { | |
2589 | foreach ($module['updates'] as $update) { | |
2590 | if ($module['id'] == $modules['forum']['cm']->id and $update['name'] == 'configuration') { | |
26659f62 | 2591 | $found = true; |
879a8f56 JL |
2592 | } |
2593 | } | |
2594 | } | |
2595 | $this->assertTrue($found); | |
2596 | ||
2597 | // Test with get_updates_since the same data. | |
2598 | $result = core_course_external::get_updates_since($course->id, $since); | |
2599 | $result = external_api::clean_returnvalue(core_course_external::get_updates_since_returns(), $result); | |
2600 | $this->assertCount(1, $result['instances']); | |
2601 | $this->assertCount(0, $result['warnings']); | |
2602 | $found = false; | |
2603 | $this->assertCount(1, $result['instances']); | |
2604 | $this->assertCount(0, $result['warnings']); | |
2605 | foreach ($result['instances'] as $module) { | |
2606 | foreach ($module['updates'] as $update) { | |
2607 | if ($module['id'] == $modules['forum']['cm']->id and $update['name'] == 'configuration') { | |
2608 | $found = true; | |
26659f62 JL |
2609 | } |
2610 | } | |
2611 | } | |
2612 | $this->assertTrue($found); | |
2613 | ||
2614 | // Do not retrieve the configuration field. | |
2615 | $filter = array('files'); | |
2616 | $found = false; | |
2617 | $result = core_course_external::check_updates($course->id, $params, $filter); | |
2618 | $result = external_api::clean_returnvalue(core_course_external::check_updates_returns(), $result); | |
25adfbaa | 2619 | $this->assertCount(0, $result['instances']); |
26659f62 | 2620 | $this->assertCount(0, $result['warnings']); |
26659f62 JL |
2621 | $this->assertFalse($found); |
2622 | ||
2623 | // Add invalid cmid. | |
2624 | $params[] = array( | |
2625 | 'contextlevel' => 'module', | |
2626 | 'id' => -2, | |
2627 | 'since' => $since | |
2628 | ); | |
2629 | $result = core_course_external::check_updates($course->id, $params); | |
2630 | $result = external_api::clean_returnvalue(core_course_external::check_updates_returns(), $result); | |
2631 | $this->assertCount(1, $result['warnings']); | |
2632 | $this->assertEquals(-2, $result['warnings'][0]['itemid']); | |
2633 | } | |
2c1d19fd RW |
2634 | |
2635 | /** | |
2636 | * Test cases for the get_enrolled_courses_by_timeline_classification test. | |
2637 | */ | |
2638 | public function get_get_enrolled_courses_by_timeline_classification_test_cases() { | |
2639 | $now = time(); | |
2640 | $day = 86400; | |
2641 | ||
2642 | $coursedata = [ | |
2643 | [ | |
2644 | 'shortname' => 'apast', | |
2645 | 'startdate' => $now - ($day * 2), | |
2646 | 'enddate' => $now - $day | |
2647 | ], | |
2648 | [ | |
2649 | 'shortname' => 'bpast', | |
2650 | 'startdate' => $now - ($day * 2), | |
2651 | 'enddate' => $now - $day | |
2652 | ], | |
2653 | [ | |
2654 | 'shortname' => 'cpast', | |
2655 | 'startdate' => $now - ($day * 2), | |
2656 | 'enddate' => $now - $day | |
2657 | ], | |
2658 | [ | |
2659 | 'shortname' => 'dpast', | |
2660 | 'startdate' => $now - ($day * 2), | |
2661 | 'enddate' => $now - $day | |
2662 | ], | |
2663 | [ | |
2664 | 'shortname' => 'epast', | |
2665 | 'startdate' => $now - ($day * 2), | |
2666 | 'enddate' => $now - $day | |
2667 | ], | |
2668 | [ | |
2669 | 'shortname' => 'ainprogress', | |
2670 | 'startdate' => $now - $day, | |
2671 | 'enddate' => $now + $day | |
2672 | ], | |
2673 | [ | |
2674 | 'shortname' => 'binprogress', | |
2675 | 'startdate' => $now - $day, | |
2676 | 'enddate' => $now + $day | |
2677 | ], | |
2678 | [ | |
2679 | 'shortname' => 'cinprogress', | |
2680 | 'startdate' => $now - $day, | |
2681 | 'enddate' => $now + $day | |
2682 | ], | |
2683 | [ | |
2684 | 'shortname' => 'dinprogress', | |
2685 | 'startdate' => $now - $day, | |
2686 | 'enddate' => $now + $day | |
2687 | ], | |
2688 | [ | |
2689 | 'shortname' => 'einprogress', | |
2690 | 'startdate' => $now - $day, | |
2691 | 'enddate' => $now + $day | |
2692 | ], | |
2693 | [ | |
2694 | 'shortname' => 'afuture', | |
2695 | 'startdate' => $now + $day | |
2696 | ], | |
2697 | [ | |
2698 | 'shortname' => 'bfuture', | |
2699 | 'startdate' => $now + $day | |
2700 | ], | |
2701 | [ | |
2702 | 'shortname' => 'cfuture', | |
2703 | 'startdate' => $now + $day | |
2704 | ], | |
2705 | [ | |
2706 | 'shortname' => 'dfuture', | |
2707 | 'startdate' => $now + $day | |
2708 | ], | |
2709 | [ | |
2710 | 'shortname' => 'efuture', | |
2711 | 'startdate' => $now + $day | |
2712 | ] | |
2713 | ]; | |
2714 | ||
2715 | // Raw enrolled courses result set should be returned in this order: | |
2716 | // afuture, ainprogress, apast, bfuture, binprogress, bpast, cfuture, cinprogress, cpast, | |
2717 | // dfuture, dinprogress, dpast, efuture, einprogress, epast | |
2718 | // | |
2719 | // By classification the offset values for each record should be: | |
2720 | // COURSE_TIMELINE_FUTURE | |
2721 | // 0 (afuture), 3 (bfuture), 6 (cfuture), 9 (dfuture), 12 (efuture) | |
2722 | // COURSE_TIMELINE_INPROGRESS | |
2723 | // 1 (ainprogress), 4 (binprogress), 7 (cinprogress), 10 (dinprogress), 13 (einprogress) | |
2724 | // COURSE_TIMELINE_PAST | |
2725 | // 2 (apast), 5 (bpast), 8 (cpast), 11 (dpast), 14 (epast). | |
2726 | // | |
2727 | // NOTE: The offset applies to the unfiltered full set of courses before the classification | |
2728 | // filtering is done. | |
2729 | // E.g. In our example if an offset of 2 is given then it would mean the first | |
2730 | // two courses (afuture, ainprogress) are ignored. | |
2731 | return [ | |
2732 | 'empty set' => [ | |
2733 | 'coursedata' => [], | |
2734 | 'classification' => 'future', | |
2735 | 'limit' => 2, | |
2736 | 'offset' => 0, | |
2737 | 'expectedcourses' => [], | |
2738 | 'expectednextoffset' => 0 | |
2739 | ], | |
2740 | // COURSE_TIMELINE_FUTURE. | |
2741 | 'future not limit no offset' => [ | |
2742 | 'coursedata' => $coursedata, | |
2743 | 'classification' => 'future', | |
2744 | 'limit' => 0, | |
2745 | 'offset' => 0, | |
2746 | 'expectedcourses' => ['afuture', 'bfuture', 'cfuture', 'dfuture', 'efuture'], | |
2747 | 'expectednextoffset' => 15 | |
2748 | ], | |
2749 | 'future no offset' => [ | |
2750 | 'coursedata' => $coursedata, | |
2751 | 'classification' => 'future', | |
2752 | 'limit' => 2, | |
2753 | 'offset' => 0, | |
2754 | 'expectedcourses' => ['afuture', 'bfuture'], | |
2755 | 'expectednextoffset' => 4 | |
2756 | ], | |
2757 | 'future offset' => [ | |
2758 | 'coursedata' => $coursedata, | |
2759 | 'classification' => 'future', | |
2760 | 'limit' => 2, | |
2761 | 'offset' => 2, | |
2762 | 'expectedcourses' => ['bfuture', 'cfuture'], | |
2763 | 'expectednextoffset' => 7 | |
2764 | ], | |
2765 | 'future exact limit' => [ | |
2766 | 'coursedata' => $coursedata, | |
2767 | 'classification' => 'future', | |
2768 | 'limit' => 5, | |
2769 | 'offset' => 0, | |
2770 | 'expectedcourses' => ['afuture', 'bfuture', 'cfuture', 'dfuture', 'efuture'], | |
2771 | 'expectednextoffset' => 13 | |
2772 | ], | |
2773 | 'future limit less results' => [ | |
2774 | 'coursedata' => $coursedata, | |
2775 | 'classification' => 'future', | |
2776 | 'limit' => 10, | |
2777 | 'offset' => 0, | |
2778 | 'expectedcourses' => ['afuture', 'bfuture', 'cfuture', 'dfuture', 'efuture'], | |
2779 | 'expectednextoffset' => 15 | |
2780 | ], | |
2781 | 'future limit less results with offset' => [ | |
2782 | 'coursedata' => $coursedata, | |
2783 | 'classification' => 'future', | |
2784 | 'limit' => 10, | |
2785 | 'offset' => 5, | |
2786 | 'expectedcourses' => ['cfuture', 'dfuture', 'efuture'], | |
2787 | 'expectednextoffset' => 15 | |
2788 | ], | |
6481a21f BB |
2789 | 'all no limit or offset' => [ |
2790 | 'coursedata' => $coursedata, | |
2791 | 'classification' => 'all', | |
2792 | 'limit' => 0, | |
2793 | 'offset' => 0, | |
2794 | 'expectedcourses' => [ | |
2795 | 'afuture', | |
2796 | 'ainprogress', | |
2797 | 'apast', | |
2798 | 'bfuture', | |
2799 | 'binprogress', | |
2800 | 'bpast', | |
2801 | 'cfuture', | |
2802 | 'cinprogress', | |
2803 | 'cpast', | |
2804 | 'dfuture', | |
2805 | 'dinprogress', | |
2806 | 'dpast', | |
2807 | 'efuture', | |
2808 | 'einprogress', | |
2809 | 'epast' | |
2810 | ], | |
2811 | 'expectednextoffset' => 15 | |
2812 | ], | |
2813 | 'all limit no offset' => [ | |
2814 | 'coursedata' => $coursedata, | |
2815 | 'classification' => 'all', | |
2816 | 'limit' => 5, | |
2817 | 'offset' => 0, | |
2818 | 'expectedcourses' => [ | |
2819 | 'afuture', | |
2820 | 'ainprogress', | |
2821 | 'apast', | |
2822 | 'bfuture', | |
2823 | 'binprogress' | |
2824 | ], | |
2825 | 'expectednextoffset' => 5 | |
2826 | ], | |
2827 | 'all limit and offset' => [ | |
2828 | 'coursedata' => $coursedata, | |
2829 | 'classification' => 'all', | |
2830 | 'limit' => 5, | |
2831 | 'offset' => 5, | |
2832 | 'expectedcourses' => [ | |
2833 | 'bpast', | |
2834 | 'cfuture', | |
2835 | 'cinprogress', | |
2836 | 'cpast', | |
2837 | 'dfuture' | |
2838 | ], | |
2839 | 'expectednextoffset' => 10 | |
2840 | ], | |
2841 | 'all offset past result set' => [ | |
2842 | 'coursedata' => $coursedata, | |
2843 | 'classification' => 'all', | |
2844 | 'limit' => 5, | |
2845 | 'offset' => 50, | |
2846 | 'expectedcourses' => [], | |
2847 | 'expectednextoffset' => 50 | |
2848 | ], | |
2c1d19fd RW |
2849 | ]; |
2850 | } | |
2851 | ||
2852 | /** | |
2853 | * Test the get_enrolled_courses_by_timeline_classification function. | |
2854 | * | |
2855 | * @dataProvider get_get_enrolled_courses_by_timeline_classification_test_cases() | |
2856 | * @param array $coursedata Courses to create | |
2857 | * @param string $classification Timeline classification | |
2858 | * @param int $limit Maximum number of results | |
2859 | * @param int $offset Offset the unfiltered courses result set by this amount | |
2860 | * @param array $expectedcourses Expected courses in result | |
2861 | * @param int $expectednextoffset Expected next offset value in result | |
2862 | */ | |
2863 | public function test_get_enrolled_courses_by_timeline_classification( | |
2864 | $coursedata, | |
2865 | $classification, | |
2866 | $limit, | |
2867 | $offset, | |
2868 | $expectedcourses, | |
2869 | $expectednextoffset | |
2870 | ) { | |
2871 | $this->resetAfterTest(); | |
2872 | $generator = $this->getDataGenerator(); | |
2873 | ||
2874 | $courses = array_map(function($coursedata) use ($generator) { | |
2875 | return $generator->create_course($coursedata); | |
2876 | }, $coursedata); | |
2877 | ||
2878 | $student = $generator->create_user(); | |
2879 | ||
2880 | foreach ($courses as $course) { | |
2881 | $generator->enrol_user($student->id, $course->id, 'student'); | |
2882 | } | |
2883 | ||
2884 | $this->setUser($student); | |
2885 | ||
2886 | // NOTE: The offset applies to the unfiltered full set of courses before the classification | |
2887 | // filtering is done. | |
2888 | // E.g. In our example if an offset of 2 is given then it would mean the first | |
2889 | // two courses (afuture, ainprogress) are ignored. | |
2890 | $result = core_course_external::get_enrolled_courses_by_timeline_classification( | |
2891 | $classification, | |
2892 | $limit, | |
2893 | $offset, | |
2894 | 'shortname ASC' | |
2895 | ); | |
2896 | $result = external_api::clean_returnvalue( | |
2897 | core_course_external::get_enrolled_courses_by_timeline_classification_returns(), | |
2898 | $result | |
2899 | ); | |
2900 | ||
2901 | $actual = array_map(function($course) { | |
2902 | return $course['shortname']; | |
2903 | }, $result['courses']); | |
2904 | ||
2905 | $this->assertEquals($expectedcourses, $actual); | |
2906 | $this->assertEquals($expectednextoffset, $result['nextoffset']); | |
2907 | } | |
98a52c80 VD |
2908 | |
2909 | /** | |
2910 | * Test the get_recent_courses function. | |
2911 | */ | |
2912 | public function test_get_recent_courses() { | |
2913 | global $USER, $DB; | |
2914 | ||
2915 | $this->resetAfterTest(); | |
2916 | $generator = $this->getDataGenerator(); | |
2917 | ||
2918 | set_config('hiddenuserfields', 'lastaccess'); | |
2919 | ||
2920 | $courses = array(); | |
2921 | for ($i = 1; $i < 12; $i++) { | |
2922 | $courses[] = $generator->create_course(); | |
2923 | }; | |
2924 | ||
2925 | $student = $generator->create_user(); | |
2926 | $teacher = $generator->create_user(); | |
2927 | ||
2928 | foreach ($courses as $course) { | |
2929 | $generator->enrol_user($student->id, $course->id, 'student'); | |
2930 | } | |
2931 | ||
2932 | $generator->enrol_user($teacher->id, $courses[0]->id, 'teacher'); | |
2933 | ||
2934 | $this->setUser($student); | |
2935 | ||
2936 | $result = core_course_external::get_recent_courses($USER->id); | |
2937 | ||
2938 | // No course accessed. | |
2939 | $this->assertCount(0, $result); | |
2940 | ||
2941 | foreach ($courses as $course) { | |
2942 | core_course_external::view_course($course->id); | |
2943 | } | |
2944 | ||
2945 | // Every course accessed. | |
2946 | $result = core_course_external::get_recent_courses($USER->id); | |
2947 | $this->assertCount( 11, $result); | |
2948 | ||
2949 | // Every course accessed, result limited to 10 courses. | |
2950 | $result = core_course_external::get_recent_courses($USER->id, 10); | |
2951 | $this->assertCount(10, $result); | |
2952 | ||
2953 | $guestcourse = $generator->create_course( | |
2954 | (object)array('shortname' => 'guestcourse', | |
2955 | 'enrol_guest_status_0' => ENROL_INSTANCE_ENABLED, | |
2956 | 'enrol_guest_password_0' => '')); | |
2957 | core_course_external::view_course($guestcourse->id); | |
2958 | ||
2959 | // Every course accessed, even the not enrolled one. | |
2960 | $result = core_course_external::get_recent_courses($USER->id); | |
2961 | $this->assertCount(12, $result); | |
2962 | ||
2963 | // Offset 5, return 7 out of 12. | |
2964 | $result = core_course_external::get_recent_courses($USER->id, 0, 5); | |
2965 | $this->assertCount(7, $result); | |
2966 | ||
2967 | // Offset 5 and limit 3, return 3 out of 12. | |
2968 | $result = core_course_external::get_recent_courses($USER->id, 3, 5); | |
2969 | $this->assertCount(3, $result); | |
2970 | ||
2971 | // Sorted by course id ASC. | |
2972 | $result = core_course_external::get_recent_courses($USER->id, 0, 0, 'id ASC'); | |
2973 | $this->assertEquals($courses[0]->id, array_shift($result)->id); | |
2974 | ||
2975 | // Sorted by course id DESC. | |
2976 | $result = core_course_external::get_recent_courses($USER->id, 0, 0, 'id DESC'); | |
2977 | $this->assertEquals($guestcourse->id, array_shift($result)->id); | |
2978 | ||
2979 | // If last access is hidden, only get the courses where has viewhiddenuserfields capability. | |
2980 | $this->setUser($teacher); | |
2981 | $teacherroleid = $DB->get_field('role', 'id', array('shortname' => 'editingteacher')); | |
2982 | $usercontext = context_user::instance($student->id); | |
2983 | $this->assignUserCapability('moodle/user:viewdetails', $usercontext, $teacherroleid); | |
2984 | ||
2985 | // Sorted by course id DESC. | |
2986 | $result = core_course_external::get_recent_courses($student->id); | |
2987 | $this->assertCount(1, $result); | |
2988 | $this->assertEquals($courses[0]->id, array_shift($result)->id); | |
2989 | } | |
06e50afd MM |
2990 | |
2991 | /** | |
2992 | * Test get enrolled users by cmid function. | |
2993 | */ | |
2994 | public function test_get_enrolled_users_by_cmid() { | |
9d752481 | 2995 | global $PAGE; |
06e50afd MM |
2996 | $this->resetAfterTest(true); |
2997 | ||
2998 | $user1 = self::getDataGenerator()->create_user(); | |
2999 | $user2 = self::getDataGenerator()->create_user(); | |
3000 | ||
9d752481 MM |
3001 | $user1picture = new user_picture($user1); |
3002 | $user1picture->size = 1; | |
3003 | $user1->profileimage = $user1picture->get_url($PAGE)->out(false); | |
3004 | ||
3005 | $user2picture = new user_picture($user2); | |
3006 | $user2picture->size = 1; | |
3007 | $user2->profileimage = $user2picture->get_url($PAGE)->out(false); | |
3008 | ||
06e50afd MM |
3009 | // Set the first created user to the test user. |
3010 | self::setUser($user1); | |
3011 | ||
3012 | // Create course to add the module. | |
3013 | $course1 = self::getDataGenerator()->create_course(); | |
3014 | ||
3015 | // Forum with tracking off. | |
3016 | $record = new stdClass(); | |
3017 | $record->course = $course1->id; | |
3018 | $forum1 = self::getDataGenerator()->create_module('forum', $record); | |
3019 | ||
3020 | // Following lines enrol and assign default role id to the users. | |
3021 | $this->getDataGenerator()->enrol_user($user1->id, $course1->id); | |
3022 | $this->getDataGenerator()->enrol_user($user2->id, $course1->id); | |
3023 | ||
3024 | // Create what we expect to be returned when querying the course module. | |
3025 | $expectedusers = array( | |
3026 | 'users' => array(), | |
3027 | 'warnings' => array(), | |
3028 | ); | |
3029 | ||
3030 | $expectedusers['users'][0] = [ | |
3031 | 'id' => $user1->id, | |
3032 | 'fullname' => fullname($user1), | |
3033 | 'firstname' => $user1->firstname, | |
3034 | 'lastname' => $user1->lastname, | |
9d752481 | 3035 | 'profileimage' => $user1->profileimage, |
06e50afd MM |
3036 | ]; |
3037 | $expectedusers['users'][1] = [ | |
3038 | 'id' => $user2->id, | |
3039 | 'fullname' => fullname($user2), | |
3040 | 'firstname' => $user2->firstname, | |
3041 | 'lastname' => $user2->lastname, | |
9d752481 | 3042 | 'profileimage' => $user2->profileimage, |
06e50afd MM |
3043 | ]; |
3044 | ||
3045 | // Test getting the users in a given context. | |
3046 | $users = core_course_external::get_enrolled_users_by_cmid($forum1->cmid); | |
3047 | $users = external_api::clean_returnvalue(core_course_external::get_enrolled_users_by_cmid_returns(), $users); | |
3048 | ||
3049 | $this->assertEquals(2, count($users['users'])); | |
3050 | $this->assertEquals($expectedusers, $users); | |
3051 | } | |
05b27f21 MM |
3052 | |
3053 | /** | |
3054 | * Test fetch_modules_activity_chooser | |
3055 | */ | |
3056 | public function test_fetch_modules_activity_chooser() { | |
3057 | global $OUTPUT; | |
3058 | ||
3059 | $this->resetAfterTest(true); | |
3060 | ||
3061 | // Log in as Admin. | |
3062 | $this->setAdminUser(); | |
3063 | ||
3064 | $course1 = self::getDataGenerator()->create_course(); | |
3065 | ||
3066 | // Fetch course modules. | |
3067 | $result = core_course_external::fetch_modules_activity_chooser($course1->id); | |
3068 | $result = external_api::clean_returnvalue(core_course_external::fetch_modules_activity_chooser_returns(), $result); | |
3069 | // Check for 0 warnings. | |
3070 | $this->assertEquals(0, count($result['warnings'])); | |
3071 | // Check we have the right number of standard modules. | |
3072 | $this->assertEquals(21, count($result['allmodules'])); | |
3073 | ||
3074 | $coursecontext = context_course::instance($course1->id); | |
3075 | $modnames = get_module_types_names(); | |
3076 | $modules = get_module_metadata($course1, $modnames, null); | |
3077 | $related = [ | |
3078 | 'context' => $coursecontext | |
3079 | ]; | |
3080 | // Export the module chooser data. | |
3081 | $modchooserdata = new \core_course\external\course_module_chooser_exporter($modules, $related); | |
3082 | $formatteddata = $modchooserdata->export($OUTPUT)->options; | |
3083 | ||
3084 | // Check if the webservice returns exactly what the exporter defines. | |
3085 | $this->assertEquals($formatteddata, $result['allmodules']); | |
3086 | } | |
76724712 | 3087 | } |