Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

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

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