MDL-66135 tool_uploadcourse: case-insensitive date string comparison.
[moodle.git] / admin / tool / uploadcourse / tests / course_test.php
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/>.
17 /**
18  * File containing tests for the course class.
19  *
20  * @package    tool_uploadcourse
21  * @copyright  2013 Frédéric Massart
22  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23  */
25 defined('MOODLE_INTERNAL') || die();
27 global $CFG;
29 /**
30  * Course test case.
31  *
32  * @package    tool_uploadcourse
33  * @copyright  2013 Frédéric Massart
34  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or late
35  */
36 class tool_uploadcourse_course_testcase extends advanced_testcase {
38     /**
39      * @expectedException coding_exception
40      */
41     public function test_proceed_without_prepare() {
42         $this->resetAfterTest(true);
43         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
44         $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
45         $data = array();
46         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
47         $co->proceed();
48     }
50     /**
51      * @expectedException moodle_exception
52      */
53     public function test_proceed_when_prepare_failed() {
54         $this->resetAfterTest(true);
55         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
56         $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
57         $data = array();
58         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
59         $this->assertFalse($co->prepare());
60         $co->proceed();
61     }
63     public function test_proceed_when_already_started() {
64         $this->resetAfterTest(true);
65         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
66         $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
67         $data = array('shortname' => 'test', 'fullname' => 'New course', 'summary' => 'New', 'category' => 1);
68         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
69         $this->assertTrue($co->prepare());
70         $co->proceed();
71         $this->expectException('coding_exception');
72         $co->proceed();
73     }
75     public function test_invalid_shortname() {
76         $this->resetAfterTest(true);
77         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
78         $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
79         $data = array('shortname' => '<invalid>', 'fullname' => 'New course', 'summary' => 'New', 'category' => 1);
80         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
81         $this->assertFalse($co->prepare());
82         $this->assertArrayHasKey('invalidshortname', $co->get_errors());
83     }
85     public function test_invalid_shortname_too_long() {
86         $this->resetAfterTest();
88         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
89         $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
91         $upload = new tool_uploadcourse_course($mode, $updatemode, [
92             'category' => 1,
93             'fullname' => 'New course',
94             'shortname' => str_repeat('X', 2000),
95         ]);
97         $this->assertFalse($upload->prepare());
98         $this->assertArrayHasKey('invalidshortnametoolong', $upload->get_errors());
99     }
101     public function test_invalid_fullname_too_long() {
102         $this->resetAfterTest();
104         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
105         $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
107         $upload = new tool_uploadcourse_course($mode, $updatemode, [
108             'category' => 1,
109             'fullname' => str_repeat('X', 2000),
110         ]);
112         $this->assertFalse($upload->prepare());
113         $this->assertArrayHasKey('invalidfullnametoolong', $upload->get_errors());
114     }
116     public function test_invalid_visibility() {
117         $this->resetAfterTest(true);
118         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
119         $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
120         $data = array('shortname' => 'test', 'fullname' => 'New course', 'summary' => 'New', 'category' => 1, 'visible' => 2);
121         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
122         $this->assertFalse($co->prepare());
123         $this->assertArrayHasKey('invalidvisibilitymode', $co->get_errors());
124     }
126     public function test_create() {
127         global $DB;
128         $this->resetAfterTest(true);
130         // Existing course.
131         $c1 = $this->getDataGenerator()->create_course(array('shortname' => 'c1', 'summary' => 'Yay!'));
132         $this->assertTrue($DB->record_exists('course', array('shortname' => 'c1')));
134         $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
136         // Try to add a new course.
137         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
138         $data = array('shortname' => 'newcourse', 'fullname' => 'New course', 'summary' => 'New', 'category' => 1);
139         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
140         $this->assertTrue($co->prepare());
141         $this->assertFalse($DB->record_exists('course', array('shortname' => 'newcourse')));
142         $co->proceed();
143         $course = $DB->get_record('course', array('shortname' => 'newcourse'), '*', MUST_EXIST);
144         $this->assertEquals(0, course_get_format($course)->get_course()->coursedisplay);
146         // Try to add a new course, that already exists.
147         $coursecount = $DB->count_records('course', array());
148         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
149         $data = array('shortname' => 'c1', 'fullname' => 'C1FN', 'summary' => 'C1', 'category' => 1);
150         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
151         $this->assertFalse($co->prepare());
152         $this->assertArrayHasKey('courseexistsanduploadnotallowed', $co->get_errors());
153         $this->assertEquals($coursecount, $DB->count_records('course', array()));
154         $this->assertNotEquals('C1', $DB->get_field_select('course', 'summary', 'shortname = :s', array('s' => 'c1')));
156         // Try to add new with shortname incrementation.
157         $mode = tool_uploadcourse_processor::MODE_CREATE_ALL;
158         $data = array('shortname' => 'c1', 'fullname' => 'C1FN', 'summary' => 'C1', 'category' => 1);
159         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
160         $this->assertTrue($co->prepare());
161         $co->proceed();
162         $this->assertTrue($DB->record_exists('course', array('shortname' => 'c2')));
164         // Add a new course with non-default course format option.
165         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
166         $data = array('shortname' => 'c3', 'fullname' => 'C3', 'summary' => 'New c3', 'category' => 1,
167             'format' => 'weeks', 'coursedisplay' => 1);
168         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
169         $this->assertTrue($co->prepare());
170         $co->proceed();
171         $course = $DB->get_record('course', array('shortname' => 'c3'), '*', MUST_EXIST);
172         $this->assertEquals(1, course_get_format($course)->get_course()->coursedisplay);
173     }
175     public function test_create_with_sections() {
176         global $DB;
177         $this->resetAfterTest(true);
178         $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
179         $defaultnumsections = get_config('moodlecourse', 'numsections');
181         // Add new course, make sure default number of sections is created.
182         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
183         $data = array('shortname' => 'newcourse1', 'fullname' => 'New course1', 'format' => 'topics', 'category' => 1);
184         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
185         $this->assertTrue($co->prepare());
186         $co->proceed();
187         $courseid = $DB->get_field('course', 'id', array('shortname' => 'newcourse1'));
188         $this->assertNotEmpty($courseid);
189         $this->assertEquals($defaultnumsections + 1,
190             $DB->count_records('course_sections', ['course' => $courseid]));
192         // Add new course specifying number of sections.
193         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
194         $data = array('shortname' => 'newcourse2', 'fullname' => 'New course2', 'format' => 'topics', 'category' => 1,
195             'numsections' => 15);
196         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
197         $this->assertTrue($co->prepare());
198         $co->proceed();
199         $courseid = $DB->get_field('course', 'id', array('shortname' => 'newcourse2'));
200         $this->assertNotEmpty($courseid);
201         $this->assertEquals(15 + 1,
202             $DB->count_records('course_sections', ['course' => $courseid]));
203     }
205     public function test_delete() {
206         global $DB;
207         $this->resetAfterTest(true);
209         $c1 = $this->getDataGenerator()->create_course();
210         $c2 = $this->getDataGenerator()->create_course();
212         $this->assertTrue($DB->record_exists('course', array('shortname' => $c1->shortname)));
213         $this->assertFalse($DB->record_exists('course', array('shortname' => 'DoesNotExist')));
215         $mode = tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE;
216         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
218         // Try delete when option not available.
219         $importoptions = array('candelete' => false);
220         $data = array('shortname' => $c1->shortname, 'delete' => 1);
221         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
222         $this->assertFalse($co->prepare());
223         $this->assertArrayHasKey('coursedeletionnotallowed', $co->get_errors());
224         $this->assertTrue($DB->record_exists('course', array('shortname' => $c1->shortname)));
226         // Try delete when not requested.
227         $importoptions = array('candelete' => true);
228         $data = array('shortname' => $c1->shortname, 'delete' => 0);
229         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
230         $this->assertTrue($co->prepare());
231         $co->proceed();
232         $this->assertTrue($DB->record_exists('course', array('shortname' => $c1->shortname)));
234         // Try delete when requested.
235         $importoptions = array('candelete' => true);
236         $data = array('shortname' => $c1->shortname, 'delete' => 1);
237         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
238         $this->assertTrue($co->prepare());
239         $co->proceed();
240         $this->assertFalse($DB->record_exists('course', array('shortname' => $c1->shortname)));
241         $this->assertTrue($DB->record_exists('course', array('shortname' => $c2->shortname)));
243         // Try deleting non-existing record, this should not fail.
244         $data = array('shortname' => 'DoesNotExist', 'delete' => 1);
245         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
246         $this->assertFalse($co->prepare());
247         $this->assertArrayHasKey('cannotdeletecoursenotexist', $co->get_errors());
248     }
250     public function test_update() {
251         global $DB;
252         $this->resetAfterTest(true);
254         $c1 = $this->getDataGenerator()->create_course(array('shortname' => 'c1'));
256         // Try to update with existing shortnames, not allowing creation, and updating nothing.
257         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
258         $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
259         $data = array('shortname' => 'c1', 'fullname' => 'New fullname');
260         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
261         $this->assertFalse($co->prepare());
262         $this->assertArrayHasKey('updatemodedoessettonothing', $co->get_errors());
264         // Try to update with non-existing shortnames.
265         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
266         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
267         $data = array('shortname' => 'DoesNotExist', 'fullname' => 'New fullname');
268         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
269         $this->assertFalse($co->prepare());
270         $this->assertArrayHasKey('coursedoesnotexistandcreatenotallowed', $co->get_errors());
272         // Try a proper update.
273         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
274         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
275         $data = array('shortname' => 'c1', 'fullname' => 'New fullname');
276         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
277         $this->assertTrue($co->prepare());
278         $co->proceed();
279         $this->assertEquals('New fullname', $DB->get_field_select('course', 'fullname', 'shortname = :s', array('s' => 'c1')));
281         // Try a proper update with defaults.
282         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
283         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_OR_DEFAUTLS;
284         $data = array('shortname' => 'c1', 'fullname' => 'Another fullname');
285         $defaults = array('fullname' => 'Not this one', 'summary' => 'Awesome summary');
286         $co = new tool_uploadcourse_course($mode, $updatemode, $data, $defaults);
287         $this->assertTrue($co->prepare());
288         $co->proceed();
289         $this->assertEquals('Another fullname', $DB->get_field_select('course', 'fullname', 'shortname = :s', array('s' => 'c1')));
290         $this->assertEquals('Awesome summary', $DB->get_field_select('course', 'summary', 'shortname = :s', array('s' => 'c1')));
292         // Try a proper update missing only.
293         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
294         $updatemode = tool_uploadcourse_processor::UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS;
295         $DB->set_field('course', 'summary', '', array('shortname' => 'c1'));
296         $this->assertEquals('', $DB->get_field_select('course', 'summary', 'shortname = :s', array('s' => 'c1')));
297         $data = array('shortname' => 'c1', 'summary' => 'Fill in summary');
298         $defaults = array('summary' => 'Do not use this summary');
299         $co = new tool_uploadcourse_course($mode, $updatemode, $data, $defaults);
300         $this->assertTrue($co->prepare());
301         $co->proceed();
302         $this->assertEquals('Fill in summary', $DB->get_field_select('course', 'summary', 'shortname = :s', array('s' => 'c1')));
304         // Try a proper update missing only using defaults.
305         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
306         $updatemode = tool_uploadcourse_processor::UPDATE_MISSING_WITH_DATA_OR_DEFAUTLS;
307         $DB->set_field('course', 'summary', '', array('shortname' => 'c1'));
308         $this->assertEquals('', $DB->get_field_select('course', 'summary', 'shortname = :s', array('s' => 'c1')));
309         $data = array('shortname' => 'c1');
310         $defaults = array('summary' => 'Use this summary');
311         $co = new tool_uploadcourse_course($mode, $updatemode, $data, $defaults);
312         $this->assertTrue($co->prepare());
313         $co->proceed();
314         $this->assertEquals('Use this summary', $DB->get_field_select('course', 'summary', 'shortname = :s', array('s' => 'c1')));
316         // Update course format option.
317         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
318         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
319         $data = array('shortname' => 'c1', 'coursedisplay' => 1);
320         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
321         $this->assertTrue($co->prepare());
322         $co->proceed();
323         $course = $DB->get_record('course', array('shortname' => 'c1'), '*', MUST_EXIST);
324         $this->assertEquals(1, course_get_format($course)->get_course()->coursedisplay);
325     }
327     public function test_data_saved() {
328         global $DB;
329         $this->resetAfterTest(true);
331         $this->setAdminUser(); // To avoid warnings related to 'moodle/course:setforcedlanguage' capability check.
333         // Create.
334         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
335         $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
336         $data = array(
337             'shortname' => 'c1',
338             'fullname' => 'Fullname',
339             'category' => '1',
340             'visible' => '0',
341             'idnumber' => '123abc',
342             'summary' => 'Summary',
343             'format' => 'topics',
344             'theme' => 'afterburner',
345             'lang' => 'en',
346             'newsitems' => '7',
347             'showgrades' => '0',
348             'showreports' => '1',
349             'legacyfiles' => '1',
350             'maxbytes' => '1234',
351             'groupmode' => '2',
352             'groupmodeforce' => '1',
353             'enablecompletion' => '1',
354             'tags' => 'Cat, Dog',
356             'role_teacher' => 'Knight',
357             'role_manager' => 'Jedi',
359             'enrolment_1' => 'guest',
360             'enrolment_2' => 'self',
361             'enrolment_2_roleid' => '1',
362             'enrolment_3' => 'manual',
363             'enrolment_3_disable' => '1',
364         );
366         // There should be a start date if there is a end date.
367         $data['enddate'] = '7 June 1990';
368         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
369         $this->assertFalse($co->prepare());
370         $this->assertArrayHasKey('nostartdatenoenddate', $co->get_errors());
372         $data['startdate'] = '8 June 1990';
373         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
374         $this->assertFalse($co->prepare());
375         $this->assertArrayHasKey('enddatebeforestartdate', $co->get_errors());
377         // They are correct now.
378         $data['enddate'] = '18 June 1990';
380         $this->assertFalse($DB->record_exists('course', array('shortname' => 'c1')));
381         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
382         $this->assertTrue($co->prepare());
383         $co->proceed();
384         $this->assertTrue($DB->record_exists('course', array('shortname' => 'c1')));
385         $course = $DB->get_record('course', array('shortname' => 'c1'));
386         $ctx = context_course::instance($course->id);
388         $this->assertEquals($data['fullname'], $course->fullname);
389         $this->assertEquals($data['category'], $course->category);
390         $this->assertEquals($data['visible'], $course->visible);
391         $this->assertEquals(mktime(0, 0, 0, 6, 8, 1990), $course->startdate);
392         $this->assertEquals(mktime(0, 0, 0, 6, 18, 1990), $course->enddate);
393         $this->assertEquals($data['idnumber'], $course->idnumber);
394         $this->assertEquals($data['summary'], $course->summary);
395         $this->assertEquals($data['format'], $course->format);
396         $this->assertEquals($data['theme'], $course->theme);
397         $this->assertEquals($data['lang'], $course->lang);
398         $this->assertEquals($data['newsitems'], $course->newsitems);
399         $this->assertEquals($data['showgrades'], $course->showgrades);
400         $this->assertEquals($data['showreports'], $course->showreports);
401         $this->assertEquals($data['legacyfiles'], $course->legacyfiles);
402         $this->assertEquals($data['maxbytes'], $course->maxbytes);
403         $this->assertEquals($data['groupmode'], $course->groupmode);
404         $this->assertEquals($data['groupmodeforce'], $course->groupmodeforce);
405         $this->assertEquals($data['enablecompletion'], $course->enablecompletion);
406         $this->assertEquals($data['tags'], join(', ', core_tag_tag::get_item_tags_array('core', 'course', $course->id)));
408         // Roles.
409         $roleids = array();
410         $roles = get_all_roles();
411         foreach ($roles as $role) {
412             $roleids[$role->shortname] = $role->id;
413         }
414         $this->assertEquals('Knight', $DB->get_field_select('role_names', 'name',
415             'roleid = :roleid AND contextid = :ctxid', array('ctxid' => $ctx->id, 'roleid' => $roleids['teacher'])));
416         $this->assertEquals('Jedi', $DB->get_field_select('role_names', 'name',
417             'roleid = :roleid AND contextid = :ctxid', array('ctxid' => $ctx->id, 'roleid' => $roleids['manager'])));
419         // Enrolment methods.
420         $enroldata = array();
421         $instances = enrol_get_instances($course->id, false);
422         $this->assertCount(3, $instances);
423         foreach ($instances as $instance) {
424             $enroldata[$instance->enrol] = $instance;
425         }
427         $this->assertNotEmpty($enroldata['guest']);
428         $this->assertEquals(ENROL_INSTANCE_ENABLED, $enroldata['guest']->status);
429         $this->assertNotEmpty($enroldata['self']);
430         $this->assertEquals($data['enrolment_2_roleid'], $enroldata['self']->roleid);
431         $this->assertEquals(ENROL_INSTANCE_ENABLED, $enroldata['self']->status);
432         $this->assertNotEmpty($enroldata['manual']);
433         $this->assertEquals(ENROL_INSTANCE_DISABLED, $enroldata['manual']->status);
435         // Update existing course.
436         $cat = $this->getDataGenerator()->create_category();
437         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
438         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
439         $data = array(
440             'shortname' => 'c1',
441             'fullname' => 'Fullname 2',
442             'category' => $cat->id,
443             'visible' => '1',
444             'idnumber' => 'changeidn',
445             'summary' => 'Summary 2',
446             'format' => 'topics',
447             'theme' => 'classic',
448             'lang' => '',
449             'newsitems' => '2',
450             'showgrades' => '1',
451             'showreports' => '0',
452             'legacyfiles' => '0',
453             'maxbytes' => '4321',
454             'groupmode' => '1',
455             'groupmodeforce' => '0',
456             'enablecompletion' => '0',
458             'role_teacher' => 'Teacher',
459             'role_manager' => 'Manager',
461             'enrolment_1' => 'guest',
462             'enrolment_1_disable' => '1',
463             'enrolment_2' => 'self',
464             'enrolment_2_roleid' => '2',
465             'enrolment_3' => 'manual',
466             'enrolment_3_delete' => '1',
467         );
469         $this->assertTrue($DB->record_exists('course', array('shortname' => 'c1')));
471         $data['enddate'] = '31 June 1984';
472         // Previous start and end dates are 8 and 18 June 1990.
473         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
474         $this->assertFalse($co->prepare());
475         $this->assertArrayHasKey('enddatebeforestartdate', $co->get_errors());
477         $data['startdate'] = '19 June 1990';
478         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
479         $this->assertFalse($co->prepare());
480         $this->assertArrayHasKey('enddatebeforestartdate', $co->get_errors());
482         // They are correct now.
483         $data['startdate'] = '11 June 1984';
485         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
486         $this->assertTrue($co->prepare());
487         $co->proceed();
488         $course = $DB->get_record('course', array('shortname' => 'c1'));
489         $ctx = context_course::instance($course->id);
491         $this->assertEquals($data['fullname'], $course->fullname);
492         $this->assertEquals($data['category'], $course->category);
493         $this->assertEquals($data['visible'], $course->visible);
494         $this->assertEquals(mktime(0, 0, 0, 6, 11, 1984), $course->startdate);
495         $this->assertEquals(mktime(0, 0, 0, 6, 31, 1984), $course->enddate);
496         $this->assertEquals($data['idnumber'], $course->idnumber);
497         $this->assertEquals($data['summary'], $course->summary);
498         $this->assertEquals($data['format'], $course->format);
499         $this->assertEquals($data['theme'], $course->theme);
500         $this->assertEquals($data['lang'], $course->lang);
501         $this->assertEquals($data['newsitems'], $course->newsitems);
502         $this->assertEquals($data['showgrades'], $course->showgrades);
503         $this->assertEquals($data['showreports'], $course->showreports);
504         $this->assertEquals($data['legacyfiles'], $course->legacyfiles);
505         $this->assertEquals($data['maxbytes'], $course->maxbytes);
506         $this->assertEquals($data['groupmode'], $course->groupmode);
507         $this->assertEquals($data['groupmodeforce'], $course->groupmodeforce);
508         $this->assertEquals($data['enablecompletion'], $course->enablecompletion);
510         // Roles.
511         $roleids = array();
512         $roles = get_all_roles();
513         foreach ($roles as $role) {
514             $roleids[$role->shortname] = $role->id;
515         }
516         $this->assertEquals('Teacher', $DB->get_field_select('role_names', 'name',
517             'roleid = :roleid AND contextid = :ctxid', array('ctxid' => $ctx->id, 'roleid' => $roleids['teacher'])));
518         $this->assertEquals('Manager', $DB->get_field_select('role_names', 'name',
519             'roleid = :roleid AND contextid = :ctxid', array('ctxid' => $ctx->id, 'roleid' => $roleids['manager'])));
521         // Enrolment methods.
522         $enroldata = array();
523         $instances = enrol_get_instances($course->id, false);
524         $this->assertCount(2, $instances);
525         foreach ($instances as $instance) {
526             $enroldata[$instance->enrol] = $instance;
527         }
529         $this->assertNotEmpty($enroldata['guest']);
530         $this->assertEquals(ENROL_INSTANCE_DISABLED, $enroldata['guest']->status);
531         $this->assertNotEmpty($enroldata['self']);
532         $this->assertEquals($data['enrolment_2_roleid'], $enroldata['self']->roleid);
533         $this->assertEquals(ENROL_INSTANCE_ENABLED, $enroldata['self']->status);
534     }
536     public function test_default_data_saved() {
537         global $DB;
538         $this->resetAfterTest(true);
540         // Create.
541         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
542         $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
543         $data = array(
544             'shortname' => 'c1',
545         );
546         $defaultdata = array(
547             'fullname' => 'Fullname',
548             'category' => '1',
549             'visible' => '0',
550             'startdate' => 644803200,
551             'enddate' => 645667200,
552             'idnumber' => '123abc',
553             'summary' => 'Summary',
554             'format' => 'topics',
555             'theme' => 'afterburner',
556             'lang' => 'en',
557             'newsitems' => '7',
558             'showgrades' => '0',
559             'showreports' => '1',
560             'legacyfiles' => '1',
561             'maxbytes' => '1234',
562             'groupmode' => '2',
563             'groupmodeforce' => '1',
564             'enablecompletion' => '1',
565         );
567         $this->assertFalse($DB->record_exists('course', array('shortname' => 'c1')));
568         $co = new tool_uploadcourse_course($mode, $updatemode, $data, $defaultdata);
569         $this->assertTrue($co->prepare());
570         $co->proceed();
571         $this->assertTrue($DB->record_exists('course', array('shortname' => 'c1')));
572         $course = $DB->get_record('course', array('shortname' => 'c1'));
573         $ctx = context_course::instance($course->id);
575         $this->assertEquals($defaultdata['fullname'], $course->fullname);
576         $this->assertEquals($defaultdata['category'], $course->category);
577         $this->assertEquals($defaultdata['visible'], $course->visible);
578         $this->assertEquals($defaultdata['startdate'], $course->startdate);
579         $this->assertEquals($defaultdata['enddate'], $course->enddate);
580         $this->assertEquals($defaultdata['idnumber'], $course->idnumber);
581         $this->assertEquals($defaultdata['summary'], $course->summary);
582         $this->assertEquals($defaultdata['format'], $course->format);
583         $this->assertEquals($defaultdata['theme'], $course->theme);
584         $this->assertEquals($defaultdata['lang'], $course->lang);
585         $this->assertEquals($defaultdata['newsitems'], $course->newsitems);
586         $this->assertEquals($defaultdata['showgrades'], $course->showgrades);
587         $this->assertEquals($defaultdata['showreports'], $course->showreports);
588         $this->assertEquals($defaultdata['legacyfiles'], $course->legacyfiles);
589         $this->assertEquals($defaultdata['maxbytes'], $course->maxbytes);
590         $this->assertEquals($defaultdata['groupmode'], $course->groupmode);
591         $this->assertEquals($defaultdata['groupmodeforce'], $course->groupmodeforce);
592         $this->assertEquals($defaultdata['enablecompletion'], $course->enablecompletion);
594         // Update.
595         $cat = $this->getDataGenerator()->create_category();
596         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
597         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_OR_DEFAUTLS;
598         $data = array(
599             'shortname' => 'c1',
600         );
601         $defaultdata = array(
602             'fullname' => 'Fullname 2',
603             'category' => $cat->id,
604             'visible' => '1',
605             'startdate' => 455760000,
606             'enddate' => 457488000,
607             'idnumber' => 'changedid',
608             'summary' => 'Summary 2',
609             'format' => 'topics',
610             'theme' => 'classic',
611             'lang' => '',
612             'newsitems' => '2',
613             'showgrades' => '1',
614             'showreports' => '0',
615             'legacyfiles' => '0',
616             'maxbytes' => '1111',
617             'groupmode' => '1',
618             'groupmodeforce' => '0',
619             'enablecompletion' => '0',
620         );
622         $this->assertTrue($DB->record_exists('course', array('shortname' => 'c1')));
623         $co = new tool_uploadcourse_course($mode, $updatemode, $data, $defaultdata);
624         $this->assertTrue($co->prepare());
625         $co->proceed();
626         $this->assertTrue($DB->record_exists('course', array('shortname' => 'c1')));
627         $course = $DB->get_record('course', array('shortname' => 'c1'));
628         $ctx = context_course::instance($course->id);
630         $this->assertEquals($defaultdata['fullname'], $course->fullname);
631         $this->assertEquals($defaultdata['category'], $course->category);
632         $this->assertEquals($defaultdata['visible'], $course->visible);
633         $this->assertEquals($defaultdata['startdate'], $course->startdate);
634         $this->assertEquals($defaultdata['enddate'], $course->enddate);
635         $this->assertEquals($defaultdata['idnumber'], $course->idnumber);
636         $this->assertEquals($defaultdata['summary'], $course->summary);
637         $this->assertEquals($defaultdata['format'], $course->format);
638         $this->assertEquals($defaultdata['theme'], $course->theme);
639         $this->assertEquals($defaultdata['lang'], $course->lang);
640         $this->assertEquals($defaultdata['newsitems'], $course->newsitems);
641         $this->assertEquals($defaultdata['showgrades'], $course->showgrades);
642         $this->assertEquals($defaultdata['showreports'], $course->showreports);
643         $this->assertEquals($defaultdata['legacyfiles'], $course->legacyfiles);
644         $this->assertEquals($defaultdata['maxbytes'], $course->maxbytes);
645         $this->assertEquals($defaultdata['groupmode'], $course->groupmode);
646         $this->assertEquals($defaultdata['groupmodeforce'], $course->groupmodeforce);
647         $this->assertEquals($defaultdata['enablecompletion'], $course->enablecompletion);
648     }
650     public function test_rename() {
651         global $DB;
652         $this->resetAfterTest(true);
654         $c1 = $this->getDataGenerator()->create_course(array('shortname' => 'c1'));
655         $c2 = $this->getDataGenerator()->create_course(array('shortname' => 'c2'));
657         // Cannot rename when creating.
658         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
659         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
660         $importoptions = array('canrename' => true);
661         $data = array('shortname' => 'c1', 'rename' => 'newshortname');
662         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
663         $this->assertFalse($co->prepare());
664         $this->assertArrayHasKey('courseexistsanduploadnotallowed', $co->get_errors());
666         // Cannot rename when creating.
667         $mode = tool_uploadcourse_processor::MODE_CREATE_ALL;
668         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
669         $importoptions = array('canrename' => true);
670         $data = array('shortname' => 'c1', 'rename' => 'newshortname', 'category' => 1, 'summary' => 'S', 'fullname' => 'F');
671         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
672         $this->assertFalse($co->prepare());
673         $this->assertArrayHasKey('canonlyrenameinupdatemode', $co->get_errors());
675         // Error when not allowed to rename the course.
676         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
677         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
678         $importoptions = array('canrename' => false);
679         $data = array('shortname' => 'c1', 'rename' => 'newshortname');
680         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
681         $this->assertFalse($co->prepare());
682         $this->assertArrayHasKey('courserenamingnotallowed', $co->get_errors());
684         // Can rename when updating.
685         $mode = tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE;
686         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
687         $importoptions = array('canrename' => true);
688         $data = array('shortname' => 'c1', 'rename' => 'newshortname');
689         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
690         $this->assertTrue($co->prepare());
691         $co->proceed();
692         $this->assertEquals('newshortname', $DB->get_field_select('course', 'shortname', 'id = :id', array('id' => $c1->id)));
694         // Can rename when updating.
695         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
696         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
697         $importoptions = array('canrename' => true);
698         $data = array('shortname' => 'newshortname', 'rename' => 'newshortname2');
699         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
700         $this->assertTrue($co->prepare());
701         $co->proceed();
702         $this->assertEquals('newshortname2', $DB->get_field_select('course', 'shortname', 'id = :id', array('id' => $c1->id)));
704         // Error when course does not exist.
705         $mode = tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE;
706         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
707         $importoptions = array('canrename' => true);
708         $data = array('shortname' => 'DoesNotExist', 'rename' => 'c1', 'category' => 1, 'summary' => 'S', 'fullname' => 'F');
709         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
710         $this->assertFalse($co->prepare());
711         $this->assertArrayHasKey('cannotrenamecoursenotexist', $co->get_errors());
713         // Renaming still updates the other values.
714         $mode = tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE;
715         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_OR_DEFAUTLS;
716         $importoptions = array('canrename' => true);
717         $data = array('shortname' => 'newshortname2', 'rename' => 'c1', 'fullname' => 'Another fullname!');
718         $defaultdata = array('summary' => 'New summary!');
719         $co = new tool_uploadcourse_course($mode, $updatemode, $data, $defaultdata, $importoptions);
720         $this->assertTrue($co->prepare());
721         $co->proceed();
722         $this->assertEquals('c1', $DB->get_field_select('course', 'shortname', 'id = :id', array('id' => $c1->id)));
723         $this->assertEquals('New summary!', $DB->get_field_select('course', 'summary', 'id = :id', array('id' => $c1->id)));
724         $this->assertEquals('Another fullname!', $DB->get_field_select('course', 'fullname', 'id = :id', array('id' => $c1->id)));
726         // Renaming with invalid shortname.
727         $mode = tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE;
728         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
729         $importoptions = array('canrename' => true);
730         $data = array('shortname' => 'c1', 'rename' => '<span>invalid</span>');
731         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
732         $this->assertFalse($co->prepare());
733         $this->assertArrayHasKey('invalidshortname', $co->get_errors());
735         // Renaming with invalid shortname.
736         $mode = tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE;
737         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
738         $importoptions = array('canrename' => true);
739         $data = array('shortname' => 'c1', 'rename' => 'c2');
740         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
741         $this->assertFalse($co->prepare());
742         $this->assertArrayHasKey('cannotrenameshortnamealreadyinuse', $co->get_errors());
743     }
745     public function test_restore_course() {
746         global $DB;
747         $this->resetAfterTest(true);
748         $this->setAdminUser();
750         $c1 = $this->getDataGenerator()->create_course();
751         $c1f1 = $this->getDataGenerator()->create_module('forum', array('course' => $c1->id));
753         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
754         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
755         $data = array('shortname' => 'A1', 'templatecourse' => $c1->shortname, 'summary' => 'A', 'category' => 1,
756             'fullname' => 'A1');
757         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
758         $this->assertTrue($co->prepare());
759         $co->proceed();
760         $course = $DB->get_record('course', array('shortname' => 'A1'));
761         $modinfo = get_fast_modinfo($course);
762         $found = false;
763         foreach ($modinfo->get_cms() as $cmid => $cm) {
764             if ($cm->modname == 'forum' && $cm->name == $c1f1->name) {
765                 $found = true;
766                 break;
767             }
768         }
769         $this->assertTrue($found);
771         // Restoring twice from the same course should work.
772         $data = array('shortname' => 'B1', 'templatecourse' => $c1->shortname, 'summary' => 'B', 'category' => 1,
773             'fullname' => 'B1');
774         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
775         $this->assertTrue($co->prepare());
776         $co->proceed();
777         $course = $DB->get_record('course', array('shortname' => 'B1'));
778         $modinfo = get_fast_modinfo($course);
779         $found = false;
780         foreach ($modinfo->get_cms() as $cmid => $cm) {
781             if ($cm->modname == 'forum' && $cm->name == $c1f1->name) {
782                 $found = true;
783                 break;
784             }
785         }
786         $this->assertTrue($found);
787     }
789     public function test_restore_file() {
790         global $DB;
791         $this->resetAfterTest(true);
792         $this->setAdminUser();
794         $c1 = $this->getDataGenerator()->create_course();
795         $c1f1 = $this->getDataGenerator()->create_module('forum', array('course' => $c1->id));
797         // Restore from a file, checking that the file takes priority over the templatecourse.
798         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
799         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
800         $data = array('shortname' => 'A1', 'backupfile' => __DIR__ . '/fixtures/backup.mbz',
801             'summary' => 'A', 'category' => 1, 'fullname' => 'A1', 'templatecourse' => $c1->shortname);
802         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
803         $this->assertTrue($co->prepare());
804         $co->proceed();
805         $course = $DB->get_record('course', array('shortname' => 'A1'));
806         $modinfo = get_fast_modinfo($course);
807         $found = false;
808         foreach ($modinfo->get_cms() as $cmid => $cm) {
809             if ($cm->modname == 'glossary' && $cm->name == 'Imported Glossary') {
810                 $found = true;
811             } else if ($cm->modname == 'forum' && $cm->name == $c1f1->name) {
812                 // We should not find this!
813                 $this->assertTrue(false);
814             }
815         }
816         $this->assertTrue($found);
818         // Restoring twice from the same file should work.
819         $data = array('shortname' => 'B1', 'backupfile' => __DIR__ . '/fixtures/backup.mbz',
820             'summary' => 'B', 'category' => 1, 'fullname' => 'B1');
821         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
822         $this->assertTrue($co->prepare());
823         $co->proceed();
824         $course = $DB->get_record('course', array('shortname' => 'B1'));
825         $modinfo = get_fast_modinfo($course);
826         $found = false;
827         foreach ($modinfo->get_cms() as $cmid => $cm) {
828             if ($cm->modname == 'glossary' && $cm->name == 'Imported Glossary') {
829                 $found = true;
830             } else if ($cm->modname == 'forum' && $cm->name == $c1f1->name) {
831                 // We should not find this!
832                 $this->assertTrue(false);
833             }
834         }
835         $this->assertTrue($found);
836     }
838     /**
839      * Test that specifying course template respects default restore settings
840      */
841     public function test_restore_file_settings() {
842         global $DB;
843         $this->resetAfterTest(true);
844         $this->setAdminUser();
846         // Set admin config setting so that activities are not restored by default.
847         set_config('restore_general_activities', 0, 'restore');
849         $c1 = $this->getDataGenerator()->create_course();
851         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
852         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
853         $data = array('shortname' => 'A1', 'backupfile' => __DIR__ . '/fixtures/backup.mbz',
854             'summary' => 'A', 'category' => 1, 'fullname' => 'A1', 'templatecourse' => $c1->shortname);
855         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
856         $this->assertTrue($co->prepare());
857         $co->proceed();
858         $course = $DB->get_record('course', array('shortname' => 'A1'));
860         // Make sure the glossary is not restored.
861         $modinfo = get_fast_modinfo($course);
862         $this->assertEmpty($modinfo->get_instances_of('glossary'));
863     }
865     public function test_restore_invalid_file() {
866         $this->resetAfterTest();
868         // Restore from a non-existing file should not be allowed.
869         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
870         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
871         $data = array('shortname' => 'A1', 'backupfile' => '/lead/no/where',
872             'category' => 1, 'fullname' => 'A1');
873         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
874         $this->assertFalse($co->prepare());
875         $this->assertArrayHasKey('cannotreadbackupfile', $co->get_errors());
877         // Restore from an invalid file should not be allowed.
878         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
879         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
880         $data = array('shortname' => 'A1', 'backupfile' => __FILE__,
881             'category' => 1, 'fullname' => 'A1');
882         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
884         $this->assertFalse($co->prepare());
885         $this->assertArrayHasKey('invalidbackupfile', $co->get_errors());
887         // Zip packer throws a debugging message, this assertion is only here to prevent
888         // the message from being displayed.
889         $this->assertDebuggingCalled();
890     }
892     public function test_restore_invalid_course() {
893         $this->resetAfterTest();
895         // Restore from an invalid file should not be allowed.
896         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
897         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
898         $data = array('shortname' => 'A1', 'templatecourse' => 'iamnotavalidcourse',
899             'category' => 1, 'fullname' => 'A1');
900         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
901         $this->assertFalse($co->prepare());
902         $this->assertArrayHasKey('coursetorestorefromdoesnotexist', $co->get_errors());
903     }
905     /**
906      * Testing the reset on groups, group members and enrolments.
907      */
908     public function test_reset() {
909         global $DB;
910         $this->resetAfterTest(true);
912         $c1 = $this->getDataGenerator()->create_course();
913         $c1ctx = context_course::instance($c1->id);
914         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
915         $teacherrole = $DB->get_record('role', array('shortname' => 'teacher'));
917         $u1 = $this->getDataGenerator()->create_user();
918         $this->getDataGenerator()->enrol_user($u1->id, $c1->id, $studentrole->id);
919         $this->assertCount(1, get_enrolled_users($c1ctx));
921         $g1 = $this->getDataGenerator()->create_group(array('courseid' => $c1->id));
922         $this->getDataGenerator()->create_group_member(array('groupid' => $g1->id, 'userid' => $u1->id));
923         $this->assertEquals(1, $DB->count_records('groups', array('courseid' => $c1->id)));
924         $this->assertTrue($DB->record_exists('groups_members', array('groupid' => $g1->id, 'userid' => $u1->id)));
926         // Wrong mode.
927         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
928         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
929         $data = array('shortname' => 'DoesNotExist', 'reset' => '1', 'summary' => 'summary', 'fullname' => 'FN', 'category' => 1);
930         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
931         $this->assertFalse($co->prepare());
932         $this->assertArrayHasKey('canonlyresetcourseinupdatemode', $co->get_errors());
933         $this->assertTrue($DB->record_exists('groups', array('id' => $g1->id)));
934         $this->assertTrue($DB->record_exists('groups_members', array('groupid' => $g1->id, 'userid' => $u1->id)));
935         $this->assertCount(1, get_enrolled_users($c1ctx));
937         // Reset not allowed.
938         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
939         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
940         $data = array('shortname' => $c1->shortname, 'reset' => '1');
941         $importoptions = array('canreset' => false);
942         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
943         $this->assertFalse($co->prepare());
944         $this->assertArrayHasKey('courseresetnotallowed', $co->get_errors());
945         $this->assertTrue($DB->record_exists('groups', array('id' => $g1->id)));
946         $this->assertTrue($DB->record_exists('groups_members', array('groupid' => $g1->id, 'userid' => $u1->id)));
947         $this->assertCount(1, get_enrolled_users($c1ctx));
949         // Reset allowed but not requested.
950         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
951         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
952         $data = array('shortname' => $c1->shortname, 'reset' => '0');
953         $importoptions = array('canreset' => true);
954         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
955         $this->assertTrue($co->prepare());
956         $co->proceed();
957         $this->assertTrue($DB->record_exists('groups', array('id' => $g1->id)));
958         $this->assertTrue($DB->record_exists('groups_members', array('groupid' => $g1->id, 'userid' => $u1->id)));
959         $this->assertCount(1, get_enrolled_users($c1ctx));
961         // Reset passed as a default parameter, should not be taken in account.
962         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
963         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
964         $data = array('shortname' => $c1->shortname);
965         $importoptions = array('canreset' => true);
966         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array('reset' => 1), $importoptions);
967         $this->assertTrue($co->prepare());
968         $co->proceed();
969         $this->assertTrue($DB->record_exists('groups', array('id' => $g1->id)));
970         $this->assertTrue($DB->record_exists('groups_members', array('groupid' => $g1->id, 'userid' => $u1->id)));
971         $this->assertCount(1, get_enrolled_users($c1ctx));
973         // Reset executed from data.
974         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
975         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
976         $data = array('shortname' => $c1->shortname, 'reset' => 1);
977         $importoptions = array('canreset' => true);
978         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
979         $this->assertTrue($co->prepare());
980         $co->proceed();
981         $this->assertFalse($DB->record_exists('groups', array('id' => $g1->id)));
982         $this->assertFalse($DB->record_exists('groups_members', array('groupid' => $g1->id, 'userid' => $u1->id)));
983         $this->assertCount(0, get_enrolled_users($c1ctx));
985         // Reset executed from import option.
986         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
987         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
988         $data = array('shortname' => $c1->shortname, 'reset' => 0);
989         $importoptions = array('reset' => 1, 'canreset' => true);
990         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
992         $g1 = $this->getDataGenerator()->create_group(array('courseid' => $c1->id));
993         $this->getDataGenerator()->create_group_member(array('groupid' => $g1->id, 'userid' => $u1->id));
994         $this->assertEquals(1, $DB->count_records('groups', array('courseid' => $c1->id)));
995         $this->assertTrue($co->prepare());
996         $co->proceed();
997         $this->assertFalse($DB->record_exists('groups', array('id' => $g1->id)));
998     }
1000     public function test_create_bad_category() {
1001         global $DB;
1002         $this->resetAfterTest(true);
1004         // Ensure fails when category cannot be resolved upon creation.
1005         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
1006         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
1007         $data = array('shortname' => 'c1', 'summary' => 'summary', 'fullname' => 'FN', 'category' => 'Wrong cat');
1008         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
1009         $this->assertFalse($co->prepare());
1010         $this->assertArrayHasKey('couldnotresolvecatgorybyid', $co->get_errors());
1012         // Ensure fails when category is 0 on create.
1013         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
1014         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
1015         $data = array('shortname' => 'c1', 'summary' => 'summary', 'fullname' => 'FN', 'category' => '0');
1016         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
1017         $this->assertFalse($co->prepare());
1018         $this->assertArrayHasKey('missingmandatoryfields', $co->get_errors());
1020         // Ensure fails when category cannot be resolved upon update.
1021         $c1 = $this->getDataGenerator()->create_course();
1022         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
1023         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
1024         $data = array('shortname' => $c1->shortname, 'category' => 'Wrong cat');
1025         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
1026         $this->assertFalse($co->prepare());
1027         $this->assertArrayHasKey('couldnotresolvecatgorybyid', $co->get_errors());
1029         // Ensure does not update the category when it is 0.
1030         $c1 = $this->getDataGenerator()->create_course();
1031         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
1032         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
1033         $data = array('shortname' => $c1->shortname, 'category' => '0');
1034         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
1035         $this->assertTrue($co->prepare());
1036         $this->assertEmpty($co->get_errors());
1037         $this->assertEmpty($co->get_statuses());
1038         $co->proceed();
1039         $this->assertEquals($c1->category, $DB->get_field('course', 'category', array('id' => $c1->id)));
1041         // Ensure does not update the category when it is set to 0 in the defaults.
1042         $c1 = $this->getDataGenerator()->create_course();
1043         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
1044         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_OR_DEFAUTLS;
1045         $data = array('shortname' => $c1->shortname);
1046         $defaults = array('category' => '0');
1047         $co = new tool_uploadcourse_course($mode, $updatemode, $data, $defaults);
1048         $this->assertTrue($co->prepare());
1049         $this->assertEmpty($co->get_errors());
1050         $this->assertEmpty($co->get_statuses());
1051         $co->proceed();
1052         $this->assertEquals($c1->category, $DB->get_field('course', 'category', array('id' => $c1->id)));
1053     }
1055     public function test_enrolment_data() {
1056         $this->resetAfterTest(true);
1058         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
1059         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
1060         $data = array('shortname' => 'c1', 'summary' => 'S', 'fullname' => 'FN', 'category' => '1');
1061         $data['enrolment_1'] = 'manual';
1062         $data['enrolment_1_role'] = 'teacher';
1063         $data['enrolment_1_startdate'] = '2nd July 2013';
1064         $data['enrolment_1_enddate'] = '2nd August 2013';
1065         $data['enrolment_1_enrolperiod'] = '10 days';
1066         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
1067         $this->assertTrue($co->prepare());
1068         $co->proceed();
1070         // Enrolment methods.
1071         $enroldata = array();
1072         $instances = enrol_get_instances($co->get_id(), false);
1073         foreach ($instances as $instance) {
1074             $enroldata[$instance->enrol] = $instance;
1075         }
1077         $this->assertNotEmpty($enroldata['manual']);
1078         $this->assertEquals(ENROL_INSTANCE_ENABLED, $enroldata['manual']->status);
1079         $this->assertEquals(strtotime($data['enrolment_1_startdate']), $enroldata['manual']->enrolstartdate);
1080         $this->assertEquals(strtotime('1970-01-01 GMT + ' . $data['enrolment_1_enrolperiod']), $enroldata['manual']->enrolperiod);
1081         $this->assertEquals(strtotime('12th July 2013'), $enroldata['manual']->enrolenddate);
1082     }
1084     /**
1085      * Test upload processing of course custom fields
1086      */
1087     public function test_custom_fields_data() {
1088         $this->resetAfterTest();
1089         $this->setAdminUser();
1091         $course = $this->getDataGenerator()->create_course(['shortname' => 'C1']);
1093         // Create our custom fields.
1094         $category = $this->get_customfield_generator()->create_category();
1095         $this->create_custom_field($category, 'date', 'mydatefield');
1096         $this->create_custom_field($category, 'text', 'mytextfield');
1097         $this->create_custom_field($category, 'textarea', 'mytextareafield');
1099         // Perform upload.
1100         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
1101         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
1102         $dataupload = [
1103             'shortname' => $course->shortname,
1104             'customfield_mydatefield' => '2020-04-01 16:00',
1105             'customfield_mytextfield' => 'Hello',
1106             'customfield_mytextareafield' => 'Is it me you\'re looking for?',
1107         ];
1109         $uploader = new tool_uploadcourse_course($mode, $updatemode, $dataupload);
1110         $this->assertTrue($uploader->prepare());
1111         $uploader->proceed();
1113         // Confirm presence of course custom fields.
1114         $data = \core_course\customfield\course_handler::create()->export_instance_data_object($course->id);
1115         $this->assertEquals('Wednesday, 1 April 2020, 4:00 PM', $data->mydatefield, '', 0.0, 10, false, true);
1116         $this->assertEquals($dataupload['customfield_mytextfield'], $data->mytextfield);
1117         $this->assertContains($dataupload['customfield_mytextareafield'], $data->mytextareafield);
1118     }
1120     /**
1121      * Test upload processing of course custom field that is required but empty
1122      */
1123     public function test_custom_fields_data_required() {
1124         $this->resetAfterTest();
1125         $this->setAdminUser();
1127         $course = $this->getDataGenerator()->create_course(['shortname' => 'C1']);
1129         // Create our custom field.
1130         $category = $this->get_customfield_generator()->create_category();
1131         $this->create_custom_field($category, 'select', 'myselect', ['required' => true, 'options' => "Cat\nDog"]);
1133         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
1134         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
1135         $dataupload = [
1136             'shortname' => $course->shortname,
1137             'customfield_myselect' => null,
1138         ];
1140         $uploader = new tool_uploadcourse_course($mode, $updatemode, $dataupload);
1141         $this->assertFalse($uploader->prepare());
1142         $this->assertArrayHasKey('customfieldinvalid', $uploader->get_errors());
1144         // Try again with a default value.
1145         $defaults = [
1146             'customfield_myselect' => 2, // Our second option: Dog.
1147         ];
1149         $uploader = new tool_uploadcourse_course($mode, $updatemode, $dataupload, $defaults);
1150         $this->assertTrue($uploader->prepare());
1151         $uploader->proceed();
1153         // Confirm presence of course custom fields.
1154         $data = \core_course\customfield\course_handler::create()->export_instance_data_object($course->id);
1155         $this->assertEquals('Dog', $data->myselect);
1156     }
1158     /**
1159      * Test upload processing of course custom field with an invalid select option
1160      */
1161     public function test_custom_fields_data_invalid_select_option() {
1162         $this->resetAfterTest();
1163         $this->setAdminUser();
1165         $course = $this->getDataGenerator()->create_course(['shortname' => 'C1']);
1167         // Create our custom field.
1168         $category = $this->get_customfield_generator()->create_category();
1169         $this->create_custom_field($category, 'select', 'myselect',
1170             ['required' => true, 'options' => "Cat\nDog", 'defaultvalue' => 'Cat']);
1172         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
1173         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
1174         $dataupload = [
1175             'shortname' => $course->shortname,
1176             'customfield_myselect' => 'Fish', // No, invalid.
1177         ];
1179         $uploader = new tool_uploadcourse_course($mode, $updatemode, $dataupload);
1180         $this->assertTrue($uploader->prepare());
1181         $uploader->proceed();
1183         // Confirm presence of course custom fields.
1184         $data = \core_course\customfield\course_handler::create()->export_instance_data_object($course->id);
1185         $this->assertEquals('Cat', $data->myselect);
1186     }
1188     /**
1189      * Test upload processing of course custom field with an out of range date
1190      */
1191     public function test_custom_fields_data_invalid_date() {
1192         $this->resetAfterTest();
1193         $this->setAdminUser();
1195         $course = $this->getDataGenerator()->create_course(['shortname' => 'C1']);
1197         // Create our custom field.
1198         $category = $this->get_customfield_generator()->create_category();
1199         $this->create_custom_field($category, 'date', 'mydate',
1200             ['mindate' => strtotime('2020-04-01'), 'maxdate' => '2020-04-30']);
1202         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
1203         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
1204         $dataupload = [
1205             'shortname' => $course->shortname,
1206             'customfield_mydate' => '2020-05-06', // Out of range.
1207         ];
1209         $uploader = new tool_uploadcourse_course($mode, $updatemode, $dataupload);
1210         $this->assertFalse($uploader->prepare());
1211         $this->assertArrayHasKey('customfieldinvalid', $uploader->get_errors());
1212     }
1214     public function test_idnumber_problems() {
1215         $this->resetAfterTest(true);
1217         $c1 = $this->getDataGenerator()->create_course(array('shortname' => 'sntaken', 'idnumber' => 'taken'));
1218         $c2 = $this->getDataGenerator()->create_course();
1220         // Create with existing ID number.
1221         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
1222         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
1223         $data = array('shortname' => 'c2', 'summary' => 'summary', 'fullname' => 'FN', 'category' => '1',
1224             'idnumber' => $c1->idnumber);
1225         $co = new tool_uploadcourse_course($mode, $updatemode, $data);
1226         $this->assertFalse($co->prepare());
1227         $this->assertArrayHasKey('idnumberalreadyinuse', $co->get_errors());
1229         // Rename to existing ID number.
1230         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
1231         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
1232         $data = array('shortname' => $c2->shortname, 'rename' => 'SN', 'idnumber' => $c1->idnumber);
1233         $importoptions = array('canrename' => true);
1234         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
1235         $this->assertFalse($co->prepare());
1236         $this->assertArrayHasKey('cannotrenameidnumberconflict', $co->get_errors());
1238         // Incrementing shortname increments idnumber.
1239         $mode = tool_uploadcourse_processor::MODE_CREATE_ALL;
1240         $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
1241         $data = array('shortname' => $c1->shortname, 'idnumber' => $c1->idnumber, 'summary' => 'S', 'fullname' => 'F',
1242             'category' => 1);
1243         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), array());
1244         $this->assertTrue($co->prepare());
1245         $this->assertArrayHasKey('courseshortnameincremented', $co->get_statuses());
1246         $this->assertArrayHasKey('courseidnumberincremented', $co->get_statuses());
1247         $data = $co->get_data();
1248         $this->assertEquals('sntaken_2', $data['shortname']);
1249         $this->assertEquals('taken_2', $data['idnumber']);
1251         // Incrementing shortname increments idnumber unless available.
1252         $mode = tool_uploadcourse_processor::MODE_CREATE_ALL;
1253         $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
1254         $data = array('shortname' => $c1->shortname, 'idnumber' => 'nottaken', 'summary' => 'S', 'fullname' => 'F',
1255             'category' => 1);
1256         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), array());
1257         $this->assertTrue($co->prepare());
1258         $this->assertArrayHasKey('courseshortnameincremented', $co->get_statuses());
1259         $this->assertArrayNotHasKey('courseidnumberincremented', $co->get_statuses());
1260         $data = $co->get_data();
1261         $this->assertEquals('sntaken_2', $data['shortname']);
1262         $this->assertEquals('nottaken', $data['idnumber']);
1263     }
1265     public function test_generate_shortname() {
1266         $this->resetAfterTest(true);
1268         $c1 = $this->getDataGenerator()->create_course(array('shortname' => 'taken'));
1270         // Generate a shortname.
1271         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
1272         $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
1273         $data = array('summary' => 'summary', 'fullname' => 'FN', 'category' => '1', 'idnumber' => 'IDN');
1274         $importoptions = array('shortnametemplate' => '%i');
1275         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
1276         $this->assertTrue($co->prepare());
1277         $this->assertArrayHasKey('courseshortnamegenerated', $co->get_statuses());
1279         // Generate a shortname without a template.
1280         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
1281         $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
1282         $data = array('summary' => 'summary', 'fullname' => 'FN', 'category' => '1');
1283         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), array());
1284         $this->assertFalse($co->prepare());
1285         $this->assertArrayHasKey('missingshortnamenotemplate', $co->get_errors());
1287         // Generate a shortname in update mode.
1288         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
1289         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
1290         $data = array('summary' => 'summary', 'fullname' => 'FN', 'category' => '1');
1291         $importoptions = array('shortnametemplate' => '%f');
1292         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
1293         $this->assertFalse($co->prepare());
1294         // Commented because we never get here as the course without shortname does not exist.
1295         // $this->assertArrayHasKey('cannotgenerateshortnameupdatemode', $co->get_errors());
1297         // Generate a shortname to a course that already exists.
1298         $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
1299         $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
1300         $data = array('summary' => 'summary', 'fullname' => 'taken', 'category' => '1');
1301         $importoptions = array('shortnametemplate' => '%f');
1302         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
1303         $this->assertFalse($co->prepare());
1304         $this->assertArrayHasKey('generatedshortnamealreadyinuse', $co->get_errors());
1306         // Generate a shortname to a course that already exists will be incremented.
1307         $mode = tool_uploadcourse_processor::MODE_CREATE_ALL;
1308         $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
1309         $data = array('summary' => 'summary', 'fullname' => 'taken', 'category' => '1');
1310         $importoptions = array('shortnametemplate' => '%f');
1311         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
1312         $this->assertTrue($co->prepare());
1313         $this->assertArrayHasKey('courseshortnamegenerated', $co->get_statuses());
1314         $this->assertArrayHasKey('courseshortnameincremented', $co->get_statuses());
1315     }
1317     public function test_mess_with_frontpage() {
1318         global $SITE;
1319         $this->resetAfterTest(true);
1321         // Updating the front page.
1322         $mode = tool_uploadcourse_processor::MODE_UPDATE_ONLY;
1323         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
1324         $data = array('shortname' => $SITE->shortname, 'idnumber' => 'NewIDN');
1325         $importoptions = array();
1326         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
1327         $this->assertFalse($co->prepare());
1328         $this->assertArrayHasKey('cannotupdatefrontpage', $co->get_errors());
1330         // Updating the front page.
1331         $mode = tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE;
1332         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
1333         $data = array('shortname' => $SITE->shortname, 'idnumber' => 'NewIDN');
1334         $importoptions = array();
1335         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
1336         $this->assertFalse($co->prepare());
1337         $this->assertArrayHasKey('cannotupdatefrontpage', $co->get_errors());
1339         // Generating a shortname should not be allowed in update mode, and so we cannot update the front page.
1340         $mode = tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE;
1341         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
1342         $data = array('idnumber' => 'NewIDN', 'fullname' => 'FN', 'category' => 1);
1343         $importoptions = array('shortnametemplate' => $SITE->shortname);
1344         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
1345         $this->assertFalse($co->prepare());
1346         $this->assertArrayHasKey('cannotgenerateshortnameupdatemode', $co->get_errors());
1348         // Renaming to the front page should not be allowed.
1349         $c1 = $this->getDataGenerator()->create_course();
1350         $mode = tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE;
1351         $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
1352         $data = array('shortname' => $c1->shortname, 'fullname' => 'FN', 'idnumber' => 'NewIDN', 'rename' => $SITE->shortname);
1353         $importoptions = array('canrename' => true);
1354         $co = new tool_uploadcourse_course($mode, $updatemode, $data, array(), $importoptions);
1355         $this->assertFalse($co->prepare());
1356         $this->assertArrayHasKey('cannotrenameshortnamealreadyinuse', $co->get_errors());
1357     }
1359     /**
1360      * Get custom field plugin generator
1361      *
1362      * @return core_customfield_generator
1363      */
1364     protected function get_customfield_generator() : core_customfield_generator {
1365         return $this->getDataGenerator()->get_plugin_generator('core_customfield');
1366     }
1368     /**
1369      * Helper method to create custom course field
1370      *
1371      * @param \core_customfield\category_controller $category
1372      * @param string $type
1373      * @param string $shortname
1374      * @param array $configdata
1375      * @return \core_customfield\field_controller
1376      */
1377     protected function create_custom_field(\core_customfield\category_controller $category, string $type, string $shortname,
1378             array $configdata = []) : \core_customfield\field_controller {
1380         return $this->get_customfield_generator()->create_field([
1381             'categoryid' => $category->get('id'),
1382             'type' => $type,
1383             'shortname' => $shortname,
1384             'configdata' => $configdata,
1385         ]);
1386     }