From 06e50afd5e4a75962c78fb2b9b5dafec1e57ba32 Mon Sep 17 00:00:00 2001 From: Mathew May Date: Sat, 5 Oct 2019 11:33:05 +0800 Subject: [PATCH] MDL-66077 course: Add WS to get list of users in a cmid Part of MDL-66074 --- course/amd/build/repository.min.js | 2 +- course/amd/build/repository.min.js.map | 2 +- course/amd/src/repository.js | 20 ++++++- course/externallib.php | 78 ++++++++++++++++++++++++++ course/tests/externallib_test.php | 51 +++++++++++++++++ lib/db/services.php | 8 +++ 6 files changed, 158 insertions(+), 3 deletions(-) diff --git a/course/amd/build/repository.min.js b/course/amd/build/repository.min.js index 3c301ffbb76..2e4849bbfd8 100644 --- a/course/amd/build/repository.min.js +++ b/course/amd/build/repository.min.js @@ -1,2 +1,2 @@ -define ("core_course/repository",["jquery","core/ajax"],function(a,b){return{getEnrolledCoursesByTimelineClassification:function getEnrolledCoursesByTimelineClassification(a,c,d,e){var f={classification:a};if("undefined"!=typeof c){f.limit=c}if("undefined"!=typeof d){f.offset=d}if("undefined"!=typeof e){f.sort=e}return b.call([{methodname:"core_course_get_enrolled_courses_by_timeline_classification",args:f}])[0]},getLastAccessedCourses:function getLastAccessedCourses(a,c,d,e){var f={};if("undefined"!=typeof a){f.userid=a}if("undefined"!=typeof c){f.limit=c}if("undefined"!=typeof d){f.offset=d}if("undefined"!=typeof e){f.sort=e}return b.call([{methodname:"core_course_get_recent_courses",args:f}])[0]}}}); +define ("core_course/repository",["jquery","core/ajax"],function(a,b){return{getEnrolledCoursesByTimelineClassification:function getEnrolledCoursesByTimelineClassification(a,c,d,e){var f={classification:a};if("undefined"!=typeof c){f.limit=c}if("undefined"!=typeof d){f.offset=d}if("undefined"!=typeof e){f.sort=e}return b.call([{methodname:"core_course_get_enrolled_courses_by_timeline_classification",args:f}])[0]},getLastAccessedCourses:function getLastAccessedCourses(a,c,d,e){var f={};if("undefined"!=typeof a){f.userid=a}if("undefined"!=typeof c){f.limit=c}if("undefined"!=typeof d){f.offset=d}if("undefined"!=typeof e){f.sort=e}return b.call([{methodname:"core_course_get_recent_courses",args:f}])[0]},getUsersFromCourseModuleID:function getEnrolledUsersFromCourseModuleID(a){return b.call([{methodname:"core_course_get_enrolled_users_by_cmid",args:{cmid:a}}])[0]}}}); //# sourceMappingURL=repository.min.js.map diff --git a/course/amd/build/repository.min.js.map b/course/amd/build/repository.min.js.map index 21f6694971f..1e49dc2d9b0 100644 --- a/course/amd/build/repository.min.js.map +++ b/course/amd/build/repository.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/repository.js"],"names":["define","$","Ajax","getEnrolledCoursesByTimelineClassification","classification","limit","offset","sort","args","call","methodname","getLastAccessedCourses","userid"],"mappings":"AAsBAA,OAAM,0BAAC,CAAC,QAAD,CAAW,WAAX,CAAD,CAA0B,SAASC,CAAT,CAAYC,CAAZ,CAAkB,CA0E9C,MAAO,CACHC,0CAA0C,CA/DG,QAA7CA,CAAAA,0CAA6C,CAASC,CAAT,CAAyBC,CAAzB,CAAgCC,CAAhC,CAAwCC,CAAxC,CAA8C,CAC3F,GAAIC,CAAAA,CAAI,CAAG,CACPJ,cAAc,CAAEA,CADT,CAAX,CAIA,GAAqB,WAAjB,QAAOC,CAAAA,CAAX,CAAkC,CAC9BG,CAAI,CAACH,KAAL,CAAaA,CAChB,CAED,GAAsB,WAAlB,QAAOC,CAAAA,CAAX,CAAmC,CAC/BE,CAAI,CAACF,MAAL,CAAcA,CACjB,CAED,GAAoB,WAAhB,QAAOC,CAAAA,CAAX,CAAiC,CAC7BC,CAAI,CAACD,IAAL,CAAYA,CACf,CAOD,MAAOL,CAAAA,CAAI,CAACO,IAAL,CAAU,CALH,CACVC,UAAU,CAAE,6DADF,CAEVF,IAAI,CAAEA,CAFI,CAKG,CAAV,EAAqB,CAArB,CACV,CAuCM,CAEHG,sBAAsB,CA7BG,QAAzBA,CAAAA,sBAAyB,CAASC,CAAT,CAAiBP,CAAjB,CAAwBC,CAAxB,CAAgCC,CAAhC,CAAsC,CAC/D,GAAIC,CAAAA,CAAI,CAAG,EAAX,CAEA,GAAsB,WAAlB,QAAOI,CAAAA,CAAX,CAAmC,CAC/BJ,CAAI,CAACI,MAAL,CAAcA,CACjB,CAED,GAAqB,WAAjB,QAAOP,CAAAA,CAAX,CAAkC,CAC9BG,CAAI,CAACH,KAAL,CAAaA,CAChB,CAED,GAAsB,WAAlB,QAAOC,CAAAA,CAAX,CAAmC,CAC/BE,CAAI,CAACF,MAAL,CAAcA,CACjB,CAED,GAAoB,WAAhB,QAAOC,CAAAA,CAAX,CAAiC,CAC7BC,CAAI,CAACD,IAAL,CAAYA,CACf,CAOD,MAAOL,CAAAA,CAAI,CAACO,IAAL,CAAU,CALH,CACVC,UAAU,CAAE,gCADF,CAEVF,IAAI,CAAEA,CAFI,CAKG,CAAV,EAAqB,CAArB,CACV,CAEM,CAIV,CA9EK,CAAN","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * A javascript module to handle course ajax actions.\n *\n * @module core_course/repository\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax'], function($, Ajax) {\n\n /**\n * Get the list of courses that the logged in user is enrolled in for a given\n * timeline classification.\n *\n * @param {string} classification past, inprogress, or future\n * @param {int} limit Only return this many results\n * @param {int} offset Skip this many results from the start of the result set\n * @param {string} sort Column to sort by and direction, e.g. 'shortname asc'\n * @return {object} jQuery promise resolved with courses.\n */\n var getEnrolledCoursesByTimelineClassification = function(classification, limit, offset, sort) {\n var args = {\n classification: classification\n };\n\n if (typeof limit !== 'undefined') {\n args.limit = limit;\n }\n\n if (typeof offset !== 'undefined') {\n args.offset = offset;\n }\n\n if (typeof sort !== 'undefined') {\n args.sort = sort;\n }\n\n var request = {\n methodname: 'core_course_get_enrolled_courses_by_timeline_classification',\n args: args\n };\n\n return Ajax.call([request])[0];\n };\n\n /**\n * Get the list of courses that the user has most recently accessed.\n *\n * @method getLastAccessedCourses\n * @param {int} userid User from which the courses will be obtained\n * @param {int} limit Only return this many results\n * @param {int} offset Skip this many results from the start of the result set\n * @param {string} sort Column to sort by and direction, e.g. 'shortname asc'\n * @return {promise} Resolved with an array of courses\n */\n var getLastAccessedCourses = function(userid, limit, offset, sort) {\n var args = {};\n\n if (typeof userid !== 'undefined') {\n args.userid = userid;\n }\n\n if (typeof limit !== 'undefined') {\n args.limit = limit;\n }\n\n if (typeof offset !== 'undefined') {\n args.offset = offset;\n }\n\n if (typeof sort !== 'undefined') {\n args.sort = sort;\n }\n\n var request = {\n methodname: 'core_course_get_recent_courses',\n args: args\n };\n\n return Ajax.call([request])[0];\n };\n\n return {\n getEnrolledCoursesByTimelineClassification: getEnrolledCoursesByTimelineClassification,\n getLastAccessedCourses: getLastAccessedCourses\n };\n});\n"],"file":"repository.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/repository.js"],"names":["define","$","Ajax","getEnrolledCoursesByTimelineClassification","classification","limit","offset","sort","args","call","methodname","getLastAccessedCourses","userid","getUsersFromCourseModuleID","getEnrolledUsersFromCourseModuleID","cmid"],"mappings":"AAsBAA,OAAM,0BAAC,CAAC,QAAD,CAAW,WAAX,CAAD,CAA0B,SAASC,CAAT,CAAYC,CAAZ,CAAkB,CA2F9C,MAAO,CACHC,0CAA0C,CAhFG,QAA7CA,CAAAA,0CAA6C,CAASC,CAAT,CAAyBC,CAAzB,CAAgCC,CAAhC,CAAwCC,CAAxC,CAA8C,CAC3F,GAAIC,CAAAA,CAAI,CAAG,CACPJ,cAAc,CAAEA,CADT,CAAX,CAIA,GAAqB,WAAjB,QAAOC,CAAAA,CAAX,CAAkC,CAC9BG,CAAI,CAACH,KAAL,CAAaA,CAChB,CAED,GAAsB,WAAlB,QAAOC,CAAAA,CAAX,CAAmC,CAC/BE,CAAI,CAACF,MAAL,CAAcA,CACjB,CAED,GAAoB,WAAhB,QAAOC,CAAAA,CAAX,CAAiC,CAC7BC,CAAI,CAACD,IAAL,CAAYA,CACf,CAOD,MAAOL,CAAAA,CAAI,CAACO,IAAL,CAAU,CALH,CACVC,UAAU,CAAE,6DADF,CAEVF,IAAI,CAAEA,CAFI,CAKG,CAAV,EAAqB,CAArB,CACV,CAwDM,CAEHG,sBAAsB,CA9CG,QAAzBA,CAAAA,sBAAyB,CAASC,CAAT,CAAiBP,CAAjB,CAAwBC,CAAxB,CAAgCC,CAAhC,CAAsC,CAC/D,GAAIC,CAAAA,CAAI,CAAG,EAAX,CAEA,GAAsB,WAAlB,QAAOI,CAAAA,CAAX,CAAmC,CAC/BJ,CAAI,CAACI,MAAL,CAAcA,CACjB,CAED,GAAqB,WAAjB,QAAOP,CAAAA,CAAX,CAAkC,CAC9BG,CAAI,CAACH,KAAL,CAAaA,CAChB,CAED,GAAsB,WAAlB,QAAOC,CAAAA,CAAX,CAAmC,CAC/BE,CAAI,CAACF,MAAL,CAAcA,CACjB,CAED,GAAoB,WAAhB,QAAOC,CAAAA,CAAX,CAAiC,CAC7BC,CAAI,CAACD,IAAL,CAAYA,CACf,CAOD,MAAOL,CAAAA,CAAI,CAACO,IAAL,CAAU,CALH,CACVC,UAAU,CAAE,gCADF,CAEVF,IAAI,CAAEA,CAFI,CAKG,CAAV,EAAqB,CAArB,CACV,CAmBM,CAGHK,0BAA0B,CAdW,QAArCC,CAAAA,kCAAqC,CAASC,CAAT,CAAe,CAQpD,MAAOb,CAAAA,CAAI,CAACO,IAAL,CAAU,CAPH,CACVC,UAAU,CAAE,wCADF,CAEVF,IAAI,CAAE,CACFO,IAAI,CAAEA,CADJ,CAFI,CAOG,CAAV,EAAqB,CAArB,CACV,CAEM,CAKV,CAhGK,CAAN","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * A javascript module to handle course ajax actions.\n *\n * @module core_course/repository\n * @copyright 2018 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\ndefine(['jquery', 'core/ajax'], function($, Ajax) {\n\n /**\n * Get the list of courses that the logged in user is enrolled in for a given\n * timeline classification.\n *\n * @param {string} classification past, inprogress, or future\n * @param {int} limit Only return this many results\n * @param {int} offset Skip this many results from the start of the result set\n * @param {string} sort Column to sort by and direction, e.g. 'shortname asc'\n * @return {object} jQuery promise resolved with courses.\n */\n var getEnrolledCoursesByTimelineClassification = function(classification, limit, offset, sort) {\n var args = {\n classification: classification\n };\n\n if (typeof limit !== 'undefined') {\n args.limit = limit;\n }\n\n if (typeof offset !== 'undefined') {\n args.offset = offset;\n }\n\n if (typeof sort !== 'undefined') {\n args.sort = sort;\n }\n\n var request = {\n methodname: 'core_course_get_enrolled_courses_by_timeline_classification',\n args: args\n };\n\n return Ajax.call([request])[0];\n };\n\n /**\n * Get the list of courses that the user has most recently accessed.\n *\n * @method getLastAccessedCourses\n * @param {int} userid User from which the courses will be obtained\n * @param {int} limit Only return this many results\n * @param {int} offset Skip this many results from the start of the result set\n * @param {string} sort Column to sort by and direction, e.g. 'shortname asc'\n * @return {promise} Resolved with an array of courses\n */\n var getLastAccessedCourses = function(userid, limit, offset, sort) {\n var args = {};\n\n if (typeof userid !== 'undefined') {\n args.userid = userid;\n }\n\n if (typeof limit !== 'undefined') {\n args.limit = limit;\n }\n\n if (typeof offset !== 'undefined') {\n args.offset = offset;\n }\n\n if (typeof sort !== 'undefined') {\n args.sort = sort;\n }\n\n var request = {\n methodname: 'core_course_get_recent_courses',\n args: args\n };\n\n return Ajax.call([request])[0];\n };\n\n /**\n * Get the list of users enrolled in this cmid.\n *\n * @param {Number} cmid Course Module from which the users will be obtained\n * @returns {Promise} Promise containing a list of users\n */\n var getEnrolledUsersFromCourseModuleID = function(cmid) {\n var request = {\n methodname: 'core_course_get_enrolled_users_by_cmid',\n args: {\n cmid: cmid,\n },\n };\n\n return Ajax.call([request])[0];\n };\n\n return {\n getEnrolledCoursesByTimelineClassification: getEnrolledCoursesByTimelineClassification,\n getLastAccessedCourses: getLastAccessedCourses,\n getUsersFromCourseModuleID: getEnrolledUsersFromCourseModuleID,\n };\n});\n"],"file":"repository.min.js"} \ No newline at end of file diff --git a/course/amd/src/repository.js b/course/amd/src/repository.js index 6340eab31b1..7e530a72543 100644 --- a/course/amd/src/repository.js +++ b/course/amd/src/repository.js @@ -94,8 +94,26 @@ define(['jquery', 'core/ajax'], function($, Ajax) { return Ajax.call([request])[0]; }; + /** + * Get the list of users enrolled in this cmid. + * + * @param {Number} cmid Course Module from which the users will be obtained + * @returns {Promise} Promise containing a list of users + */ + var getEnrolledUsersFromCourseModuleID = function(cmid) { + var request = { + methodname: 'core_course_get_enrolled_users_by_cmid', + args: { + cmid: cmid, + }, + }; + + return Ajax.call([request])[0]; + }; + return { getEnrolledCoursesByTimelineClassification: getEnrolledCoursesByTimelineClassification, - getLastAccessedCourses: getLastAccessedCourses + getLastAccessedCourses: getLastAccessedCourses, + getUsersFromCourseModuleID: getEnrolledUsersFromCourseModuleID, }; }); diff --git a/course/externallib.php b/course/externallib.php index bb2b08a7478..028f105b722 100644 --- a/course/externallib.php +++ b/course/externallib.php @@ -4053,4 +4053,82 @@ class core_course_external extends external_api { public static function get_recent_courses_returns() { return new external_multiple_structure(course_summary_exporter::get_read_structure(), 'Courses'); } + + /** + * Returns description of method parameters + * + * @return external_function_parameters + */ + public static function get_enrolled_users_by_cmid_parameters() { + return new external_function_parameters([ + 'cmid' => new external_value(PARAM_INT, 'id of the course module', VALUE_REQUIRED), + ]); + } + + /** + * Get all users in a course for a given cmid. + * + * @param int $cmid Course Module id from which the users will be obtained + * @return array List of users + * @throws invalid_parameter_exception + */ + public static function get_enrolled_users_by_cmid(int $cmid) { + $warnings = []; + + [ + 'cmid' => $cmid, + ] = self::validate_parameters(self::get_enrolled_users_by_cmid_parameters(), [ + 'cmid' => $cmid, + ]); + + list($course, $cm) = get_course_and_cm_from_cmid($cmid); + $coursecontext = context_course::instance($course->id); + self::validate_context($coursecontext); + + $enrolledusers = get_enrolled_users($coursecontext); + + $users = array_map(function ($user) { + $user->fullname = fullname($user); + return $user; + }, $enrolledusers); + sort($users); + + return [ + 'users' => $users, + 'warnings' => $warnings, + ]; + } + + /** + * Returns description of method result value + * + * @return external_description + */ + public static function get_enrolled_users_by_cmid_returns() { + return new external_single_structure([ + 'users' => new external_multiple_structure(self::user_description()), + 'warnings' => new external_warnings(), + ]); + } + + /** + * Create user return value description. + * + * @return single_structure_description + */ + public static function user_description() { + $userfields = array( + 'id' => new external_value(core_user::get_property_type('id'), 'ID of the user'), + 'fullname' => new external_value(PARAM_TEXT, 'The full name of the user', VALUE_OPTIONAL), + 'firstname' => new external_value( + core_user::get_property_type('firstname'), + 'The first name(s) of the user', + VALUE_OPTIONAL), + 'lastname' => new external_value( + core_user::get_property_type('lastname'), + 'The family name of the user', + VALUE_OPTIONAL), + ); + return new external_single_structure($userfields); + } } diff --git a/course/tests/externallib_test.php b/course/tests/externallib_test.php index ab857e93840..242b02cade1 100644 --- a/course/tests/externallib_test.php +++ b/course/tests/externallib_test.php @@ -2988,4 +2988,55 @@ class core_course_externallib_testcase extends externallib_advanced_testcase { $this->assertCount(1, $result); $this->assertEquals($courses[0]->id, array_shift($result)->id); } + + /** + * Test get enrolled users by cmid function. + */ + public function test_get_enrolled_users_by_cmid() { + $this->resetAfterTest(true); + + $user1 = self::getDataGenerator()->create_user(); + $user2 = self::getDataGenerator()->create_user(); + + // Set the first created user to the test user. + self::setUser($user1); + + // Create course to add the module. + $course1 = self::getDataGenerator()->create_course(); + + // Forum with tracking off. + $record = new stdClass(); + $record->course = $course1->id; + $forum1 = self::getDataGenerator()->create_module('forum', $record); + + // Following lines enrol and assign default role id to the users. + $this->getDataGenerator()->enrol_user($user1->id, $course1->id); + $this->getDataGenerator()->enrol_user($user2->id, $course1->id); + + // Create what we expect to be returned when querying the course module. + $expectedusers = array( + 'users' => array(), + 'warnings' => array(), + ); + + $expectedusers['users'][0] = [ + 'id' => $user1->id, + 'fullname' => fullname($user1), + 'firstname' => $user1->firstname, + 'lastname' => $user1->lastname, + ]; + $expectedusers['users'][1] = [ + 'id' => $user2->id, + 'fullname' => fullname($user2), + 'firstname' => $user2->firstname, + 'lastname' => $user2->lastname, + ]; + + // Test getting the users in a given context. + $users = core_course_external::get_enrolled_users_by_cmid($forum1->cmid); + $users = external_api::clean_returnvalue(core_course_external::get_enrolled_users_by_cmid_returns(), $users); + + $this->assertEquals(2, count($users['users'])); + $this->assertEquals($expectedusers, $users); + } } diff --git a/lib/db/services.php b/lib/db/services.php index 167e4314dbc..147079d167c 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -613,6 +613,14 @@ $functions = array( 'ajax' => true, 'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE), ), + 'core_course_get_enrolled_users_by_cmid' => array( + 'classname' => 'core_course_external', + 'methodname' => 'get_enrolled_users_by_cmid', + 'classpath' => 'course/externallib.php', + 'description' => 'List users bycourse module id.', + 'type' => 'read', + 'ajax' => true, + ), 'core_enrol_get_course_enrolment_methods' => array( 'classname' => 'core_enrol_external', 'methodname' => 'get_course_enrolment_methods',