Merge branch 'MDL-69869-master' of git://github.com/ilya-catalyst/moodle
authorAndrew Nicols <andrew@nicols.co.uk>
Wed, 9 Dec 2020 03:08:06 +0000 (11:08 +0800)
committerAndrew Nicols <andrew@nicols.co.uk>
Wed, 9 Dec 2020 03:08:06 +0000 (11:08 +0800)
enrol/externallib.php
enrol/tests/externallib_test.php
enrol/upgrade.txt

index d0fbaa9..5189a3a 100644 (file)
@@ -679,28 +679,34 @@ class core_enrol_external extends external_api {
      */
     public static function get_enrolled_users_parameters() {
         return new external_function_parameters(
-            array(
+            [
                 'courseid' => new external_value(PARAM_INT, 'course id'),
                 'options'  => new external_multiple_structure(
                     new external_single_structure(
-                        array(
+                        [
                             'name'  => new external_value(PARAM_ALPHANUMEXT, 'option name'),
                             'value' => new external_value(PARAM_RAW, 'option value')
-                        )
+                        ]
                     ), 'Option names:
                             * withcapability (string) return only users with this capability. This option requires \'moodle/role:review\' on the course context.
                             * groupid (integer) return only users in this group id. If the course has groups enabled and this param
                                                 isn\'t defined, returns all the viewable users.
                                                 This option requires \'moodle/site:accessallgroups\' on the course context if the
                                                 user doesn\'t belong to the group.
-                            * onlyactive (integer) return only users with active enrolments and matching time restrictions. This option requires \'moodle/course:enrolreview\' on the course context.
+                            * onlyactive (integer) return only users with active enrolments and matching time restrictions.
+                                                This option requires \'moodle/course:enrolreview\' on the course context.
+                                                Please note that this option can\'t
+                                                be used together with onlysuspended (only one can be active).
+                            * onlysuspended (integer) return only suspended users. This option requires
+                                            \'moodle/course:enrolreview\' on the course context. Please note that this option can\'t
+                                                be used together with onlyactive (only one can be active).
                             * userfields (\'string, string, ...\') return only the values of these user fields.
                             * limitfrom (integer) sql limit from.
                             * limitnumber (integer) maximum number of returned users.
                             * sortby (string) sort by id, firstname or lastname. For ordering like the site does, use siteorder.
                             * sortdirection (string) ASC or DESC',
-                            VALUE_DEFAULT, array()),
-            )
+                            VALUE_DEFAULT, []),
+            ]
         );
     }
 
@@ -714,7 +720,7 @@ class core_enrol_external extends external_api {
      *                               }
      * @return array An array of users
      */
-    public static function get_enrolled_users($courseid, $options = array()) {
+    public static function get_enrolled_users($courseid, $options = []) {
         global $CFG, $USER, $DB;
 
         require_once($CFG->dirroot . '/course/lib.php');
@@ -722,67 +728,71 @@ class core_enrol_external extends external_api {
 
         $params = self::validate_parameters(
             self::get_enrolled_users_parameters(),
-            array(
+            [
                 'courseid'=>$courseid,
                 'options'=>$options
-            )
+            ]
         );
         $withcapability = '';
         $groupid        = 0;
         $onlyactive     = false;
-        $userfields     = array();
+        $onlysuspended  = false;
+        $userfields     = [];
         $limitfrom = 0;
         $limitnumber = 0;
         $sortby = 'us.id';
-        $sortparams = array();
+        $sortparams = [];
         $sortdirection = 'ASC';
         foreach ($options as $option) {
             switch ($option['name']) {
-            case 'withcapability':
-                $withcapability = $option['value'];
-                break;
-            case 'groupid':
-                $groupid = (int)$option['value'];
-                break;
-            case 'onlyactive':
-                $onlyactive = !empty($option['value']);
-                break;
-            case 'userfields':
-                $thefields = explode(',', $option['value']);
-                foreach ($thefields as $f) {
-                    $userfields[] = clean_param($f, PARAM_ALPHANUMEXT);
-                }
-                break;
-            case 'limitfrom' :
-                $limitfrom = clean_param($option['value'], PARAM_INT);
-                break;
-            case 'limitnumber' :
-                $limitnumber = clean_param($option['value'], PARAM_INT);
-                break;
-            case 'sortby':
-                $sortallowedvalues = array('id', 'firstname', 'lastname', 'siteorder');
-                if (!in_array($option['value'], $sortallowedvalues)) {
-                    throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' . $option['value'] . '),' .
-                        'allowed values are: ' . implode(',', $sortallowedvalues));
-                }
-                if ($option['value'] == 'siteorder') {
-                    list($sortby, $sortparams) = users_order_by_sql('us');
-                } else {
-                    $sortby = 'us.' . $option['value'];
-                }
-                break;
-            case 'sortdirection':
-                $sortdirection = strtoupper($option['value']);
-                $directionallowedvalues = array('ASC', 'DESC');
-                if (!in_array($sortdirection, $directionallowedvalues)) {
-                    throw new invalid_parameter_exception('Invalid value for sortdirection parameter
+                case 'withcapability':
+                    $withcapability = $option['value'];
+                    break;
+                case 'groupid':
+                    $groupid = (int)$option['value'];
+                    break;
+                case 'onlyactive':
+                    $onlyactive = !empty($option['value']);
+                    break;
+                case 'onlysuspended':
+                    $onlysuspended = !empty($option['value']);
+                    break;
+                case 'userfields':
+                    $thefields = explode(',', $option['value']);
+                    foreach ($thefields as $f) {
+                        $userfields[] = clean_param($f, PARAM_ALPHANUMEXT);
+                    }
+                    break;
+                case 'limitfrom' :
+                    $limitfrom = clean_param($option['value'], PARAM_INT);
+                    break;
+                case 'limitnumber' :
+                    $limitnumber = clean_param($option['value'], PARAM_INT);
+                    break;
+                case 'sortby':
+                    $sortallowedvalues = ['id', 'firstname', 'lastname', 'siteorder'];
+                    if (!in_array($option['value'], $sortallowedvalues)) {
+                        throw new invalid_parameter_exception('Invalid value for sortby parameter (value: ' .
+                            $option['value'] . '), allowed values are: ' . implode(',', $sortallowedvalues));
+                    }
+                    if ($option['value'] == 'siteorder') {
+                        list($sortby, $sortparams) = users_order_by_sql('us');
+                    } else {
+                        $sortby = 'us.' . $option['value'];
+                    }
+                    break;
+                case 'sortdirection':
+                    $sortdirection = strtoupper($option['value']);
+                    $directionallowedvalues = ['ASC', 'DESC'];
+                    if (!in_array($sortdirection, $directionallowedvalues)) {
+                        throw new invalid_parameter_exception('Invalid value for sortdirection parameter
                         (value: ' . $sortdirection . '),' . 'allowed values are: ' . implode(',', $directionallowedvalues));
-                }
-                break;
+                    }
+                    break;
             }
         }
 
-        $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
+        $course = $DB->get_record('course', ['id' => $courseid], '*', MUST_EXIST);
         $coursecontext = context_course::instance($courseid, IGNORE_MISSING);
         if ($courseid == SITEID) {
             $context = context_system::instance();
@@ -809,11 +819,12 @@ class core_enrol_external extends external_api {
             require_capability('moodle/site:accessallgroups', $coursecontext);
         }
         // to overwrite this option, you need course:enrolereview permission
-        if ($onlyactive) {
+        if ($onlyactive || $onlysuspended) {
             require_capability('moodle/course:enrolreview', $coursecontext);
         }
 
-        list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive);
+        list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive,
+        $onlysuspended);
         $ctxselect = ', ' . context_helper::get_preload_record_columns_sql('ctx');
         $ctxjoin = "LEFT JOIN {context} ctx ON (ctx.instanceid = u.id AND ctx.contextlevel = :contextlevel)";
         $enrolledparams['contextlevel'] = CONTEXT_USER;
@@ -829,7 +840,7 @@ class core_enrol_external extends external_api {
                 $enrolledparams = array_merge($enrolledparams, $groupparams);
             } else {
                 // User doesn't belong to any group, so he can't see any user. Return an empty array.
-                return array();
+                return [];
             }
         }
         $sql = "SELECT us.*, COALESCE(ul.timeaccess, 0) AS lastcourseaccess
@@ -845,7 +856,7 @@ class core_enrol_external extends external_api {
         $enrolledparams['courseid'] = $courseid;
 
         $enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams, $limitfrom, $limitnumber);
-        $users = array();
+        $users = [];
         foreach ($enrolledusers as $user) {
             context_helper::preload_from_record($user);
             if ($userdetails = user_get_user_details($user, $course, $userfields)) {
@@ -865,7 +876,7 @@ class core_enrol_external extends external_api {
     public static function get_enrolled_users_returns() {
         return new external_multiple_structure(
             new external_single_structure(
-                array(
+                [
                     'id'    => new external_value(PARAM_INT, 'ID of the user'),
                     'username'    => new external_value(PARAM_RAW, 'Username policy is defined in Moodle security config', VALUE_OPTIONAL),
                     'firstname'   => new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL),
@@ -896,47 +907,47 @@ class core_enrol_external extends external_api {
                     'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version', VALUE_OPTIONAL),
                     'customfields' => new external_multiple_structure(
                         new external_single_structure(
-                            array(
+                            [
                                 'type'  => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field - text field, checkbox...'),
                                 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
                                 'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
                                 'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field - to be able to build the field class in the code'),
-                            )
+                            ]
                         ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
                     'groups' => new external_multiple_structure(
                         new external_single_structure(
-                            array(
+                            [
                                 'id'  => new external_value(PARAM_INT, 'group id'),
                                 'name' => new external_value(PARAM_RAW, 'group name'),
                                 'description' => new external_value(PARAM_RAW, 'group description'),
                                 'descriptionformat' => new external_format_value('description'),
-                            )
+                            ]
                         ), 'user groups', VALUE_OPTIONAL),
                     'roles' => new external_multiple_structure(
                         new external_single_structure(
-                            array(
+                            [
                                 'roleid'       => new external_value(PARAM_INT, 'role id'),
                                 'name'         => new external_value(PARAM_RAW, 'role name'),
                                 'shortname'    => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
                                 'sortorder'    => new external_value(PARAM_INT, 'role sortorder')
-                            )
+                            ]
                         ), 'user roles', VALUE_OPTIONAL),
                     'preferences' => new external_multiple_structure(
                         new external_single_structure(
-                            array(
+                            [
                                 'name'  => new external_value(PARAM_RAW, 'The name of the preferences'),
                                 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
-                            )
+                            ]
                     ), 'User preferences', VALUE_OPTIONAL),
                     'enrolledcourses' => new external_multiple_structure(
                         new external_single_structure(
-                            array(
+                            [
                                 'id'  => new external_value(PARAM_INT, 'Id of the course'),
                                 'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'),
                                 'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
-                            )
+                            ]
                     ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
-                )
+                ]
             )
         );
     }
index a12df0b..c98df46 100644 (file)
@@ -355,6 +355,95 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase {
         }
     }
 
+    /**
+     * Verify get_enrolled_users() returned users according to their status.
+     */
+    public function test_get_enrolled_users_active_suspended() {
+        global $USER;
+
+        $this->resetAfterTest();
+
+        // Create the course and the users.
+        $course = $this->getDataGenerator()->create_course();
+        $coursecontext = context_course::instance($course->id);
+        $user0 = $this->getDataGenerator()->create_user(['username' => 'user0active']);
+        $user1 = $this->getDataGenerator()->create_user(['username' => 'user1active']);
+        $user2 = $this->getDataGenerator()->create_user(['username' => 'user2active']);
+        $user2su = $this->getDataGenerator()->create_user(['username' => 'user2suspended']); // Suspended user.
+        $user3 = $this->getDataGenerator()->create_user(['username' => 'user3active']);
+        $user3su = $this->getDataGenerator()->create_user(['username' => 'user3suspended']); // Suspended user.
+
+        // Enrol the users in the course.
+        $this->getDataGenerator()->enrol_user($user0->id, $course->id);
+        $this->getDataGenerator()->enrol_user($user1->id, $course->id);
+        $this->getDataGenerator()->enrol_user($user2->id, $course->id);
+        $this->getDataGenerator()->enrol_user($user2su->id, $course->id, null, 'manual', 0, 0, ENROL_USER_SUSPENDED);
+        $this->getDataGenerator()->enrol_user($user3->id, $course->id);
+        $this->getDataGenerator()->enrol_user($user3su->id, $course->id, null, 'manual', 0, 0, ENROL_USER_SUSPENDED);
+
+        // Create a role to add the allowedcaps. Users will have this role assigned.
+        $roleid = $this->getDataGenerator()->create_role();
+        // Allow the specified capabilities.
+        assign_capability('moodle/course:enrolreview', CAP_ALLOW, $roleid, $coursecontext);
+        assign_capability('moodle/user:viewalldetails', CAP_ALLOW, $roleid, $coursecontext);
+
+        // Switch to the user and assign the role.
+        $this->setUser($user0);
+        role_assign($roleid, $USER->id, $coursecontext);
+
+        // Suspended users.
+        $options = [
+            ['name' => 'onlysuspended', 'value' => true],
+            ['name' => 'userfields', 'value' => 'id,username']
+        ];
+        $suspendedusers = core_enrol_external::get_enrolled_users($course->id, $options);
+
+        // We need to execute the return values cleaning process to simulate the web service server.
+        $suspendedusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $suspendedusers);
+        $this->assertCount(2, $suspendedusers);
+
+        foreach ($suspendedusers as $suspendeduser) {
+            $this->assertStringContainsString('suspended', $suspendeduser['username']);
+        }
+
+        // Active users.
+        $options = [
+            ['name' => 'onlyactive', 'value' => true],
+            ['name' => 'userfields', 'value' => 'id,username']
+        ];
+        $activeusers = core_enrol_external::get_enrolled_users($course->id, $options);
+
+        // We need to execute the return values cleaning process to simulate the web service server.
+        $activeusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $activeusers);
+        $this->assertCount(4, $activeusers);
+
+        foreach ($activeusers as $activeuser) {
+            $this->assertStringContainsString('active', $activeuser['username']);
+        }
+
+        // All enrolled users.
+        $options = [
+            ['name' => 'userfields', 'value' => 'id,username']
+        ];
+        $allusers = core_enrol_external::get_enrolled_users($course->id, $options);
+
+        // We need to execute the return values cleaning process to simulate the web service server.
+        $allusers = external_api::clean_returnvalue(core_enrol_external::get_enrolled_users_returns(), $allusers);
+        $this->assertCount(6, $allusers);
+
+        // Active and suspended. Test exception is thrown.
+        $options = [
+            ['name' => 'onlyactive', 'value' => true],
+            ['name' => 'onlysuspended', 'value' => true],
+            ['name' => 'userfields', 'value' => 'id,username']
+        ];
+        $this->expectException('coding_exception');
+        $message = 'Coding error detected, it must be fixed by a programmer: Both onlyactive ' .
+                        'and onlysuspended are set, this is probably not what you want!';
+        $this->expectExceptionMessage($message);
+        core_enrol_external::get_enrolled_users($course->id, $options);
+    }
+
     /**
      * Test get_users_courses
      */
index bfec2ff..a882f6e 100644 (file)
@@ -1,6 +1,10 @@
 This files describes API changes in /enrol/* - plugins,
 information provided here is intended especially for developers.
 
+=== 3.11 ===
+
+* Added onlysuspended option to core_enrol_get_enrolled_users webservice to retrieve only suspended users.
+
 === 3.8 ===
 
 * Function enrol_manual_plugin::enrol_cohort now return the number of enrolled cohort users.