Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 310 and 401] [Versions 39 and 401]

   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 enrol_imsenterprise;
  18  
  19  use enrol_imsenterprise_plugin;
  20  use enrol_imsenterprise\task\cron_task;
  21  
  22  defined('MOODLE_INTERNAL') || die();
  23  
  24  global $CFG;
  25  require_once($CFG->dirroot . '/enrol/imsenterprise/locallib.php');
  26  require_once($CFG->dirroot . '/enrol/imsenterprise/lib.php');
  27  
  28  /**
  29   * IMS Enterprise test case
  30   *
  31   * @package    enrol_imsenterprise
  32   * @category   test
  33   * @copyright  2012 David MonllaĆ³
  34   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  35   */
  36  class imsenterprise_test extends \advanced_testcase {
  37  
  38      /**
  39       * @var $imsplugin enrol_imsenterprise_plugin IMS plugin instance.
  40       */
  41      public $imsplugin;
  42  
  43      /**
  44       * Setup required for all tests.
  45       */
  46      protected function setUp(): void {
  47          $this->resetAfterTest(true);
  48          $this->imsplugin = enrol_get_plugin('imsenterprise');
  49          $this->set_test_config();
  50      }
  51  
  52      /**
  53       * With an empty IMS enterprise file
  54       */
  55      public function test_emptyfile() {
  56          global $DB;
  57  
  58          $prevncourses = $DB->count_records('course');
  59          $prevnusers = $DB->count_records('user');
  60  
  61          $this->set_xml_file(false, false);
  62          $this->imsplugin->cron();
  63  
  64          $this->assertEquals($prevncourses, $DB->count_records('course'));
  65          $this->assertEquals($prevnusers, $DB->count_records('user'));
  66      }
  67  
  68      /**
  69       * Existing users are not created again
  70       */
  71      public function test_users_existing() {
  72          global $DB;
  73  
  74          $user1 = $this->getDataGenerator()->create_user();
  75          $user2 = $this->getDataGenerator()->create_user();
  76  
  77          $prevnusers = $DB->count_records('user');
  78  
  79          $users = array($user1, $user2);
  80          $this->set_xml_file($users);
  81          $this->imsplugin->cron();
  82  
  83          $this->assertEquals($prevnusers, $DB->count_records('user'));
  84      }
  85  
  86      /**
  87       * Add new users
  88       */
  89      public function test_users_add() {
  90          global $DB;
  91  
  92          $prevnusers = $DB->count_records('user');
  93  
  94          $user1 = new \stdClass();
  95          $user1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
  96          $user1->username = 'u1';
  97          $user1->email = 'u1@example.com';
  98          $user1->firstname = 'U';
  99          $user1->lastname = '1';
 100  
 101          $users = array($user1);
 102          $this->set_xml_file($users);
 103          $this->imsplugin->cron();
 104  
 105          $this->assertEquals(($prevnusers + 1), $DB->count_records('user'));
 106      }
 107  
 108      /**
 109       * Add new users and set an auth type
 110       */
 111      public function test_users_add_with_auth() {
 112          global $DB;
 113  
 114          $prevnusers = $DB->count_records('user');
 115  
 116          $user2 = new \stdClass();
 117          $user2->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 118          $user2->username = 'u2';
 119          $user2->auth = 'cas';
 120          $user2->email = 'u2@u2.org';
 121          $user2->firstname = 'U';
 122          $user2->lastname = '2';
 123  
 124          $users = array($user2);
 125          $this->set_xml_file($users);
 126          $this->imsplugin->cron();
 127  
 128          $dbuser = $DB->get_record('user', array('username' => $user2->username));
 129          // TODO: MDL-15863 this needs more work due to multiauth changes, use first auth for now.
 130          $dbauth = explode(',', $dbuser->auth);
 131          $dbauth = reset($dbauth);
 132  
 133          $this->assertEquals(($prevnusers + 1), $DB->count_records('user'));
 134          $this->assertEquals($dbauth, $user2->auth);
 135      }
 136  
 137  
 138      /**
 139       * Update user
 140       */
 141      public function test_user_update() {
 142          global $DB;
 143  
 144          $user = $this->getDataGenerator()->create_user(array('idnumber' => 'test-update-user'));
 145          $imsuser = new \stdClass();
 146          $imsuser->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_UPDATE;
 147          // THIS SHOULD WORK, surely?: $imsuser->username = $user->username;
 148          // But this is required...
 149          $imsuser->username = $user->idnumber;
 150          $imsuser->email = 'u3@u3.org';
 151          $imsuser->firstname = 'U';
 152          $imsuser->lastname = '3';
 153  
 154          $this->set_xml_file(array($imsuser));
 155          $this->imsplugin->cron();
 156          $dbuser = $DB->get_record('user', array('id' => $user->id), '*', MUST_EXIST);
 157          $this->assertEquals($imsuser->email, $dbuser->email);
 158          $this->assertEquals($imsuser->firstname, $dbuser->firstname);
 159          $this->assertEquals($imsuser->lastname, $dbuser->lastname);
 160      }
 161  
 162      public function test_user_update_disabled() {
 163          global $DB;
 164  
 165          $this->imsplugin->set_config('imsupdateusers', false);
 166  
 167          $user = $this->getDataGenerator()->create_user(array('idnumber' => 'test-update-user'));
 168          $imsuser = new \stdClass();
 169          $imsuser->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_UPDATE;
 170          // THIS SHOULD WORK, surely?: $imsuser->username = $user->username;
 171          // But this is required...
 172          $imsuser->username = $user->idnumber;
 173          $imsuser->email = 'u3@u3.org';
 174          $imsuser->firstname = 'U';
 175          $imsuser->lastname = '3';
 176  
 177          $this->set_xml_file(array($imsuser));
 178          $this->imsplugin->cron();
 179  
 180          // Verify no changes have been made.
 181          $dbuser = $DB->get_record('user', array('id' => $user->id), '*', MUST_EXIST);
 182          $this->assertEquals($user->email, $dbuser->email);
 183          $this->assertEquals($user->firstname, $dbuser->firstname);
 184          $this->assertEquals($user->lastname, $dbuser->lastname);
 185      }
 186  
 187      /**
 188       * Delete user
 189       */
 190      public function test_user_delete() {
 191          global $DB;
 192  
 193          $this->imsplugin->set_config('imsdeleteusers', true);
 194          $user = $this->getDataGenerator()->create_user(array('idnumber' => 'test-update-user'));
 195  
 196          $imsuser = new \stdClass();
 197          $imsuser->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_DELETE;
 198          $imsuser->username = $user->username;
 199          $imsuser->firstname = $user->firstname;
 200          $imsuser->lastname = $user->lastname;
 201          $imsuser->email = $user->email;
 202          $this->set_xml_file(array($imsuser));
 203  
 204          $this->imsplugin->cron();
 205          $this->assertEquals(1, $DB->get_field('user', 'deleted', array('id' => $user->id), MUST_EXIST));
 206      }
 207  
 208      /**
 209       * Delete user disabled
 210       */
 211      public function test_user_delete_disabled() {
 212          global $DB;
 213  
 214          $this->imsplugin->set_config('imsdeleteusers', false);
 215          $user = $this->getDataGenerator()->create_user(array('idnumber' => 'test-update-user'));
 216  
 217          $imsuser = new \stdClass();
 218          $imsuser->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_DELETE;
 219          $imsuser->username = $user->username;
 220          $imsuser->firstname = $user->firstname;
 221          $imsuser->lastname = $user->lastname;
 222          $imsuser->email = $user->email;
 223          $this->set_xml_file(array($imsuser));
 224  
 225          $this->imsplugin->cron();
 226          $this->assertEquals(0, $DB->get_field('user', 'deleted', array('id' => $user->id), MUST_EXIST));
 227      }
 228  
 229      /**
 230       * Existing courses are not created again
 231       */
 232      public function test_courses_existing() {
 233          global $DB;
 234  
 235          $course1 = $this->getDataGenerator()->create_course(array('idnumber' => 'id1'));
 236          $course2 = $this->getDataGenerator()->create_course(array('idnumber' => 'id2'));
 237  
 238          // Default mapping according to default course attributes - IMS description tags mapping.
 239          $course1->imsshort = $course1->fullname;
 240          $course2->imsshort = $course2->fullname;
 241          unset($course1->category);
 242          unset($course2->category);
 243  
 244          $prevncourses = $DB->count_records('course');
 245  
 246          $courses = array($course1, $course2);
 247          $this->set_xml_file(false, $courses);
 248          $this->imsplugin->cron();
 249  
 250          $this->assertEquals($prevncourses, $DB->count_records('course'));
 251      }
 252  
 253      /**
 254       * Add new courses
 255       */
 256      public function test_courses_add() {
 257          global $DB;
 258  
 259          $prevncourses = $DB->count_records('course');
 260  
 261          $course1 = new \stdClass();
 262          $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 263          $course1->idnumber = 'id1';
 264          $course1->imsshort = 'id1';
 265          $course1->category[] = 'DEFAULT CATNAME';
 266  
 267          $course2 = new \stdClass();
 268          $course2->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 269          $course2->idnumber = 'id2';
 270          $course2->imsshort = 'id2';
 271          $course2->category[] = 'DEFAULT CATNAME';
 272  
 273          $courses = array($course1, $course2);
 274          $this->set_xml_file(false, $courses);
 275          $this->imsplugin->cron();
 276  
 277          $this->assertEquals(($prevncourses + 2), $DB->count_records('course'));
 278          $this->assertTrue($DB->record_exists('course', array('idnumber' => $course1->idnumber)));
 279          $this->assertTrue($DB->record_exists('course', array('idnumber' => $course2->idnumber)));
 280      }
 281  
 282      /**
 283       * Verify that courses are not created when createnewcourses
 284       * option is diabled.
 285       */
 286      public function test_courses_add_createnewcourses_disabled() {
 287          global $DB;
 288  
 289          $this->imsplugin->set_config('createnewcourses', false);
 290          $prevncourses = $DB->count_records('course');
 291  
 292          $course1 = new \stdClass();
 293          $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 294          $course1->idnumber = 'id1';
 295          $course1->imsshort = 'id1';
 296          $course1->category[] = 'DEFAULT CATNAME';
 297  
 298          $course2 = new \stdClass();
 299          $course2->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 300          $course2->idnumber = 'id2';
 301          $course2->imsshort = 'id2';
 302          $course2->category[] = 'DEFAULT CATNAME';
 303  
 304          $courses = array($course1, $course2);
 305          $this->set_xml_file(false, $courses);
 306          $this->imsplugin->cron();
 307  
 308          $courses = array($course1, $course2);
 309          $this->set_xml_file(false, $courses);
 310          $this->imsplugin->cron();
 311  
 312          // Verify the courses have not ben creased.
 313          $this->assertEquals($prevncourses , $DB->count_records('course'));
 314          $this->assertFalse($DB->record_exists('course', array('idnumber' => $course1->idnumber)));
 315          $this->assertFalse($DB->record_exists('course', array('idnumber' => $course2->idnumber)));
 316      }
 317  
 318      /**
 319       * Test adding a course with no idnumber.
 320       */
 321      public function test_courses_no_idnumber() {
 322          global $DB;
 323  
 324          $prevncourses = $DB->count_records('course');
 325  
 326          $course1 = new \stdClass();
 327          $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 328          $course1->idnumber = '';
 329          $course1->imsshort = 'id1';
 330          $course1->category[] = 'DEFAULT CATNAME';
 331  
 332          $this->set_xml_file(false, array($course1));
 333          $this->imsplugin->cron();
 334  
 335          // Verify no action.
 336          $this->assertEquals($prevncourses, $DB->count_records('course'));
 337      }
 338  
 339      /**
 340       * Add new course with the truncateidnumber setting.
 341       */
 342      public function test_courses_add_truncate_idnumber() {
 343          global $DB;
 344  
 345          $truncatelength = 4;
 346  
 347          $this->imsplugin->set_config('truncatecoursecodes', $truncatelength);
 348          $prevncourses = $DB->count_records('course');
 349  
 350          $course1 = new \stdClass();
 351          $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 352          $course1->idnumber = '123456789';
 353          $course1->imsshort = 'id1';
 354          $course1->category[] = 'DEFAULT CATNAME';
 355  
 356          $this->set_xml_file(false, array($course1));
 357          $this->imsplugin->cron();
 358  
 359          // Verify the new course has been added.
 360          $this->assertEquals(($prevncourses + 1), $DB->count_records('course'));
 361  
 362          $truncatedidnumber = substr($course1->idnumber, 0, $truncatelength);
 363  
 364          $this->assertTrue($DB->record_exists('course', array('idnumber' => $truncatedidnumber)));
 365      }
 366  
 367      /**
 368       * Add new course without a category.
 369       */
 370      public function test_course_add_default_category() {
 371          global $DB;
 372  
 373          $this->imsplugin->set_config('createnewcategories', false);
 374  
 375          // Delete the default category, to ensure the plugin handles this gracefully.
 376          $defaultcat = \core_course_category::get_default();
 377          $defaultcat->delete_full(false);
 378  
 379          // Create an course with the IMS plugin without a category.
 380          $course1 = new \stdClass();
 381          $course1->idnumber = 'id1';
 382          $course1->imsshort = 'id1';
 383          $course1->category[] = '';
 384          $this->set_xml_file(false, array($course1));
 385          $this->imsplugin->cron();
 386  
 387          // Check the course has been created.
 388          $dbcourse = $DB->get_record('course', array('idnumber' => $course1->idnumber), '*', MUST_EXIST);
 389          // Check that it belongs to a category which exists.
 390          $this->assertTrue($DB->record_exists('course_categories', array('id' => $dbcourse->category)));
 391      }
 392  
 393      /**
 394       * Course attributes mapping to IMS enterprise group description tags
 395       */
 396      public function test_courses_attrmapping() {
 397          global $DB;
 398  
 399          // Setting a all = coursecode (idnumber) mapping.
 400          $this->imsplugin->set_config('imscoursemapshortname', 'coursecode');
 401          $this->imsplugin->set_config('imscoursemapfullname', 'coursecode');
 402          $this->imsplugin->set_config('imscoursemapsummary', 'coursecode');
 403  
 404          $course1 = new \stdClass();
 405          $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 406          $course1->idnumber = 'id1';
 407          $course1->imsshort = 'description_short1';
 408          $course1->imslong = 'description_long';
 409          $course1->imsfull = 'description_full';
 410          $course1->category[] = 'DEFAULT CATNAME';
 411  
 412          $this->set_xml_file(false, array($course1));
 413          $this->imsplugin->cron();
 414  
 415          $dbcourse = $DB->get_record('course', array('idnumber' => $course1->idnumber));
 416          $this->assertFalse(!$dbcourse);
 417          $this->assertEquals($dbcourse->shortname, $course1->idnumber);
 418          $this->assertEquals($dbcourse->fullname, $course1->idnumber);
 419          $this->assertEquals($dbcourse->summary, $course1->idnumber);
 420  
 421          // Setting a mapping using all the description tags.
 422          $this->imsplugin->set_config('imscoursemapshortname', 'short');
 423          $this->imsplugin->set_config('imscoursemapfullname', 'long');
 424          $this->imsplugin->set_config('imscoursemapsummary', 'full');
 425  
 426          $course2 = new \stdClass();
 427          $course2->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 428          $course2->idnumber = 'id2';
 429          $course2->imsshort = 'description_short2';
 430          $course2->imslong = 'description_long';
 431          $course2->imsfull = 'description_full';
 432          $course2->category[] = 'DEFAULT CATNAME';
 433  
 434          $this->set_xml_file(false, array($course2));
 435          $this->imsplugin->cron();
 436  
 437          $dbcourse = $DB->get_record('course', array('idnumber' => $course2->idnumber));
 438          $this->assertFalse(!$dbcourse);
 439          $this->assertEquals($dbcourse->shortname, $course2->imsshort);
 440          $this->assertEquals($dbcourse->fullname, $course2->imslong);
 441          $this->assertEquals($dbcourse->summary, $course2->imsfull);
 442  
 443          // Setting a mapping where the specified description tags doesn't exist in the XML file (must delegate into idnumber).
 444          $this->imsplugin->set_config('imscoursemapshortname', 'short');
 445          $this->imsplugin->set_config('imscoursemapfullname', 'long');
 446          $this->imsplugin->set_config('imscoursemapsummary', 'full');
 447  
 448          $course3 = new \stdClass();
 449          $course3->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 450          $course3->idnumber = 'id3';
 451          $course3->imsshort = 'description_short3';
 452          $course3->category[] = 'DEFAULT CATNAME';
 453  
 454          $this->set_xml_file(false, array($course3));
 455          $this->imsplugin->cron();
 456  
 457          $dbcourse = $DB->get_record('course', array('idnumber' => $course3->idnumber));
 458          $this->assertFalse(!$dbcourse);
 459          $this->assertEquals($dbcourse->shortname, $course3->imsshort);
 460          $this->assertEquals($dbcourse->fullname, $course3->idnumber);
 461          $this->assertEquals($dbcourse->summary, $course3->idnumber);
 462  
 463      }
 464  
 465      /**
 466       * Course updates
 467       */
 468      public function test_course_update() {
 469          global $DB;
 470  
 471          $course4 = new \stdClass();
 472          $course4->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 473          $course4->idnumber = 'id4';
 474          $course4->imsshort = 'id4';
 475          $course4->imsfull = 'id4';
 476          $course4->category[] = 'DEFAULT CATNAME';
 477  
 478          $this->set_xml_file(false, array($course4));
 479          $this->imsplugin->cron();
 480  
 481          $course4u = $DB->get_record('course', array('idnumber' => $course4->idnumber));
 482  
 483          $course4u->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_UPDATE;
 484          $course4u->imsshort = 'description_short_updated';
 485          $course4u->imsfull = 'description_full_updated';
 486          unset($course4u->category);
 487  
 488          $this->set_xml_file(false, array($course4u));
 489          $this->imsplugin->cron();
 490  
 491          $dbcourse = $DB->get_record('course', array('idnumber' => $course4->idnumber));
 492          $this->assertFalse(!$dbcourse);
 493          $this->assertEquals($dbcourse->shortname, $course4u->imsshort);
 494          $this->assertEquals($dbcourse->fullname, $course4u->imsfull);
 495      }
 496  
 497      /**
 498       * Course delete. Make it hidden.
 499       */
 500      public function test_course_delete() {
 501          global $DB;
 502  
 503          $course8 = new \stdClass();
 504          $course8->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 505          $course8->idnumber = 'id8';
 506          $course8->imsshort = 'id8';
 507          $course8->imsfull = 'id8';
 508          $course8->category[] = 'DEFAULT CATNAME';
 509  
 510          $this->set_xml_file(false, array($course8));
 511          $this->imsplugin->cron();
 512  
 513          $course8d = $DB->get_record('course', array('idnumber' => $course8->idnumber));
 514          $this->assertEquals($course8d->visible, 1);
 515  
 516          $course8d->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_DELETE;
 517          unset($course8d->category);
 518  
 519          $this->set_xml_file(false, array($course8d));
 520          $this->imsplugin->cron();
 521  
 522          $dbcourse = $DB->get_record('course', array('idnumber' => $course8d->idnumber));
 523          $this->assertFalse(!$dbcourse);
 524          $this->assertEquals($dbcourse->visible, 0);
 525      }
 526  
 527  
 528      /**
 529       * Nested categories with name during course creation
 530       */
 531      public function test_nested_categories() {
 532          global $DB;
 533  
 534          $this->imsplugin->set_config('nestedcategories', true);
 535  
 536          $topcat = 'DEFAULT CATNAME';
 537          $subcat = 'DEFAULT SUB CATNAME';
 538  
 539          $course5 = new \stdClass();
 540          $course5->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 541          $course5->idnumber = 'id5';
 542          $course5->imsshort = 'description_short';
 543          $course5->imslong = 'description_long';
 544          $course5->imsfull = 'description_full';
 545          $course5->category = array();
 546          $course5->category[] = $topcat;
 547          $course5->category[] = $subcat;
 548  
 549          $this->set_xml_file(false, array($course5));
 550          $this->imsplugin->cron();
 551  
 552          $parentcatid = $DB->get_field('course_categories', 'id', array('name' => $topcat));
 553          $subcatid = $DB->get_field('course_categories', 'id', array('name' => $subcat, 'parent' => $parentcatid));
 554  
 555          $this->assertTrue(isset($subcatid));
 556          $this->assertTrue($subcatid > 0);
 557  
 558          $topcat = 'DEFAULT CATNAME';
 559          $subcat = 'DEFAULT SUB CATNAME TEST2';
 560  
 561          $course6 = new \stdClass();
 562          $course6->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 563          $course6->idnumber = 'id6';
 564          $course6->imsshort = 'description_short';
 565          $course6->imslong = 'description_long';
 566          $course6->imsfull = 'description_full';
 567          $course6->category = array();
 568          $course6->category[] = $topcat;
 569          $course6->category[] = $subcat;
 570  
 571          $this->set_xml_file(false, array($course6));
 572          $this->imsplugin->cron();
 573  
 574          $parentcatid = $DB->get_field('course_categories', 'id', array('name' => $topcat));
 575          $subcatid = $DB->get_field('course_categories', 'id', array('name' => $subcat, 'parent' => $parentcatid));
 576  
 577          $this->assertTrue(isset($subcatid));
 578          $this->assertTrue($subcatid > 0);
 579      }
 580  
 581  
 582      /**
 583       * Test that duplicate nested categories with name are not created
 584       */
 585      public function test_nested_categories_for_dups() {
 586          global $DB;
 587  
 588          $this->imsplugin->set_config('nestedcategories', true);
 589  
 590          $topcat = 'DEFAULT CATNAME';
 591          $subcat = 'DEFAULT SUB CATNAME DUPTEST';
 592  
 593          $course7 = new \stdClass();
 594          $course7->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 595          $course7->idnumber = 'id7';
 596          $course7->imsshort = 'description_short';
 597          $course7->imslong = 'description_long';
 598          $course7->imsfull = 'description_full';
 599          $course7->category[] = $topcat;
 600          $course7->category[] = $subcat;
 601  
 602          $this->set_xml_file(false, array($course7));
 603          $this->imsplugin->cron();
 604  
 605          $prevncategories = $DB->count_records('course_categories');
 606  
 607          $course8 = new \stdClass();
 608          $course8->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 609          $course8->idnumber = 'id8';
 610          $course8->imsshort = 'description_short';
 611          $course8->imslong = 'description_long';
 612          $course8->imsfull = 'description_full';
 613          $course8->category[] = $topcat;
 614          $course8->category[] = $subcat;
 615  
 616          $this->set_xml_file(false, array($course8));
 617          $this->imsplugin->cron();
 618  
 619          $this->assertEquals($prevncategories, $DB->count_records('course_categories'));
 620      }
 621  
 622      /**
 623       * Nested categories with idnumber during course creation
 624       */
 625      public function test_nested_categories_idnumber() {
 626          global $DB;
 627  
 628          $this->imsplugin->set_config('nestedcategories', true);
 629          $this->imsplugin->set_config('categoryidnumber', true);
 630          $this->imsplugin->set_config('categoryseparator', '|');
 631  
 632          $catsep = trim($this->imsplugin->get_config('categoryseparator'));
 633  
 634          $topcatname = 'DEFAULT CATNAME';
 635          $subcatname = 'DEFAULT SUB CATNAME';
 636          $topcatidnumber = '01';
 637          $subcatidnumber = '0101';
 638  
 639          $topcat = $topcatname.$catsep.$topcatidnumber;
 640          $subcat = $subcatname.$catsep.$subcatidnumber;
 641  
 642          $course1 = new \stdClass();
 643          $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 644          $course1->idnumber = 'id5';
 645          $course1->imsshort = 'description_short';
 646          $course1->imslong = 'description_long';
 647          $course1->imsfull = 'description_full';
 648          $course1->category[] = $topcat;
 649          $course1->category[] = $subcat;
 650  
 651          $this->set_xml_file(false, array($course1));
 652          $this->imsplugin->cron();
 653  
 654          $parentcatid = $DB->get_field('course_categories', 'id', array('idnumber' => $topcatidnumber));
 655          $subcatid = $DB->get_field('course_categories', 'id', array('idnumber' => $subcatidnumber, 'parent' => $parentcatid));
 656  
 657          $this->assertTrue(isset($subcatid));
 658          $this->assertTrue($subcatid > 0);
 659  
 660          // Change the category separator character.
 661          $this->imsplugin->set_config('categoryseparator', ':');
 662  
 663          $catsep = trim($this->imsplugin->get_config('categoryseparator'));
 664  
 665          $topcatname = 'DEFAULT CATNAME';
 666          $subcatname = 'DEFAULT SUB CATNAME TEST2';
 667          $topcatidnumber = '01';
 668          $subcatidnumber = '0102';
 669  
 670          $topcat = $topcatname.$catsep.$topcatidnumber;
 671          $subcat = $subcatname.$catsep.$subcatidnumber;
 672  
 673          $course2 = new \stdClass();
 674          $course2->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 675          $course2->idnumber = 'id6';
 676          $course2->imsshort = 'description_short';
 677          $course2->imslong = 'description_long';
 678          $course2->imsfull = 'description_full';
 679          $course2->category[] = $topcat;
 680          $course2->category[] = $subcat;
 681  
 682          $this->set_xml_file(false, array($course2));
 683          $this->imsplugin->cron();
 684  
 685          $parentcatid = $DB->get_field('course_categories', 'id', array('idnumber' => $topcatidnumber));
 686          $subcatid = $DB->get_field('course_categories', 'id', array('idnumber' => $subcatidnumber, 'parent' => $parentcatid));
 687  
 688          $this->assertTrue(isset($subcatid));
 689          $this->assertTrue($subcatid > 0);
 690      }
 691  
 692      /**
 693       * Test that duplicate nested categories with idnumber are not created
 694       */
 695      public function test_nested_categories_idnumber_for_dups() {
 696          global $DB;
 697  
 698          $this->imsplugin->set_config('nestedcategories', true);
 699          $this->imsplugin->set_config('categoryidnumber', true);
 700          $this->imsplugin->set_config('categoryseparator', '|');
 701  
 702          $catsep = trim($this->imsplugin->get_config('categoryseparator'));
 703  
 704          $topcatname = 'DEFAULT CATNAME';
 705          $subcatname = 'DEFAULT SUB CATNAME';
 706          $topcatidnumber = '01';
 707          $subcatidnumber = '0101';
 708  
 709          $topcat = $topcatname.$catsep.$topcatidnumber;
 710          $subcat = $subcatname.$catsep.$subcatidnumber;
 711  
 712          $course1 = new \stdClass();
 713          $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 714          $course1->idnumber = 'id1';
 715          $course1->imsshort = 'description_short';
 716          $course1->imslong = 'description_long';
 717          $course1->imsfull = 'description_full';
 718          $course1->category[] = $topcat;
 719          $course1->category[] = $subcat;
 720  
 721          $this->set_xml_file(false, array($course1));
 722          $this->imsplugin->cron();
 723  
 724          $prevncategories = $DB->count_records('course_categories');
 725  
 726          $course2 = new \stdClass();
 727          $course2->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 728          $course2->idnumber = 'id2';
 729          $course2->imsshort = 'description_short';
 730          $course2->imslong = 'description_long';
 731          $course2->imsfull = 'description_full';
 732          $course2->category[] = $topcat;
 733          $course2->category[] = $subcat;
 734  
 735          $this->set_xml_file(false, array($course2));
 736          $this->imsplugin->cron();
 737  
 738          $this->assertEquals($prevncategories, $DB->count_records('course_categories'));
 739      }
 740  
 741      /**
 742       * Test that nested categories with idnumber is not created if name is missing
 743       */
 744      public function test_categories_idnumber_missing_name() {
 745          global $DB, $CFG;
 746  
 747          $this->imsplugin->set_config('nestedcategories', true);
 748          $this->imsplugin->set_config('categoryidnumber', true);
 749          $this->imsplugin->set_config('categoryseparator', '|');
 750          $catsep = trim($this->imsplugin->get_config('categoryseparator'));
 751  
 752          $topcatname = 'DEFAULT CATNAME';
 753          $subcatname = '';
 754          $topcatidnumber = '01';
 755          $subcatidnumber = '0101';
 756  
 757          $topcat = $topcatname.$catsep.$topcatidnumber;
 758          $subcat = $subcatname.$catsep.$subcatidnumber;
 759  
 760          $course1 = new \stdClass();
 761          $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 762          $course1->idnumber = 'id1';
 763          $course1->imsshort = 'description_short';
 764          $course1->imslong = 'description_long';
 765          $course1->imsfull = 'description_full';
 766          $course1->category[] = $topcat;
 767          $course1->category[] = $subcat;
 768  
 769          $this->set_xml_file(false, array($course1));
 770          $this->imsplugin->cron();
 771  
 772          // Check all categories except the last subcategory was created.
 773          $parentcatid = $DB->get_field('course_categories', 'id', array('idnumber' => $topcatidnumber));
 774          $this->assertTrue((boolean)$parentcatid);
 775          $subcatid = $DB->get_field('course_categories', 'id', array('idnumber' => $subcatidnumber, 'parent' => $parentcatid));
 776          $this->assertFalse((boolean)$subcatid);
 777  
 778          // Check course was put in default category.
 779          $defaultcat = \core_course_category::get_default();
 780          $dbcourse = $DB->get_record('course', array('idnumber' => $course1->idnumber), '*', MUST_EXIST);
 781          $this->assertEquals($dbcourse->category, $defaultcat->id);
 782  
 783      }
 784  
 785      /**
 786       * Create category with name (nested categories not activated).
 787       */
 788      public function test_create_category_name_no_nested() {
 789          global $DB;
 790  
 791          $course = new \stdClass();
 792          $course->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 793          $course->idnumber = 'id';
 794          $course->imsshort = 'description_short';
 795          $course->imslong = 'description_long';
 796          $course->imsfull = 'description_full';
 797          $course->category[] = 'CATNAME';
 798  
 799          $this->set_xml_file(false, array($course));
 800          $this->imsplugin->cron();
 801  
 802          $dbcat = $DB->get_record('course_categories', array('name' => $course->category[0]));
 803          $this->assertFalse(!$dbcat);
 804          $this->assertEquals($dbcat->parent, 0);
 805  
 806          $dbcourse = $DB->get_record('course', array('idnumber' => $course->idnumber));
 807          $this->assertFalse(!$dbcourse);
 808          $this->assertEquals($dbcourse->category, $dbcat->id);
 809  
 810      }
 811  
 812      /**
 813       * Find a category with name (nested categories not activated).
 814       */
 815      public function test_find_category_name_no_nested() {
 816          global $DB;
 817  
 818          $cattop = $this->getDataGenerator()->create_category(array('name' => 'CAT-TOP'));
 819          $catsub = $this->getDataGenerator()->create_category(array('name' => 'CAT-SUB', 'parent' => $cattop->id));
 820          $prevcats = $DB->count_records('course_categories');
 821  
 822          $course = new \stdClass();
 823          $course->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 824          $course->idnumber = 'id';
 825          $course->imsshort = 'description_short';
 826          $course->imslong = 'description_long';
 827          $course->imsfull = 'description_full';
 828          $course->category[] = 'CAT-SUB';
 829  
 830          $this->set_xml_file(false, array($course));
 831          $this->imsplugin->cron();
 832  
 833          $newcats = $DB->count_records('course_categories');
 834  
 835          // Check that no new category was not created.
 836          $this->assertEquals($prevcats, $newcats);
 837  
 838          // Check course is associated to CAT-SUB.
 839          $dbcourse = $DB->get_record('course', array('idnumber' => $course->idnumber));
 840          $this->assertFalse(!$dbcourse);
 841          $this->assertEquals($dbcourse->category, $catsub->id);
 842  
 843      }
 844  
 845      /**
 846       * Create category with idnumber (nested categories not activated).
 847       */
 848      public function test_create_category_idnumber_no_nested() {
 849          global $DB;
 850  
 851          $this->imsplugin->set_config('categoryidnumber', true);
 852          $this->imsplugin->set_config('categoryseparator', '|');
 853          $catsep = trim($this->imsplugin->get_config('categoryseparator'));
 854  
 855          $course = new \stdClass();
 856          $course->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 857          $course->idnumber = 'id';
 858          $course->imsshort = 'description_short';
 859          $course->imslong = 'description_long';
 860          $course->imsfull = 'description_full';
 861          $course->category[] = 'CATNAME'. $catsep .  'CATIDNUMBER';
 862  
 863          $this->set_xml_file(false, array($course));
 864          $this->imsplugin->cron();
 865  
 866          $dbcat = $DB->get_record('course_categories', array('idnumber' => 'CATIDNUMBER'));
 867          $this->assertFalse(!$dbcat);
 868          $this->assertEquals($dbcat->parent, 0);
 869          $this->assertEquals($dbcat->name, 'CATNAME');
 870  
 871          $dbcourse = $DB->get_record('course', array('idnumber' => $course->idnumber));
 872          $this->assertFalse(!$dbcourse);
 873          $this->assertEquals($dbcourse->category, $dbcat->id);
 874  
 875      }
 876  
 877      /**
 878       * Find a category with idnumber (nested categories not activated).
 879       */
 880      public function test_find_category_idnumber_no_nested() {
 881          global $DB;
 882  
 883          $this->imsplugin->set_config('categoryidnumber', true);
 884          $this->imsplugin->set_config('categoryseparator', '|');
 885          $catsep = trim($this->imsplugin->get_config('categoryseparator'));
 886  
 887          $topcatname = 'CAT-TOP';
 888          $subcatname = 'CAT-SUB';
 889          $topcatidnumber = 'ID-TOP';
 890          $subcatidnumber = 'ID-SUB';
 891  
 892          $cattop = $this->getDataGenerator()->create_category(array('name' => $topcatname, 'idnumber' => $topcatidnumber));
 893          $catsub = $this->getDataGenerator()->create_category(array('name' => $subcatname, 'idnumber' => $subcatidnumber,
 894                  'parent' => $cattop->id));
 895          $prevcats = $DB->count_records('course_categories');
 896  
 897          $course = new \stdClass();
 898          $course->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 899          $course->idnumber = 'id';
 900          $course->imsshort = 'description_short';
 901          $course->imslong = 'description_long';
 902          $course->imsfull = 'description_full';
 903          $course->category[] = $subcatname . $catsep . $subcatidnumber;
 904  
 905          $this->set_xml_file(false, array($course));
 906          $this->imsplugin->cron();
 907  
 908          $newcats = $DB->count_records('course_categories');
 909  
 910          // Check that no new category was not created.
 911          $this->assertEquals($prevcats, $newcats);
 912  
 913          $dbcourse = $DB->get_record('course', array('idnumber' => $course->idnumber));
 914          $this->assertFalse(!$dbcourse);
 915          $this->assertEquals($dbcourse->category, $catsub->id);
 916  
 917      }
 918  
 919      /**
 920       * Test that category with idnumber is not created if name is missing (nested categories not activated).
 921       */
 922      public function test_category_idnumber_missing_name_no_nested() {
 923          global $DB;
 924  
 925          $this->imsplugin->set_config('categoryidnumber', true);
 926          $this->imsplugin->set_config('categoryseparator', '|');
 927          $catsep = trim($this->imsplugin->get_config('categoryseparator'));
 928  
 929          $catidnumber = '01';
 930  
 931          $course = new \stdClass();
 932          $course->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
 933          $course->idnumber = 'id1';
 934          $course->imsshort = 'description_short';
 935          $course->imslong = 'description_long';
 936          $course->imsfull = 'description_full';
 937          $course->category[] = '' . $catsep . $catidnumber;
 938  
 939          $this->set_xml_file(false, array($course));
 940          $this->imsplugin->cron();
 941  
 942          // Check category was not created.
 943          $catid = $DB->get_record('course_categories', array('idnumber' => $catidnumber));
 944          $this->assertFalse($catid);
 945  
 946          // Check course was put in default category.
 947          $defaultcat = \core_course_category::get_default();
 948          $dbcourse = $DB->get_record('course', array('idnumber' => $course->idnumber), '*', MUST_EXIST);
 949          $this->assertEquals($dbcourse->category, $defaultcat->id);
 950  
 951      }
 952  
 953      /**
 954       * Sets the plugin configuration for testing
 955       */
 956      public function set_test_config() {
 957          $this->imsplugin->set_config('mailadmins', false);
 958          $this->imsplugin->set_config('prev_path', '');
 959          $this->imsplugin->set_config('createnewusers', true);
 960          $this->imsplugin->set_config('imsupdateusers', true);
 961          $this->imsplugin->set_config('createnewcourses', true);
 962          $this->imsplugin->set_config('updatecourses', true);
 963          $this->imsplugin->set_config('createnewcategories', true);
 964          $this->imsplugin->set_config('categoryseparator', '');
 965          $this->imsplugin->set_config('categoryidnumber', false);
 966          $this->imsplugin->set_config('nestedcategories', false);
 967      }
 968  
 969      /**
 970       * Creates an IMS enterprise XML file and adds it's path to config settings.
 971       *
 972       * @param bool|array $users false or array of users stdClass
 973       * @param bool|array $courses false or of courses stdClass
 974       */
 975      public function set_xml_file($users = false, $courses = false) {
 976  
 977          $xmlcontent = '<enterprise>';
 978  
 979          // Users.
 980          if (!empty($users)) {
 981              foreach ($users as $user) {
 982                  $xmlcontent .= '
 983    <person';
 984  
 985                  // Optional recstatus (1=add, 2=update, 3=delete).
 986                  if (!empty($user->recstatus)) {
 987                      $xmlcontent .= ' recstatus="'.$user->recstatus.'"';
 988                  }
 989  
 990                  $xmlcontent .= '>
 991      <sourcedid>
 992        <source>TestSource</source>
 993        <id>'.$user->username.'</id>
 994      </sourcedid>
 995      <userid';
 996  
 997                  // Optional authentication type.
 998                  if (!empty($user->auth)) {
 999                      $xmlcontent .= ' authenticationtype="'.$user->auth.'"';
1000                  }
1001  
1002                  $xmlcontent .= '>'.$user->username.'</userid>
1003      <name>
1004        <fn>'.$user->firstname.' '.$user->lastname.'</fn>
1005        <n>
1006          <family>'.$user->lastname.'</family>
1007          <given>'.$user->firstname.'</given>
1008        </n>
1009      </name>
1010      <email>'.$user->email.'</email>
1011    </person>';
1012              }
1013          }
1014  
1015          // Courses.
1016          // Mapping based on default course attributes - IMS group tags mapping.
1017          if (!empty($courses)) {
1018              foreach ($courses as $course) {
1019  
1020                  $xmlcontent .= '
1021    <group';
1022  
1023                  // Optional recstatus (1=add, 2=update, 3=delete).
1024                  if (!empty($course->recstatus)) {
1025                      $xmlcontent .= ' recstatus="'.$course->recstatus.'"';
1026                  }
1027  
1028                  $xmlcontent .= '>
1029      <sourcedid>
1030        <source>TestSource</source>
1031        <id>'.$course->idnumber.'</id>
1032      </sourcedid>
1033      <description>';
1034  
1035                  // Optional to test course attributes mappings.
1036                  if (!empty($course->imsshort)) {
1037                      $xmlcontent .= '
1038        <short>'.$course->imsshort.'</short>';
1039                  }
1040  
1041                  // Optional to test course attributes mappings.
1042                  if (!empty($course->imslong)) {
1043                      $xmlcontent .= '
1044        <long>'.$course->imslong.'</long>';
1045                  }
1046  
1047                  // Optional to test course attributes mappings.
1048                  if (!empty($course->imsfull)) {
1049                      $xmlcontent .= '
1050        <full>'.$course->imsfull.'</full>';
1051                  }
1052  
1053                  // The orgunit tag value is used by moodle as category name.
1054                  $xmlcontent .= '
1055      </description>
1056      <org>';
1057                  // Optional category name.
1058                  if (isset($course->category) && !empty($course->category)) {
1059                      foreach ($course->category as $category) {
1060                          $xmlcontent .= '
1061        <orgunit>'.$category.'</orgunit>';
1062                      }
1063                  }
1064  
1065                  $xmlcontent .= '
1066      </org>
1067    </group>';
1068              }
1069          }
1070  
1071          $xmlcontent .= '
1072  </enterprise>';
1073  
1074          // Creating the XML file.
1075          $filename = 'ims_' . rand(1000, 9999) . '.xml';
1076          $tmpdir = make_temp_directory('enrol_imsenterprise');
1077          $xmlfilepath = $tmpdir . '/' . $filename;
1078          file_put_contents($xmlfilepath, $xmlcontent);
1079  
1080          // Setting the file path in CFG.
1081          $this->imsplugin->set_config('imsfilelocation', $xmlfilepath);
1082      }
1083  
1084      /**
1085       * IMS Enterprise enrolment task test.
1086       */
1087      public function test_imsenterprise_cron_task() {
1088          global $DB;
1089          $prevnusers = $DB->count_records('user');
1090  
1091          $user1 = new \stdClass();
1092          $user1->username = 'u1';
1093          $user1->email = 'u1@example.com';
1094          $user1->firstname = 'U';
1095          $user1->lastname = '1';
1096  
1097          $users = array($user1);
1098          $this->set_xml_file($users);
1099  
1100          $task = new cron_task();
1101          $task->execute();
1102  
1103          $this->assertEquals(($prevnusers + 1), $DB->count_records('user'));
1104      }
1105  }