Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 3.9.x will end* 10 May 2021 (12 months).
  • Bug fixes for security issues in 3.9.x will end* 8 May 2023 (36 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 39 and 310] [Versions 39 and 311] [Versions 39 and 400] [Versions 39 and 401] [Versions 39 and 402] [Versions 39 and 403]

   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      /**
  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      }
  49  
  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      }
  62  
  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      }
  74  
  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      }
  84  
  85      public function test_invalid_shortname_too_long() {
  86          $this->resetAfterTest();
  87  
  88          $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
  89          $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
  90  
  91          $upload = new tool_uploadcourse_course($mode, $updatemode, [
  92              'category' => 1,
  93              'fullname' => 'New course',
  94              'shortname' => str_repeat('X', 2000),
  95          ]);
  96  
  97          $this->assertFalse($upload->prepare());
  98          $this->assertArrayHasKey('invalidshortnametoolong', $upload->get_errors());
  99      }
 100  
 101      public function test_invalid_fullname_too_long() {
 102          $this->resetAfterTest();
 103  
 104          $mode = tool_uploadcourse_processor::MODE_CREATE_NEW;
 105          $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
 106  
 107          $upload = new tool_uploadcourse_course($mode, $updatemode, [
 108              'category' => 1,
 109              'fullname' => str_repeat('X', 2000),
 110          ]);
 111  
 112          $this->assertFalse($upload->prepare());
 113          $this->assertArrayHasKey('invalidfullnametoolong', $upload->get_errors());
 114      }
 115  
 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      }
 125  
 126      public function test_create() {
 127          global $DB;
 128          $this->resetAfterTest(true);
 129  
 130          // Existing course.
 131          $c1 = $this->getDataGenerator()->create_course(array('shortname' => 'c1', 'summary' => 'Yay!'));
 132          $this->assertTrue($DB->record_exists('course', array('shortname' => 'c1')));
 133  
 134          $updatemode = tool_uploadcourse_processor::UPDATE_NOTHING;
 135  
 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);
 145  
 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')));
 155  
 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')));
 163  
 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      }
 174  
 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');
 180  
 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]));
 191  
 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      }
 204  
 205      public function test_delete() {
 206          global $DB;
 207          $this->resetAfterTest(true);
 208  
 209          $c1 = $this->getDataGenerator()->create_course();
 210          $c2 = $this->getDataGenerator()->create_course();
 211  
 212          $this->assertTrue($DB->record_exists('course', array('shortname' => $c1->shortname)));
 213          $this->assertFalse($DB->record_exists('course', array('shortname' => 'DoesNotExist')));
 214  
 215          $mode = tool_uploadcourse_processor::MODE_CREATE_OR_UPDATE;
 216          $updatemode = tool_uploadcourse_processor::UPDATE_ALL_WITH_DATA_ONLY;
 217  
 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)));
 225  
 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)));
 233  
 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)));
 242  
 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      }
 249  
 250      public function test_update() {
 251          global $DB;
 252          $this->resetAfterTest(true);
 253  
 254          $c1 = $this->getDataGenerator()->create_course(array('shortname' => 'c1'));
 255  
 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());
 263  
 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());
 271  
 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')));
 280  
 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')));
 291  
 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')));
 303  
 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')));
 315  
 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      }
 326  
 327      public function test_data_saved() {
 328          global $DB;
 329          $this->resetAfterTest(true);
 330  
 331          $this->setAdminUser(); // To avoid warnings related to 'moodle/course:setforcedlanguage' capability check.
 332  
 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',
 355  
 356              'role_teacher' => 'Knight',
 357              'role_manager' => 'Jedi',
 358  
 359              'enrolment_1' => 'guest',
 360              'enrolment_2' => 'self',
 361              'enrolment_2_roleid' => '1',
 362              'enrolment_3' => 'manual',
 363              'enrolment_3_disable' => '1',
 364          );
 365  
 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());
 371  
 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());
 376  
 377          // They are correct now.
 378          $data['enddate'] = '18 June 1990';
 379  
 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);
 387  
 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)));
 407  
 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'])));
 418  
 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          }
 426  
 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);
 434  
 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',
 457  
 458              'role_teacher' => 'Teacher',
 459              'role_manager' => 'Manager',
 460  
 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          );
 468  
 469          $this->assertTrue($DB->record_exists('course', array('shortname' => 'c1')));
 470  
 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());
 476  
 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());
 481  
 482          // They are correct now.
 483          $data['startdate'] = '11 June 1984';
 484  
 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);
 490  
 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);
 509  
 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'])));
 520  
 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          }
 528  
 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      }
 535  
 536      public function test_default_data_saved() {
 537          global $DB;
 538          $this->resetAfterTest(true);
 539  
 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          );
 566  
 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);
 574  
 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);
 593  
 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          );
 621  
 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);
 629  
 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      }
 649  
 650      public function test_rename() {
 651          global $DB;
 652          $this->resetAfterTest(true);
 653  
 654          $c1 = $this->getDataGenerator()->create_course(array('shortname' => 'c1'));
 655          $c2 = $this->getDataGenerator()->create_course(array('shortname' => 'c2'));
 656  
 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());
 665  
 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());
 674  
 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());
 683  
 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)));
 693  
 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)));
 703  
 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());
 712  
 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)));
 725  
 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());
 734  
 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      }
 744  
 745      public function test_restore_course() {
 746          global $DB;
 747          $this->resetAfterTest(true);
 748          $this->setAdminUser();
 749  
 750          $c1 = $this->getDataGenerator()->create_course();
 751          $c1f1 = $this->getDataGenerator()->create_module('forum', array('course' => $c1->id));
 752  
 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);
 770  
 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      }
 788  
 789      public function test_restore_file() {
 790          global $DB;
 791          $this->resetAfterTest(true);
 792          $this->setAdminUser();
 793  
 794          $c1 = $this->getDataGenerator()->create_course();
 795          $c1f1 = $this->getDataGenerator()->create_module('forum', array('course' => $c1->id));
 796  
 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);
 817  
 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      }
 837  
 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();
 845  
 846          // Set admin config setting so that activities are not restored by default.
 847          set_config('restore_general_activities', 0, 'restore');
 848  
 849          $c1 = $this->getDataGenerator()->create_course();
 850  
 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'));
 859  
 860          // Make sure the glossary is not restored.
 861          $modinfo = get_fast_modinfo($course);
 862          $this->assertEmpty($modinfo->get_instances_of('glossary'));
 863      }
 864  
 865      public function test_restore_invalid_file() {
 866          $this->resetAfterTest();
 867  
 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());
 876  
 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);
 883  
 884          $this->assertFalse($co->prepare());
 885          $this->assertArrayHasKey('invalidbackupfile', $co->get_errors());
 886  
 887          // Zip packer throws a debugging message, this assertion is only here to prevent
 888          // the message from being displayed.
 889          $this->assertDebuggingCalled();
 890      }
 891  
 892      public function test_restore_invalid_course() {
 893          $this->resetAfterTest();
 894  
 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      }
 904  
 905      /**
 906       * Testing the reset on groups, group members and enrolments.
 907       */
 908      public function test_reset() {
 909          global $DB;
 910          $this->resetAfterTest(true);
 911  
 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'));
 916  
 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));
 920  
 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)));
 925  
 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));
 936  
 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));
 948  
 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));
 960  
 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));
 972  
 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));
 984  
 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);
 991  
 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      }
 999  
1000      public function test_create_bad_category() {
1001          global $DB;
1002          $this->resetAfterTest(true);
1003  
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());
1011  
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());
1019  
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());
1028  
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)));
1040  
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      }
1054  
1055      public function test_enrolment_data() {
1056          $this->resetAfterTest(true);
1057  
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();
1069  
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          }
1076  
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      }
1083  
1084      /**
1085       * Test upload processing of course custom fields
1086       */
1087      public function test_custom_fields_data() {
1088          $this->resetAfterTest();
1089          $this->setAdminUser();
1090  
1091          $course = $this->getDataGenerator()->create_course(['shortname' => 'C1']);
1092  
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');
1098  
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          ];
1108  
1109          $uploader = new tool_uploadcourse_course($mode, $updatemode, $dataupload);
1110          $this->assertTrue($uploader->prepare());
1111          $uploader->proceed();
1112  
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      }
1119  
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();
1126  
1127          $course = $this->getDataGenerator()->create_course(['shortname' => 'C1']);
1128  
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"]);
1132  
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          ];
1139  
1140          $uploader = new tool_uploadcourse_course($mode, $updatemode, $dataupload);
1141          $this->assertFalse($uploader->prepare());
1142          $this->assertArrayHasKey('customfieldinvalid', $uploader->get_errors());
1143  
1144          // Try again with a default value.
1145          $defaults = [
1146              'customfield_myselect' => 2, // Our second option: Dog.
1147          ];
1148  
1149          $uploader = new tool_uploadcourse_course($mode, $updatemode, $dataupload, $defaults);
1150          $this->assertTrue($uploader->prepare());
1151          $uploader->proceed();
1152  
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      }
1157  
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();
1164  
1165          $course = $this->getDataGenerator()->create_course(['shortname' => 'C1']);
1166  
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']);
1171  
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          ];
1178  
1179          $uploader = new tool_uploadcourse_course($mode, $updatemode, $dataupload);
1180          $this->assertTrue($uploader->prepare());
1181          $uploader->proceed();
1182  
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      }
1187  
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();
1194  
1195          $course = $this->getDataGenerator()->create_course(['shortname' => 'C1']);
1196  
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']);
1201  
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          ];
1208  
1209          $uploader = new tool_uploadcourse_course($mode, $updatemode, $dataupload);
1210          $this->assertFalse($uploader->prepare());
1211          $this->assertArrayHasKey('customfieldinvalid', $uploader->get_errors());
1212      }
1213  
1214      public function test_idnumber_problems() {
1215          $this->resetAfterTest(true);
1216  
1217          $c1 = $this->getDataGenerator()->create_course(array('shortname' => 'sntaken', 'idnumber' => 'taken'));
1218          $c2 = $this->getDataGenerator()->create_course();
1219  
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());
1228  
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());
1237  
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']);
1250  
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      }
1264  
1265      public function test_generate_shortname() {
1266          $this->resetAfterTest(true);
1267  
1268          $c1 = $this->getDataGenerator()->create_course(array('shortname' => 'taken'));
1269  
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());
1278  
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());
1286  
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());
1296  
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());
1305  
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      }
1316  
1317      public function test_mess_with_frontpage() {
1318          global $SITE;
1319          $this->resetAfterTest(true);
1320  
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());
1329  
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());
1338  
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());
1347  
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      }
1358  
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      }
1367  
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 {
1379  
1380          return $this->get_customfield_generator()->create_field([
1381              'categoryid' => $category->get('id'),
1382              'type' => $type,
1383              'shortname' => $shortname,
1384              'configdata' => $configdata,
1385          ]);
1386      }
1387  }