MDL-31356 enrol_imsenterprise: Continue development for added features
authorSerge Gauthier <serge.gauthier.2@umontreal.ca>
Thu, 7 Jul 2016 18:32:23 +0000 (14:32 -0400)
committerIssam Taboubi <issam.taboubi@umontreal.ca>
Thu, 14 Jul 2016 13:36:03 +0000 (09:36 -0400)
    * Add the possibility to make it works as before (with categrory name or idnumber).
    * Add possibility to specify nested categories by name or idnumber.
    * Fix error in core_course_courselib_testcase::test_course_created_event because dependency with imsenterprise

course/tests/courselib_test.php
enrol/imsenterprise/lang/en/enrol_imsenterprise.php
enrol/imsenterprise/lib.php
enrol/imsenterprise/settings.php
enrol/imsenterprise/tests/imsenterprise_test.php
enrol/imsenterprise/version.php

index f0d4869..4c9ed73 100644 (file)
@@ -1622,6 +1622,7 @@ class core_course_courselib_testcase extends advanced_testcase {
         ob_end_clean();
 
         // Create the XML file we want to use.
+        $course->category = (array)$course->category;
         $imstestcase = new enrol_imsenterprise_testcase();
         $imstestcase->imsplugin = enrol_get_plugin('imsenterprise');
         $imstestcase->set_test_config();
@@ -1632,7 +1633,13 @@ class core_course_courselib_testcase extends advanced_testcase {
         $imstestcase->imsplugin->cron();
         $events = $sink->get_events();
         $sink->close();
-        $event = $events[0];
+        $event = null;
+        foreach ($events as $eventinfo) {
+            if ($eventinfo instanceof \core\event\course_created ) {
+                $event = $eventinfo;
+                break;
+            }
+        }
 
         // Validate the event triggered is \core\event\course_created. There is no need to validate the other values
         // as they have already been validated in the previous steps. Here we only want to make sure that when the
index c87ccd1..4cebfe9 100644 (file)
@@ -26,8 +26,10 @@ $string['aftersaving...'] = 'Once you have saved your settings, you may wish to'
 $string['allowunenrol'] = 'Allow the IMS data to <strong>unenrol</strong> students/teachers';
 $string['allowunenrol_desc'] = 'If enabled, course enrolments will be removed when specified in the Enterprise data.';
 $string['basicsettings'] = 'Basic settings';
+$string['categoryidnumber'] = 'Allow category idnumber';
+$string['categoryidnumber_desc'] = 'If enabled IMS Enterprise will create category with idnumber';
 $string['categoryseparator'] = 'Category Separator Character';
-$string['categoryseparator_desc'] = 'You can create nested categories (if you allow category creation).';
+$string['categoryseparator_desc'] = 'Required when "Category idnumber" is enabled. Character to separate the category name and idnumber.';
 $string['coursesettings'] = 'Course data options';
 $string['createnewcategories'] = 'Create new (hidden) course categories if not found in Moodle';
 $string['createnewcategories_desc'] = 'If the <org><orgunit> element is present in a course\'s incoming data, its content will be used to specify a category if the course is to be created from scratch. The plugin will NOT re-categorise existing courses.
@@ -57,6 +59,8 @@ $string['mailadmins'] = 'Notify admin by email';
 $string['mailusers'] = 'Notify users by email';
 $string['messageprovider:imsenterprise_enrolment'] = 'IMS Enterprise enrolment messages';
 $string['miscsettings'] = 'Miscellaneous';
+$string['nestedcategories'] = 'Allow nested categories';
+$string['nestedcategories_desc'] = 'If enabled IMS Enterprise will create nested categories';
 $string['pluginname'] = 'IMS Enterprise file';
 $string['pluginname_desc'] = 'This method will repeatedly check for and process a specially-formatted text file in the location that you specify.  The file must follow the IMS Enterprise specifications containing person, group, and membership XML elements.';
 $string['processphoto'] = 'Add user photo data to profile';
index 3dc1165..ede2700 100644 (file)
@@ -29,7 +29,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 require_once($CFG->dirroot.'/group/lib.php');
-
+require_once($CFG->dirroot.'/lib/coursecatlib.php');
 
 /**
  * IMS Enterprise file enrolment plugin.
@@ -79,6 +79,11 @@ class enrol_imsenterprise_plugin extends enrol_plugin {
      */
     protected $rolemappings;
 
+    /**
+     * @var $defaultcategoryid id of default category.
+     */
+    protected $defaultcategoryid;
+
     /**
      * Read in an IMS Enterprise file.
      * Originally designed to handle v1.1 files but should be able to handle
@@ -108,6 +113,8 @@ class enrol_imsenterprise_plugin extends enrol_plugin {
             $this->logfp = fopen($logtolocation, 'a');
         }
 
+        $this->defaultcategoryid = null;
+
         $fileisnew = false;
         if ( file_exists($filename) ) {
             core_php_time_limit::raise();
@@ -118,6 +125,9 @@ class enrol_imsenterprise_plugin extends enrol_plugin {
             $this->log_line('Found file '.$filename);
             $this->xmlcache = '';
 
+            $categoryseparator = trim($this->get_config('categoryseparator'));
+            $categoryidnumber = $this->get_config('categoryidnumber');
+
             // Make sure we understand how to map the IMS-E roles to Moodle roles.
             $this->load_role_mappings();
             // Make sure we understand how to map the IMS-E course names to Moodle course names.
@@ -128,7 +138,9 @@ class enrol_imsenterprise_plugin extends enrol_plugin {
 
             // Decide if we want to process the file (based on filepath, modification time, and MD5 hash)
             // This is so we avoid wasting the server's efforts processing a file unnecessarily.
-            if (empty($prevpath)  || ($filename != $prevpath)) {
+            if ($categoryidnumber && empty($categoryseparator)) {
+                $this->log_line('Category idnumber is enabled but category separator is not defined - skipping processing.');
+            } else if (empty($prevpath)  || ($filename != $prevpath)) {
                 $fileisnew = true;
             } else if (isset($prevtime) && ($filemtime <= $prevtime)) {
                 $this->log_line('File modification time is not more recent than last update - skipping processing.');
@@ -292,13 +304,6 @@ class enrol_imsenterprise_plugin extends enrol_plugin {
         $truncatecoursecodes    = $this->get_config('truncatecoursecodes');
         $createnewcourses       = $this->get_config('createnewcourses');
         $updatecourses          = $this->get_config('updatecourses');
-        $createnewcategories    = $this->get_config('createnewcategories');
-        $categoryseparator      = trim($this->get_config('categoryseparator'));
-
-        // Ensure a default is set for the category separator.
-        if (empty($categoryseparator)) {
-            $categoryseparator = '|';
-        }
 
         if ($createnewcourses) {
             require_once("$CFG->dirroot/course/lib.php");
@@ -325,9 +330,10 @@ class enrol_imsenterprise_plugin extends enrol_plugin {
             $group->full = trim($matches[1]);
         }
 
-        $matches = array();
-        if (preg_match('{<org>.*?<orgunit>(.*?)</orgunit>.*?</org>}is', $tagcontents, $matches)) {
-            $group->category = trim($matches[1]);
+        if (preg_match('{<org>(.*?)</org>}is', $tagcontents, $matchesorg)) {
+            if (preg_match_all('{<orgunit>(.*?)</orgunit>}is', $matchesorg[1], $matchesorgunit)) {
+                $group->categories = array_map('trim', $matchesorgunit[1]);
+            }
         }
 
         $recstatus = ($this->get_recstatus($tagcontents, 'group'));
@@ -389,49 +395,8 @@ class enrol_imsenterprise_plugin extends enrol_plugin {
                         $course->enablecompletion = $courseconfig->enablecompletion;
                         // Insert default names for teachers/students, from the current language.
 
-                        // Handle course categorisation (taken from the group.org.orgunit field if present).
-                        if (strlen($group->category) > 0) {
-                            $sep = '{\\'.$categoryseparator.'}';
-                            $matches = preg_split($sep, $group->category, -1, PREG_SPLIT_NO_EMPTY);
-
-                            // Categories can be nested.
-                            // For example: "Fall 2013|Biology" is the "Biology" category nested under the "Fall 2013" category.
-                            // Iterate through each category and create it if necessary.
-
-                            $catid = 0;
-                            $fullnestedcatname = '';
-                            foreach ($matches as $catname) {
-                                $catname = trim($catname);
-                                if (strlen($fullnestedcatname)) {
-                                    $fullnestedcatname .= ' / ';
-                                }
-                                $fullnestedcatname .= $catname;
-                                $parentid = $catid;
-                                if ($catid = $DB->get_field('course_categories', 'id',
-                                        array('name' => $catname, 'parent' => $parentid))) {
-                                    $course->category = $catid;
-                                    continue; // This category already exists.
-                                }
-                                if ($createnewcategories) {
-                                    // Else if we're allowed to create new categories, let's create this one.
-                                    $newcat = new stdClass();
-                                    $newcat->name = $catname;
-                                    $newcat->visible = 0;
-                                    $newcat->parent = $parentid;
-                                    $catid = $DB->insert_record('course_categories', $newcat);
-                                    $this->log_line("Created new (hidden) category '$fullnestedcatname'");
-                                    $course->category = $catid;
-                                } else {
-                                    // If not found and not allowed to create, stick with default.
-                                    $this->log_line('Category '.$group->category.' not found in Moodle database, so using '.
-                                            'default category instead.');
-                                    $course->category = $this->get_default_category_id();
-                                    break;
-                                }
-                            }
-                        } else {
-                            $course->category = $this->get_default_category_id();
-                        }
+                        // Handle course categorisation (taken from the group.org.orgunit or group.org.id fields if present).
+                        $course->category = $this->get_category_from_group($group->categories);
 
                         $course->startdate = time();
                         // Choose a sort order that puts us at the start of the list!
@@ -480,7 +445,7 @@ class enrol_imsenterprise_plugin extends enrol_plugin {
 
     /**
      * Process the person tag. This defines a Moodle user.
-     * 
+     *
      * @param string $tagcontents The raw contents of the XML element
      */
     protected function process_person_tag($tagcontents) {
@@ -591,7 +556,7 @@ class enrol_imsenterprise_plugin extends enrol_plugin {
                 $this->log_line("Ignoring update request for user $person->username");
             }
 
-        } else { // Add record.
+        } else { // Add or update record.
 
             // If the user exists (matching sourcedid) then we don't need to do anything.
             if (!$DB->get_field('user', 'id', array('idnumber' => $person->idnumber)) && $createnewusers) {
@@ -909,14 +874,101 @@ class enrol_imsenterprise_plugin extends enrol_plugin {
         global $CFG;
         require_once($CFG->libdir.'/coursecatlib.php');
 
-        static $defaultcategoryid = null;
-
-        if ($defaultcategoryid === null) {
+        if ($this->defaultcategoryid === null) {
             $category = coursecat::get_default();
-            $defaultcategoryid = $category->id;
+            $this->defaultcategoryid = $category->id;
+        }
+
+        return $this->defaultcategoryid;
+    }
+
+    /**
+     * Find the category using idnumber or name.
+     *
+     * @param array $categories List of categories
+     *
+     * @return int id of category found.
+     */
+    private function get_category_from_group($categories) {
+        global $DB;
+
+        if (empty($categories)) {
+            $catid = $this->get_default_category_id();
+        } else {
+            $createnewcategories = $this->get_config('createnewcategories');
+            $categoryseparator = trim($this->get_config('categoryseparator'));
+            $nestedcategories = trim($this->get_config('nestedcategories'));
+            $searchbyidnumber = trim($this->get_config('categoryidnumber'));
+
+            if (!empty($categoryseparator)) {
+                $sep = '{\\'.$categoryseparator.'}';
+            }
+
+            $catid = 0;
+            $fullnestedcatname = '';
+
+            foreach ($categories as $categoryinfo) {
+                if ($searchbyidnumber) {
+                    $values = preg_split($sep, $categoryinfo, -1, PREG_SPLIT_NO_EMPTY);
+                    if (count($values) < 2) {
+                        $this->log_line('Category ' . $categoryinfo . ' missing name or idnumber. Using default category instead.');
+                        $catid = $this->get_default_category_id();
+                        break;
+                    }
+                    $categoryname = $values[0];
+                    $categoryidnumber = $values[1];
+                } else {
+                    $categoryname = $categoryinfo;
+                    $categoryidnumber = null;
+                    if (empty($categoryname)) {
+                        $this->log_line('Category ' . $categoryinfo . ' missing name. Using default category instead.');
+                        $catid = $this->get_default_category_id();
+                        break;
+                    }
+                }
+
+                if (!empty($fullnestedcatname)) {
+                    $fullnestedcatname .= ' / ';
+                }
+
+                $fullnestedcatname .= $categoryname;
+                $parentid = $catid;
+
+                // Check if category exist.
+                $params = array();
+                if ($searchbyidnumber) {
+                    $params['idnumber'] = $categoryidnumber;
+                } else {
+                    $params['name'] = $categoryname;
+                }
+                if ($nestedcategories) {
+                    $params['parent'] = $parentid;
+                }
+
+                if ($catid = $DB->get_field('course_categories', 'id', $params)) {
+                    continue; // This category already exists.
+                }
+
+                // If we're allowed to create new categories, let's create this one.
+                if ($createnewcategories) {
+                    $newcat = new stdClass();
+                    $newcat->name = $categoryname;
+                    $newcat->visible = 0;
+                    $newcat->parent = $parentid;
+                    $newcat->idnumber = $categoryidnumber;
+                    $newcat = coursecat::create($newcat);
+                    $catid = $newcat->id;
+                    $this->log_line("Created new (hidden) category '$fullnestedcatname'");
+                } else {
+                    // If not found and not allowed to create, stick with default.
+                    $this->log_line('Category ' . $categoryinfo . ' not found in Moodle database. Using default category instead.');
+                    $catid = $this->get_default_category_id();
+                    break;
+                }
+            }
         }
 
-        return $defaultcategoryid;
+        return $catid;
     }
 
     /**
index a20d92a..d55b8ee 100644 (file)
@@ -98,8 +98,14 @@ if ($ADMIN->fulltree) {
         get_string('createnewcategories', 'enrol_imsenterprise'), get_string('createnewcategories_desc', 'enrol_imsenterprise'),
         0));
 
+    $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/nestedcategories',
+        get_string('nestedcategories', 'enrol_imsenterprise'), get_string('nestedcategories_desc', 'enrol_imsenterprise'), 0));
+
+    $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/categoryidnumber',
+        get_string('categoryidnumber', 'enrol_imsenterprise'), get_string('categoryidnumber_desc', 'enrol_imsenterprise'), 0));
+
     $settings->add(new admin_setting_configtext('enrol_imsenterprise/categoryseparator',
-        get_string('categoryseparator', 'enrol_imsenterprise'), get_string('categoryseparator_desc', 'enrol_imsenterprise'), '|',
+        get_string('categoryseparator', 'enrol_imsenterprise'), get_string('categoryseparator_desc', 'enrol_imsenterprise'), '',
         PARAM_TEXT, 3));
 
     $settings->add(new admin_setting_configcheckbox('enrol_imsenterprise/imsunenrol',
index 32616cd..e3ed112 100644 (file)
@@ -242,6 +242,8 @@ class enrol_imsenterprise_testcase extends advanced_testcase {
         // Default mapping according to default course attributes - IMS description tags mapping.
         $course1->imsshort = $course1->fullname;
         $course2->imsshort = $course2->fullname;
+        unset($course1->category);
+        unset($course2->category);
 
         $prevncourses = $DB->count_records('course');
 
@@ -264,13 +266,13 @@ class enrol_imsenterprise_testcase extends advanced_testcase {
         $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
         $course1->idnumber = 'id1';
         $course1->imsshort = 'id1';
-        $course1->category = 'DEFAULT CATNAME';
+        $course1->category[] = 'DEFAULT CATNAME';
 
         $course2 = new StdClass();
         $course2->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
         $course2->idnumber = 'id2';
         $course2->imsshort = 'id2';
-        $course2->category = 'DEFAULT CATNAME';
+        $course2->category[] = 'DEFAULT CATNAME';
 
         $courses = array($course1, $course2);
         $this->set_xml_file(false, $courses);
@@ -295,13 +297,13 @@ class enrol_imsenterprise_testcase extends advanced_testcase {
         $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
         $course1->idnumber = 'id1';
         $course1->imsshort = 'id1';
-        $course1->category = 'DEFAULT CATNAME';
+        $course1->category[] = 'DEFAULT CATNAME';
 
         $course2 = new StdClass();
         $course2->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
         $course2->idnumber = 'id2';
         $course2->imsshort = 'id2';
-        $course2->category = 'DEFAULT CATNAME';
+        $course2->category[] = 'DEFAULT CATNAME';
 
         $courses = array($course1, $course2);
         $this->set_xml_file(false, $courses);
@@ -329,7 +331,7 @@ class enrol_imsenterprise_testcase extends advanced_testcase {
         $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
         $course1->idnumber = '';
         $course1->imsshort = 'id1';
-        $course1->category = 'DEFAULT CATNAME';
+        $course1->category[] = 'DEFAULT CATNAME';
 
         $this->set_xml_file(false, array($course1));
         $this->imsplugin->cron();
@@ -353,7 +355,7 @@ class enrol_imsenterprise_testcase extends advanced_testcase {
         $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
         $course1->idnumber = '123456789';
         $course1->imsshort = 'id1';
-        $course1->category = 'DEFAULT CATNAME';
+        $course1->category[] = 'DEFAULT CATNAME';
 
         $this->set_xml_file(false, array($course1));
         $this->imsplugin->cron();
@@ -383,7 +385,7 @@ class enrol_imsenterprise_testcase extends advanced_testcase {
         $course1 = new stdClass();
         $course1->idnumber = 'id1';
         $course1->imsshort = 'id1';
-        $course1->category = '';
+        $course1->category[] = '';
         $this->set_xml_file(false, array($course1));
         $this->imsplugin->cron();
 
@@ -410,7 +412,7 @@ class enrol_imsenterprise_testcase extends advanced_testcase {
         $course1->imsshort = 'description_short1';
         $course1->imslong = 'description_long';
         $course1->imsfull = 'description_full';
-        $course1->category = 'DEFAULT CATNAME';
+        $course1->category[] = 'DEFAULT CATNAME';
 
         $this->set_xml_file(false, array($course1));
         $this->imsplugin->cron();
@@ -432,7 +434,7 @@ class enrol_imsenterprise_testcase extends advanced_testcase {
         $course2->imsshort = 'description_short2';
         $course2->imslong = 'description_long';
         $course2->imsfull = 'description_full';
-        $course2->category = 'DEFAULT CATNAME';
+        $course2->category[] = 'DEFAULT CATNAME';
 
         $this->set_xml_file(false, array($course2));
         $this->imsplugin->cron();
@@ -452,7 +454,7 @@ class enrol_imsenterprise_testcase extends advanced_testcase {
         $course3->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
         $course3->idnumber = 'id3';
         $course3->imsshort = 'description_short3';
-        $course3->category = 'DEFAULT CATNAME';
+        $course3->category[] = 'DEFAULT CATNAME';
 
         $this->set_xml_file(false, array($course3));
         $this->imsplugin->cron();
@@ -476,7 +478,7 @@ class enrol_imsenterprise_testcase extends advanced_testcase {
         $course4->idnumber = 'id4';
         $course4->imsshort = 'id4';
         $course4->imsfull = 'id4';
-        $course4->category = 'DEFAULT CATNAME';
+        $course4->category[] = 'DEFAULT CATNAME';
 
         $this->set_xml_file(false, array($course4));
         $this->imsplugin->cron();
@@ -486,6 +488,7 @@ class enrol_imsenterprise_testcase extends advanced_testcase {
         $course4u->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_UPDATE;
         $course4u->imsshort = 'description_short_updated';
         $course4u->imsfull = 'description_full_updated';
+        unset($course4u->category);
 
         $this->set_xml_file(false, array($course4u));
         $this->imsplugin->cron();
@@ -507,7 +510,7 @@ class enrol_imsenterprise_testcase extends advanced_testcase {
         $course8->idnumber = 'id8';
         $course8->imsshort = 'id8';
         $course8->imsfull = 'id8';
-        $course8->category = 'DEFAULT CATNAME';
+        $course8->category[] = 'DEFAULT CATNAME';
 
         $this->set_xml_file(false, array($course8));
         $this->imsplugin->cron();
@@ -516,6 +519,7 @@ class enrol_imsenterprise_testcase extends advanced_testcase {
         $this->assertEquals($course8d->visible, 1);
 
         $course8d->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_DELETE;
+        unset($course8d->category);
 
         $this->set_xml_file(false, array($course8d));
         $this->imsplugin->cron();
@@ -527,25 +531,25 @@ class enrol_imsenterprise_testcase extends advanced_testcase {
 
 
     /**
-     * Nested categories during course creation
+     * Nested categories with name during course creation
      */
     public function test_nested_categories() {
         global $DB;
 
-        $catsep = trim($this->imsplugin->get_config('categoryseparator'));
+        $this->imsplugin->set_config('nestedcategories', true);
 
         $topcat = 'DEFAULT CATNAME';
         $subcat = 'DEFAULT SUB CATNAME';
 
-        $fullcat = $topcat.$catsep.$subcat;
-
         $course5 = new StdClass();
         $course5->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
         $course5->idnumber = 'id5';
         $course5->imsshort = 'description_short';
         $course5->imslong = 'description_long';
         $course5->imsfull = 'description_full';
-        $course5->category = $fullcat;
+        $course5->category = array();
+        $course5->category[] = $topcat;
+        $course5->category[] = $subcat;
 
         $this->set_xml_file(false, array($course5));
         $this->imsplugin->cron();
@@ -556,23 +560,18 @@ class enrol_imsenterprise_testcase extends advanced_testcase {
         $this->assertTrue(isset($subcatid));
         $this->assertTrue($subcatid > 0);
 
-        // Change the category separator character.
-        $this->imsplugin->set_config('categoryseparator', ':');
-
-        $catsep = trim($this->imsplugin->get_config('categoryseparator'));
-
         $topcat = 'DEFAULT CATNAME';
         $subcat = 'DEFAULT SUB CATNAME TEST2';
 
-        $fullcat = $topcat.$catsep.$subcat;
-
         $course6 = new StdClass();
         $course6->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
         $course6->idnumber = 'id6';
         $course6->imsshort = 'description_short';
         $course6->imslong = 'description_long';
         $course6->imsfull = 'description_full';
-        $course6->category = $fullcat;
+        $course6->category = array();
+        $course6->category[] = $topcat;
+        $course6->category[] = $subcat;
 
         $this->set_xml_file(false, array($course6));
         $this->imsplugin->cron();
@@ -586,25 +585,24 @@ class enrol_imsenterprise_testcase extends advanced_testcase {
 
 
     /**
-     * Test that duplicate nested categories are not created
+     * Test that duplicate nested categories with name are not created
      */
     public function test_nested_categories_for_dups() {
         global $DB;
 
-        $catsep = trim($this->imsplugin->get_config('categoryseparator'));
+        $this->imsplugin->set_config('nestedcategories', true);
 
         $topcat = 'DEFAULT CATNAME';
         $subcat = 'DEFAULT SUB CATNAME DUPTEST';
 
-        $fullcat = $topcat.$catsep.$subcat;
-
         $course7 = new StdClass();
         $course7->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
         $course7->idnumber = 'id7';
         $course7->imsshort = 'description_short';
         $course7->imslong = 'description_long';
         $course7->imsfull = 'description_full';
-        $course7->category = $fullcat;
+        $course7->category[] = $topcat;
+        $course7->category[] = $subcat;
 
         $this->set_xml_file(false, array($course7));
         $this->imsplugin->cron();
@@ -617,7 +615,8 @@ class enrol_imsenterprise_testcase extends advanced_testcase {
         $course8->imsshort = 'description_short';
         $course8->imslong = 'description_long';
         $course8->imsfull = 'description_full';
-        $course8->category = $fullcat;
+        $course8->category[] = $topcat;
+        $course8->category[] = $subcat;
 
         $this->set_xml_file(false, array($course8));
         $this->imsplugin->cron();
@@ -625,6 +624,336 @@ class enrol_imsenterprise_testcase extends advanced_testcase {
         $this->assertEquals($prevncategories, $DB->count_records('course_categories'));
     }
 
+    /**
+     * Nested categories with idnumber during course creation
+     */
+    public function test_nested_categories_idnumber() {
+        global $DB;
+
+        $this->imsplugin->set_config('nestedcategories', true);
+        $this->imsplugin->set_config('categoryidnumber', true);
+        $this->imsplugin->set_config('categoryseparator', '|');
+
+        $catsep = trim($this->imsplugin->get_config('categoryseparator'));
+
+        $topcatname = 'DEFAULT CATNAME';
+        $subcatname = 'DEFAULT SUB CATNAME';
+        $topcatidnumber = '01';
+        $subcatidnumber = '0101';
+
+        $topcat = $topcatname.$catsep.$topcatidnumber;
+        $subcat = $subcatname.$catsep.$subcatidnumber;
+
+        $course1 = new StdClass();
+        $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
+        $course1->idnumber = 'id5';
+        $course1->imsshort = 'description_short';
+        $course1->imslong = 'description_long';
+        $course1->imsfull = 'description_full';
+        $course1->category[] = $topcat;
+        $course1->category[] = $subcat;
+
+        $this->set_xml_file(false, array($course1));
+        $this->imsplugin->cron();
+
+        $parentcatid = $DB->get_field('course_categories', 'id', array('idnumber' => $topcatidnumber));
+        $subcatid = $DB->get_field('course_categories', 'id', array('idnumber' => $subcatidnumber, 'parent' => $parentcatid));
+
+        $this->assertTrue(isset($subcatid));
+        $this->assertTrue($subcatid > 0);
+
+        // Change the category separator character.
+        $this->imsplugin->set_config('categoryseparator', ':');
+
+        $catsep = trim($this->imsplugin->get_config('categoryseparator'));
+
+        $topcatname = 'DEFAULT CATNAME';
+        $subcatname = 'DEFAULT SUB CATNAME TEST2';
+        $topcatidnumber = '01';
+        $subcatidnumber = '0102';
+
+        $topcat = $topcatname.$catsep.$topcatidnumber;
+        $subcat = $subcatname.$catsep.$subcatidnumber;
+
+        $course2 = new StdClass();
+        $course2->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
+        $course2->idnumber = 'id6';
+        $course2->imsshort = 'description_short';
+        $course2->imslong = 'description_long';
+        $course2->imsfull = 'description_full';
+        $course2->category[] = $topcat;
+        $course2->category[] = $subcat;
+
+        $this->set_xml_file(false, array($course2));
+        $this->imsplugin->cron();
+
+        $parentcatid = $DB->get_field('course_categories', 'id', array('idnumber' => $topcatidnumber));
+        $subcatid = $DB->get_field('course_categories', 'id', array('idnumber' => $subcatidnumber, 'parent' => $parentcatid));
+
+        $this->assertTrue(isset($subcatid));
+        $this->assertTrue($subcatid > 0);
+    }
+
+    /**
+     * Test that duplicate nested categories with idnumber are not created
+     */
+    public function test_nested_categories_idnumber_for_dups() {
+        global $DB;
+
+        $this->imsplugin->set_config('nestedcategories', true);
+        $this->imsplugin->set_config('categoryidnumber', true);
+        $this->imsplugin->set_config('categoryseparator', '|');
+
+        $catsep = trim($this->imsplugin->get_config('categoryseparator'));
+
+        $topcatname = 'DEFAULT CATNAME';
+        $subcatname = 'DEFAULT SUB CATNAME';
+        $topcatidnumber = '01';
+        $subcatidnumber = '0101';
+
+        $topcat = $topcatname.$catsep.$topcatidnumber;
+        $subcat = $subcatname.$catsep.$subcatidnumber;
+
+        $course1 = new StdClass();
+        $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
+        $course1->idnumber = 'id1';
+        $course1->imsshort = 'description_short';
+        $course1->imslong = 'description_long';
+        $course1->imsfull = 'description_full';
+        $course1->category[] = $topcat;
+        $course1->category[] = $subcat;
+
+        $this->set_xml_file(false, array($course1));
+        $this->imsplugin->cron();
+
+        $prevncategories = $DB->count_records('course_categories');
+
+        $course2 = new StdClass();
+        $course2->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
+        $course2->idnumber = 'id2';
+        $course2->imsshort = 'description_short';
+        $course2->imslong = 'description_long';
+        $course2->imsfull = 'description_full';
+        $course2->category[] = $topcat;
+        $course2->category[] = $subcat;
+
+        $this->set_xml_file(false, array($course2));
+        $this->imsplugin->cron();
+
+        $this->assertEquals($prevncategories, $DB->count_records('course_categories'));
+    }
+
+    /**
+     * Test that nested categories with idnumber is not created if name is missing
+     */
+    public function test_categories_idnumber_missing_name() {
+        global $DB, $CFG;
+
+        $this->imsplugin->set_config('nestedcategories', true);
+        $this->imsplugin->set_config('categoryidnumber', true);
+        $this->imsplugin->set_config('categoryseparator', '|');
+        $catsep = trim($this->imsplugin->get_config('categoryseparator'));
+
+        $topcatname = 'DEFAULT CATNAME';
+        $subcatname = '';
+        $topcatidnumber = '01';
+        $subcatidnumber = '0101';
+
+        $topcat = $topcatname.$catsep.$topcatidnumber;
+        $subcat = $subcatname.$catsep.$subcatidnumber;
+
+        $course1 = new StdClass();
+        $course1->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
+        $course1->idnumber = 'id1';
+        $course1->imsshort = 'description_short';
+        $course1->imslong = 'description_long';
+        $course1->imsfull = 'description_full';
+        $course1->category[] = $topcat;
+        $course1->category[] = $subcat;
+
+        $this->set_xml_file(false, array($course1));
+        $this->imsplugin->cron();
+
+        // Check all categories except the last subcategory was created.
+        $parentcatid = $DB->get_field('course_categories', 'id', array('idnumber' => $topcatidnumber));
+        $this->assertTrue((boolean)$parentcatid);
+        $subcatid = $DB->get_field('course_categories', 'id', array('idnumber' => $subcatidnumber, 'parent' => $parentcatid));
+        $this->assertFalse((boolean)$subcatid);
+
+        // Check course was put in default category.
+        $defaultcat = coursecat::get_default();
+        $dbcourse = $DB->get_record('course', array('idnumber' => $course1->idnumber), '*', MUST_EXIST);
+        $this->assertEquals($dbcourse->category, $defaultcat->id);
+
+    }
+
+    /**
+     * Create category with name (nested categories not activated).
+     */
+    public function test_create_category_name_no_nested() {
+        global $DB;
+
+        $course = new StdClass();
+        $course->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
+        $course->idnumber = 'id';
+        $course->imsshort = 'description_short';
+        $course->imslong = 'description_long';
+        $course->imsfull = 'description_full';
+        $course->category[] = 'CATNAME';
+
+        $this->set_xml_file(false, array($course));
+        $this->imsplugin->cron();
+
+        $dbcat = $DB->get_record('course_categories', array('name' => $course->category[0]));
+        $this->assertFalse(!$dbcat);
+        $this->assertEquals($dbcat->parent, 0);
+
+        $dbcourse = $DB->get_record('course', array('idnumber' => $course->idnumber));
+        $this->assertFalse(!$dbcourse);
+        $this->assertEquals($dbcourse->category, $dbcat->id);
+
+    }
+
+    /**
+     * Find a category with name (nested categories not activated).
+     */
+    public function test_find_category_name_no_nested() {
+        global $DB;
+
+        $cattop = $this->getDataGenerator()->create_category(array('name' => 'CAT-TOP'));
+        $catsub = $this->getDataGenerator()->create_category(array('name' => 'CAT-SUB', 'parent' => $cattop->id));
+        $prevcats = $DB->count_records('course_categories');
+
+        $course = new StdClass();
+        $course->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
+        $course->idnumber = 'id';
+        $course->imsshort = 'description_short';
+        $course->imslong = 'description_long';
+        $course->imsfull = 'description_full';
+        $course->category[] = 'CAT-SUB';
+
+        $this->set_xml_file(false, array($course));
+        $this->imsplugin->cron();
+
+        $newcats = $DB->count_records('course_categories');
+
+        // Check that no new category was not created.
+        $this->assertEquals($prevcats, $newcats);
+
+        // Check course is associated to CAT-SUB.
+        $dbcourse = $DB->get_record('course', array('idnumber' => $course->idnumber));
+        $this->assertFalse(!$dbcourse);
+        $this->assertEquals($dbcourse->category, $catsub->id);
+
+    }
+
+    /**
+     * Create category with idnumber (nested categories not activated).
+     */
+    public function test_create_category_idnumber_no_nested() {
+        global $DB;
+
+        $this->imsplugin->set_config('categoryidnumber', true);
+        $this->imsplugin->set_config('categoryseparator', '|');
+        $catsep = trim($this->imsplugin->get_config('categoryseparator'));
+
+        $course = new StdClass();
+        $course->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
+        $course->idnumber = 'id';
+        $course->imsshort = 'description_short';
+        $course->imslong = 'description_long';
+        $course->imsfull = 'description_full';
+        $course->category[] = 'CATNAME'. $catsep .  'CATIDNUMBER';
+
+        $this->set_xml_file(false, array($course));
+        $this->imsplugin->cron();
+
+        $dbcat = $DB->get_record('course_categories', array('idnumber' => 'CATIDNUMBER'));
+        $this->assertFalse(!$dbcat);
+        $this->assertEquals($dbcat->parent, 0);
+        $this->assertEquals($dbcat->name, 'CATNAME');
+
+        $dbcourse = $DB->get_record('course', array('idnumber' => $course->idnumber));
+        $this->assertFalse(!$dbcourse);
+        $this->assertEquals($dbcourse->category, $dbcat->id);
+
+    }
+
+    /**
+     * Find a category with idnumber (nested categories not activated).
+     */
+    public function test_find_category_idnumber_no_nested() {
+        global $DB;
+
+        $this->imsplugin->set_config('categoryidnumber', true);
+        $this->imsplugin->set_config('categoryseparator', '|');
+        $catsep = trim($this->imsplugin->get_config('categoryseparator'));
+
+        $topcatname = 'CAT-TOP';
+        $subcatname = 'CAT-SUB';
+        $topcatidnumber = 'ID-TOP';
+        $subcatidnumber = 'ID-SUB';
+
+        $cattop = $this->getDataGenerator()->create_category(array('name' => $topcatname, 'idnumber' => $topcatidnumber));
+        $catsub = $this->getDataGenerator()->create_category(array('name' => $subcatname, 'idnumber' => $subcatidnumber,
+                'parent' => $cattop->id));
+        $prevcats = $DB->count_records('course_categories');
+
+        $course = new StdClass();
+        $course->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
+        $course->idnumber = 'id';
+        $course->imsshort = 'description_short';
+        $course->imslong = 'description_long';
+        $course->imsfull = 'description_full';
+        $course->category[] = $subcatname . $catsep . $subcatidnumber;
+
+        $this->set_xml_file(false, array($course));
+        $this->imsplugin->cron();
+
+        $newcats = $DB->count_records('course_categories');
+
+        // Check that no new category was not created.
+        $this->assertEquals($prevcats, $newcats);
+
+        $dbcourse = $DB->get_record('course', array('idnumber' => $course->idnumber));
+        $this->assertFalse(!$dbcourse);
+        $this->assertEquals($dbcourse->category, $catsub->id);
+
+    }
+
+    /**
+     * Test that category with idnumber is not created if name is missing (nested categories not activated).
+     */
+    public function test_category_idnumber_missing_name_no_nested() {
+        global $DB;
+
+        $this->imsplugin->set_config('categoryidnumber', true);
+        $this->imsplugin->set_config('categoryseparator', '|');
+        $catsep = trim($this->imsplugin->get_config('categoryseparator'));
+
+        $catidnumber = '01';
+
+        $course = new StdClass();
+        $course->recstatus = enrol_imsenterprise_plugin::IMSENTERPRISE_ADD;
+        $course->idnumber = 'id1';
+        $course->imsshort = 'description_short';
+        $course->imslong = 'description_long';
+        $course->imsfull = 'description_full';
+        $course->category[] = '' . $catsep . $catidnumber;
+
+        $this->set_xml_file(false, array($course));
+        $this->imsplugin->cron();
+
+        // Check category was not created.
+        $catid = $DB->get_record('course_categories', array('idnumber' => $catidnumber));
+        $this->assertFalse($catid);
+
+        // Check course was put in default category.
+        $defaultcat = coursecat::get_default();
+        $dbcourse = $DB->get_record('course', array('idnumber' => $course->idnumber), '*', MUST_EXIST);
+        $this->assertEquals($dbcourse->category, $defaultcat->id);
+
+    }
 
     /**
      * Sets the plugin configuration for testing
@@ -637,6 +966,9 @@ class enrol_imsenterprise_testcase extends advanced_testcase {
         $this->imsplugin->set_config('createnewcourses', true);
         $this->imsplugin->set_config('updatecourses', true);
         $this->imsplugin->set_config('createnewcategories', true);
+        $this->imsplugin->set_config('categoryseparator', '');
+        $this->imsplugin->set_config('categoryidnumber', false);
+        $this->imsplugin->set_config('nestedcategories', false);
     }
 
     /**
@@ -726,8 +1058,16 @@ class enrol_imsenterprise_testcase extends advanced_testcase {
                 // The orgunit tag value is used by moodle as category name.
                 $xmlcontent .= '
     </description>
-    <org>
-      <orgunit>'.$course->category.'</orgunit>
+    <org>';
+                // Optional category name.
+                if (isset($course->category) && !empty($course->category)) {
+                    foreach ($course->category as $category) {
+                        $xmlcontent .= '
+      <orgunit>'.$category.'</orgunit>';
+                    }
+                }
+
+                $xmlcontent .= '
     </org>
   </group>';
             }
index ca746c7..4b95292 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2016052300;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2016070600;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2016051900;        // Requires this Moodle version.
 $plugin->component = 'enrol_imsenterprise';