Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.10.x will end 8 November 2021 (12 months).
  • Bug fixes for security issues in 3.10.x will end 9 May 2022 (18 months).
  • PHP version: minimum PHP 7.2.0 Note: minimum PHP version has increased since Moodle 3.8. PHP 7.3.x and 7.4.x are supported too.

Differences Between: [Versions 310 and 311] [Versions 310 and 400] [Versions 310 and 401] [Versions 310 and 402] [Versions 310 and 403] [Versions 39 and 310]

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