From 987e55452f5100d69d3d0b57e46fc6c4ddefe02a Mon Sep 17 00:00:00 2001 From: sam marshall Date: Wed, 21 Oct 2020 18:44:10 +0100 Subject: [PATCH] MDL-45242 Course: Enrol feature supports custom profile fields --- badges/criteria/award_criteria_profile.php | 3 +- enrol/externallib.php | 23 +- enrol/locallib.php | 89 ++++--- .../build/form-potential-user-selector.min.js | 2 +- .../form-potential-user-selector.min.js.map | 2 +- .../amd/src/form-potential-user-selector.js | 20 +- enrol/manual/classes/enrol_users_form.php | 3 +- .../manual/tests/behat/quickenrolment.feature | 227 ++++++++++-------- enrol/tests/course_enrolment_manager_test.php | 173 +++++++++++++ enrol/tests/externallib_test.php | 75 ++++++ grade/report/grader/lib.php | 2 +- lib/moodlelib.php | 6 +- lib/outputcomponents.php | 2 +- mod/chat/lib.php | 5 +- report/completion/index.php | 2 +- report/log/locallib.php | 2 +- user/lib.php | 3 +- userpix/index.php | 3 +- 18 files changed, 482 insertions(+), 160 deletions(-) diff --git a/badges/criteria/award_criteria_profile.php b/badges/criteria/award_criteria_profile.php index bd887db96d1..dbba62aae38 100644 --- a/badges/criteria/award_criteria_profile.php +++ b/badges/criteria/award_criteria_profile.php @@ -88,7 +88,8 @@ class award_criteria_profile extends award_criteria { if (in_array($field, $existing)) { $checked = true; } - $this->config_options($mform, array('id' => $field, 'checked' => $checked, 'name' => \core\user_fields::get_display_name($field), 'error' => false)); + $this->config_options($mform, array('id' => $field, 'checked' => $checked, + 'name' => \core\user_fields::get_display_name($field), 'error' => false)); $none = false; } } diff --git a/enrol/externallib.php b/enrol/externallib.php index e2cfb216587..26dee5065d1 100644 --- a/enrol/externallib.php +++ b/enrol/externallib.php @@ -558,10 +558,20 @@ class core_enrol_external extends external_api { $results = array(); // Add also extra user fields. + $identityfields = \core\user_fields::get_identity_fields($context, true); + $customprofilefields = []; + foreach ($identityfields as $key => $value) { + if ($fieldname = \core\user_fields::match_custom_field($value)) { + unset($identityfields[$key]); + $customprofilefields[$fieldname] = true; + } + } + if ($customprofilefields) { + $identityfields[] = 'customfields'; + } $requiredfields = array_merge( ['id', 'fullname', 'profileimageurl', 'profileimageurlsmall'], - // TODO Does not support custom user profile fields (MDL-70456). - \core\user_fields::get_identity_fields($context, false) + $identityfields ); foreach ($users['users'] as $id => $user) { // Note: We pass the course here to validate that the current user can at least view user details in this course. @@ -569,6 +579,15 @@ class core_enrol_external extends external_api { // user records, and the user has been validated to have course:enrolreview in this course. Otherwise // there is no way to find users who aren't in the course in order to enrol them. if ($userdetails = user_get_user_details($user, $course, $requiredfields)) { + // For custom fields, only return the ones we actually need. + if ($customprofilefields && array_key_exists('customfields', $userdetails)) { + foreach ($userdetails['customfields'] as $key => $data) { + if (!array_key_exists($data['shortname'], $customprofilefields)) { + unset($userdetails['customfields'][$key]); + } + } + $userdetails['customfields'] = array_values($userdetails['customfields']); + } $results[] = $userdetails; } } diff --git a/enrol/locallib.php b/enrol/locallib.php index cd6f9fd8788..ba49c560265 100644 --- a/enrol/locallib.php +++ b/enrol/locallib.php @@ -23,6 +23,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +use core\user_fields; + defined('MOODLE_INTERNAL') || die(); /** @@ -238,14 +240,15 @@ class course_enrolment_manager { list($instancessql, $params, $filter) = $this->get_instance_sql(); list($filtersql, $moreparams) = $this->get_filter_sql(); $params += $moreparams; - // TODO Does not support custom user profile fields (MDL-70456). - $extrafields = \core\user_fields::get_identity_fields($this->get_context(), false); - $extrafields[] = 'lastaccess'; - $ufields = user_picture::fields('u', $extrafields); - $sql = "SELECT DISTINCT $ufields, COALESCE(ul.timeaccess, 0) AS lastcourseaccess + $userfields = user_fields::for_identity($this->get_context())->with_userpic()->excluding('lastaccess'); + ['selects' => $fieldselect, 'joins' => $fieldjoin, 'params' => $fieldjoinparams] = + (array)$userfields->get_sql('u', true, '', '', false); + $params += $fieldjoinparams; + $sql = "SELECT DISTINCT $fieldselect, COALESCE(ul.timeaccess, 0) AS lastcourseaccess FROM {user} u JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid $instancessql) JOIN {enrol} e ON (e.id = ue.enrolid) + $fieldjoin LEFT JOIN {user_lastaccess} ul ON (ul.courseid = e.courseid AND ul.userid = u.id)"; if ($this->groupfilter) { $sql .= " LEFT JOIN ({groups_members} gm JOIN {groups} g ON (g.id = gm.groupid)) @@ -270,7 +273,7 @@ class course_enrolment_manager { // Search condition. // TODO Does not support custom user profile fields (MDL-70456). - $extrafields = \core\user_fields::get_identity_fields($this->get_context(), false); + $extrafields = user_fields::get_identity_fields($this->get_context(), false); list($sql, $params) = users_search_sql($this->searchfilter, 'u', true, $extrafields); // Role condition. @@ -343,23 +346,26 @@ class course_enrolment_manager { list($ctxcondition, $params) = $DB->get_in_or_equal($this->context->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'ctx'); $params['courseid'] = $this->course->id; $params['cid'] = $this->course->id; - // TODO Does not support custom user profile fields (MDL-70456). - $extrafields = \core\user_fields::get_identity_fields($this->get_context(), false); - $ufields = user_picture::fields('u', $extrafields); - $sql = "SELECT ra.id as raid, ra.contextid, ra.component, ctx.contextlevel, ra.roleid, $ufields, - coalesce(u.lastaccess,0) AS lastaccess - FROM {role_assignments} ra - JOIN {user} u ON u.id = ra.userid - JOIN {context} ctx ON ra.contextid = ctx.id - LEFT JOIN ( + $userfields = user_fields::for_identity($this->get_context())->with_userpic(); + ['selects' => $fieldselect, 'joins' => $fieldjoin, 'params' => $fieldjoinparams] = + (array)$userfields->get_sql('u', true); + $params += $fieldjoinparams; + $sql = "SELECT ra.id as raid, ra.contextid, ra.component, ctx.contextlevel, ra.roleid, + coalesce(u.lastaccess,0) AS lastaccess + $fieldselect + FROM {role_assignments} ra + JOIN {user} u ON u.id = ra.userid + JOIN {context} ctx ON ra.contextid = ctx.id + $fieldjoin + LEFT JOIN ( SELECT ue.id, ue.userid FROM {user_enrolments} ue JOIN {enrol} e ON e.id = ue.enrolid WHERE e.courseid = :courseid ) ue ON ue.userid=u.id - WHERE ctx.id $ctxcondition AND - ue.id IS NULL - ORDER BY $sort $direction, ctx.depth DESC"; + WHERE ctx.id $ctxcondition AND + ue.id IS NULL + ORDER BY $sort $direction, ctx.depth DESC"; $this->otherusers[$key] = $DB->get_records_sql($sql, $params, $page*$perpage, $perpage); } return $this->otherusers[$key]; @@ -372,20 +378,33 @@ class course_enrolment_manager { * @param bool $searchanywhere Can the search term be anywhere, or must it be at the start. * @return array with three elements: * string list of fields to SELECT, + * string possible database joins for user fields * string contents of SQL WHERE clause, * array query params. Note that the SQL snippets use named parameters. */ protected function get_basic_search_conditions($search, $searchanywhere) { global $DB, $CFG; + // Get custom user field SQL used for querying all the fields we need (identity, name, and + // user picture). + $userfields = user_fields::for_identity($this->context)->with_name()->with_userpic() + ->excluding('username', 'lastaccess', 'maildisplay'); + ['selects' => $fieldselects, 'joins' => $fieldjoins, 'params' => $params, 'mappings' => $mappings] = + (array)$userfields->get_sql('u', true, '', '', false); + + // Searchable fields are only the identity and name ones (not userpic). + $searchable = array_fill_keys($userfields->get_required_fields( + [user_fields::PURPOSE_IDENTITY, user_fields::PURPOSE_NAME]), true); + // Add some additional sensible conditions $tests = array("u.id <> :guestid", 'u.deleted = 0', 'u.confirmed = 1'); - $params = array('guestid' => $CFG->siteguest); + $params['guestid'] = $CFG->siteguest; if (!empty($search)) { - // TODO Does not support custom user profile fields (MDL-70456). - $conditions = \core\user_fields::get_identity_fields($this->get_context(), false); - foreach (\core\user_fields::get_name_fields() as $field) { - $conditions[] = 'u.'.$field; + // Include identity and name fields as conditions. + foreach ($mappings as $fieldname => $fieldsql) { + if (array_key_exists($fieldname, $searchable)) { + $conditions[] = $fieldsql; + } } $conditions[] = $DB->sql_fullname('u.firstname', 'u.lastname'); if ($searchanywhere) { @@ -403,15 +422,8 @@ class course_enrolment_manager { } $wherecondition = implode(' AND ', $tests); - // TODO Does not support custom user profile fields (MDL-70456). - $userfieldsapi = \core\user_fields::for_identity($this->get_context(), false)->excluding('username', 'lastaccess'); - $extrafields = $userfieldsapi->get_required_fields(); - $extrafields[] = 'username'; - $extrafields[] = 'lastaccess'; - $extrafields[] = 'maildisplay'; - $ufields = user_picture::fields('u', $extrafields); - - return array($ufields, $params, $wherecondition); + $selects = $fieldselects . ', u.username, u.lastaccess, u.maildisplay'; + return [$selects, $fieldjoins, $params, $wherecondition]; } /** @@ -492,11 +504,12 @@ class course_enrolment_manager { $addedenrollment = 0, $returnexactcount = false) { global $DB; - list($ufields, $params, $wherecondition) = $this->get_basic_search_conditions($search, $searchanywhere); + [$ufields, $joins, $params, $wherecondition] = $this->get_basic_search_conditions($search, $searchanywhere); $fields = 'SELECT '.$ufields; $countfields = 'SELECT COUNT(1)'; $sql = " FROM {user} u + $joins LEFT JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid = :enrolid) WHERE $wherecondition AND ue.id IS NULL"; @@ -524,11 +537,12 @@ class course_enrolment_manager { public function search_other_users($search = '', $searchanywhere = false, $page = 0, $perpage = 25, $returnexactcount = false) { global $DB, $CFG; - list($ufields, $params, $wherecondition) = $this->get_basic_search_conditions($search, $searchanywhere); + [$ufields, $joins, $params, $wherecondition] = $this->get_basic_search_conditions($search, $searchanywhere); $fields = 'SELECT ' . $ufields; $countfields = 'SELECT COUNT(u.id)'; $sql = " FROM {user} u + $joins LEFT JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.contextid = :contextid) WHERE $wherecondition AND ra.id IS NULL"; @@ -552,11 +566,12 @@ class course_enrolment_manager { */ public function search_users(string $search = '', bool $searchanywhere = false, int $page = 0, int $perpage = 25, bool $returnexactcount = false) { - list($ufields, $params, $wherecondition) = $this->get_basic_search_conditions($search, $searchanywhere); + [$ufields, $joins, $params, $wherecondition] = $this->get_basic_search_conditions($search, $searchanywhere); $fields = 'SELECT ' . $ufields; $countfields = 'SELECT COUNT(u.id)'; $sql = " FROM {user} u + $joins JOIN {user_enrolments} ue ON ue.userid = u.id JOIN {enrol} e ON ue.enrolid = e.id WHERE $wherecondition @@ -1053,7 +1068,7 @@ class course_enrolment_manager { $context = $this->get_context(); $now = time(); // TODO Does not support custom user profile fields (MDL-70456). - $extrafields = \core\user_fields::get_identity_fields($context, false); + $extrafields = user_fields::get_identity_fields($context, false); $users = array(); foreach ($userroles as $userrole) { @@ -1132,7 +1147,7 @@ class course_enrolment_manager { $url = new moodle_url($pageurl, $this->get_url_params()); // TODO Does not support custom user profile fields (MDL-70456). - $extrafields = \core\user_fields::get_identity_fields($context, false); + $extrafields = user_fields::get_identity_fields($context, false); $enabledplugins = $this->get_enrolment_plugins(true); diff --git a/enrol/manual/amd/build/form-potential-user-selector.min.js b/enrol/manual/amd/build/form-potential-user-selector.min.js index 70944792b30..520f6313a69 100644 --- a/enrol/manual/amd/build/form-potential-user-selector.min.js +++ b/enrol/manual/amd/build/form-potential-user-selector.min.js @@ -1,2 +1,2 @@ -define ("enrol_manual/form-potential-user-selector",["jquery","core/ajax","core/templates","core/str"],function(a,b,c,d){return{processResults:function processResults(b,c){var d=[];if(a.isArray(c)){a.each(c,function(a,b){d.push({value:b.id,label:b._label})});return d}else{return c}},transport:function transport(e,f,g,h){var i,j=a(e).attr("courseid"),k=a(e).attr("userfields").split(",");if("undefined"==typeof j){j="1"}var l=a(e).attr("enrolid");if("undefined"==typeof l){l=""}var m=parseInt(a(e).attr("perpage"));if(isNaN(m)){m=100}i=b.call([{methodname:"core_enrol_get_potential_users",args:{courseid:j,enrolid:l,search:f,searchanywhere:!0,page:0,perpage:m+1}}]);i[0].then(function(b){var e=[],f=0;if(b.length<=m){a.each(b,function(b,d){var f=d,g=[];a.each(k,function(a,b){if("undefined"!=typeof d[b]&&""!==d[b]){f.hasidentity=!0;g.push(d[b])}});f.identity=g.join(", ");e.push(c.render("enrol_manual/form-user-selector-suggestion",f))});return a.when.apply(a.when,e).then(function(){var c=arguments;a.each(b,function(a,b){b._label=c[f];f++});g(b)})}else{return d.get_string("toomanyuserstoshow","core",">"+m).then(function(a){g(a)})}}).fail(h)}}}); +define ("enrol_manual/form-potential-user-selector",["jquery","core/ajax","core/templates","core/str"],function(a,b,c,d){return{processResults:function processResults(b,c){var d=[];if(a.isArray(c)){a.each(c,function(a,b){d.push({value:b.id,label:b._label})});return d}else{return c}},transport:function transport(e,f,g,h){var i,j=a(e).attr("courseid"),k=a(e).attr("userfields").split(",");if("undefined"==typeof j){j="1"}var l=a(e).attr("enrolid");if("undefined"==typeof l){l=""}var m=parseInt(a(e).attr("perpage"));if(isNaN(m)){m=100}i=b.call([{methodname:"core_enrol_get_potential_users",args:{courseid:j,enrolid:l,search:f,searchanywhere:!0,page:0,perpage:m+1}}]);i[0].then(function(b){var e=[],f=0;if(b.length<=m){a.each(b,function(b,d){var f=d,g=[];a.each(k,function(a,b){var c=/^profile_field_(.*)$/.exec(b);if(c){if(d.customfields){d.customfields.forEach(function(a){if(a.shortname===c[1]){f.hasidentity=!0;g.push(a.value)}})}}else{if("undefined"!=typeof d[b]&&""!==d[b]){f.hasidentity=!0;g.push(d[b])}}});f.identity=g.join(", ");e.push(c.render("enrol_manual/form-user-selector-suggestion",f))});return a.when.apply(a.when,e).then(function(){var c=arguments;a.each(b,function(a,b){b._label=c[f];f++});g(b)})}else{return d.get_string("toomanyuserstoshow","core",">"+m).then(function(a){g(a)})}}).fail(h)}}}); //# sourceMappingURL=form-potential-user-selector.min.js.map diff --git a/enrol/manual/amd/build/form-potential-user-selector.min.js.map b/enrol/manual/amd/build/form-potential-user-selector.min.js.map index 3f9f5baa2cd..33ca074160c 100644 --- a/enrol/manual/amd/build/form-potential-user-selector.min.js.map +++ b/enrol/manual/amd/build/form-potential-user-selector.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/form-potential-user-selector.js"],"names":["define","$","Ajax","Templates","Str","processResults","selector","results","users","isArray","each","index","user","push","value","id","label","_label","transport","query","success","failure","promise","courseid","attr","userfields","split","enrolid","perpage","parseInt","isNaN","call","methodname","args","search","searchanywhere","page","then","promises","i","length","ctx","identity","k","hasidentity","join","render","when","apply","arguments","get_string","toomanyuserstoshow","fail"],"mappings":"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","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 * Potential user selector module.\n *\n * @module enrol_manual/form-potential-user-selector\n * @class form-potential-user-selector\n * @package enrol_manual\n * @copyright 2016 Damyon Wiese\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax', 'core/templates', 'core/str'], function($, Ajax, Templates, Str) {\n\n return /** @alias module:enrol_manual/form-potential-user-selector */ {\n\n processResults: function(selector, results) {\n var users = [];\n if ($.isArray(results)) {\n $.each(results, function(index, user) {\n users.push({\n value: user.id,\n label: user._label\n });\n });\n return users;\n\n } else {\n return results;\n }\n },\n\n transport: function(selector, query, success, failure) {\n var promise;\n var courseid = $(selector).attr('courseid');\n var userfields = $(selector).attr('userfields').split(',');\n if (typeof courseid === \"undefined\") {\n courseid = '1';\n }\n var enrolid = $(selector).attr('enrolid');\n if (typeof enrolid === \"undefined\") {\n enrolid = '';\n }\n var perpage = parseInt($(selector).attr('perpage'));\n if (isNaN(perpage)) {\n perpage = 100;\n }\n\n promise = Ajax.call([{\n methodname: 'core_enrol_get_potential_users',\n args: {\n courseid: courseid,\n enrolid: enrolid,\n search: query,\n searchanywhere: true,\n page: 0,\n perpage: perpage + 1\n }\n }]);\n\n promise[0].then(function(results) {\n var promises = [],\n i = 0;\n\n if (results.length <= perpage) {\n // Render the label.\n $.each(results, function(index, user) {\n var ctx = user,\n identity = [];\n $.each(userfields, function(i, k) {\n if (typeof user[k] !== 'undefined' && user[k] !== '') {\n ctx.hasidentity = true;\n identity.push(user[k]);\n }\n });\n ctx.identity = identity.join(', ');\n promises.push(Templates.render('enrol_manual/form-user-selector-suggestion', ctx));\n });\n\n // Apply the label to the results.\n return $.when.apply($.when, promises).then(function() {\n var args = arguments;\n $.each(results, function(index, user) {\n user._label = args[i];\n i++;\n });\n success(results);\n return;\n });\n\n } else {\n return Str.get_string('toomanyuserstoshow', 'core', '>' + perpage).then(function(toomanyuserstoshow) {\n success(toomanyuserstoshow);\n return;\n });\n }\n\n }).fail(failure);\n }\n\n };\n\n});\n"],"file":"form-potential-user-selector.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/form-potential-user-selector.js"],"names":["define","$","Ajax","Templates","Str","processResults","selector","results","users","isArray","each","index","user","push","value","id","label","_label","transport","query","success","failure","promise","courseid","attr","userfields","split","enrolid","perpage","parseInt","isNaN","call","methodname","args","search","searchanywhere","page","then","promises","i","length","ctx","identity","k","result","exec","customfields","forEach","customfield","shortname","hasidentity","join","render","when","apply","arguments","get_string","toomanyuserstoshow","fail"],"mappings":"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","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 * Potential user selector module.\n *\n * @module enrol_manual/form-potential-user-selector\n * @class form-potential-user-selector\n * @package enrol_manual\n * @copyright 2016 Damyon Wiese\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax', 'core/templates', 'core/str'], function($, Ajax, Templates, Str) {\n\n return /** @alias module:enrol_manual/form-potential-user-selector */ {\n\n processResults: function(selector, results) {\n var users = [];\n if ($.isArray(results)) {\n $.each(results, function(index, user) {\n users.push({\n value: user.id,\n label: user._label\n });\n });\n return users;\n\n } else {\n return results;\n }\n },\n\n transport: function(selector, query, success, failure) {\n var promise;\n var courseid = $(selector).attr('courseid');\n var userfields = $(selector).attr('userfields').split(',');\n if (typeof courseid === \"undefined\") {\n courseid = '1';\n }\n var enrolid = $(selector).attr('enrolid');\n if (typeof enrolid === \"undefined\") {\n enrolid = '';\n }\n var perpage = parseInt($(selector).attr('perpage'));\n if (isNaN(perpage)) {\n perpage = 100;\n }\n\n promise = Ajax.call([{\n methodname: 'core_enrol_get_potential_users',\n args: {\n courseid: courseid,\n enrolid: enrolid,\n search: query,\n searchanywhere: true,\n page: 0,\n perpage: perpage + 1\n }\n }]);\n\n promise[0].then(function(results) {\n var promises = [],\n i = 0;\n\n if (results.length <= perpage) {\n // Render the label.\n const profileRegex = /^profile_field_(.*)$/;\n $.each(results, function(index, user) {\n var ctx = user,\n identity = [];\n $.each(userfields, function(i, k) {\n const result = profileRegex.exec(k);\n if (result) {\n if (user.customfields) {\n user.customfields.forEach(function(customfield) {\n if (customfield.shortname === result[1]) {\n ctx.hasidentity = true;\n identity.push(customfield.value);\n }\n\n });\n }\n } else {\n if (typeof user[k] !== 'undefined' && user[k] !== '') {\n ctx.hasidentity = true;\n identity.push(user[k]);\n }\n }\n });\n ctx.identity = identity.join(', ');\n promises.push(Templates.render('enrol_manual/form-user-selector-suggestion', ctx));\n });\n\n // Apply the label to the results.\n return $.when.apply($.when, promises).then(function() {\n var args = arguments;\n $.each(results, function(index, user) {\n user._label = args[i];\n i++;\n });\n success(results);\n return;\n });\n\n } else {\n return Str.get_string('toomanyuserstoshow', 'core', '>' + perpage).then(function(toomanyuserstoshow) {\n success(toomanyuserstoshow);\n return;\n });\n }\n\n }).fail(failure);\n }\n\n };\n\n});\n"],"file":"form-potential-user-selector.min.js"} \ No newline at end of file diff --git a/enrol/manual/amd/src/form-potential-user-selector.js b/enrol/manual/amd/src/form-potential-user-selector.js index c2e53d82fd5..f485cbf38c4 100644 --- a/enrol/manual/amd/src/form-potential-user-selector.js +++ b/enrol/manual/amd/src/form-potential-user-selector.js @@ -77,13 +77,27 @@ define(['jquery', 'core/ajax', 'core/templates', 'core/str'], function($, Ajax, if (results.length <= perpage) { // Render the label. + const profileRegex = /^profile_field_(.*)$/; $.each(results, function(index, user) { var ctx = user, identity = []; $.each(userfields, function(i, k) { - if (typeof user[k] !== 'undefined' && user[k] !== '') { - ctx.hasidentity = true; - identity.push(user[k]); + const result = profileRegex.exec(k); + if (result) { + if (user.customfields) { + user.customfields.forEach(function(customfield) { + if (customfield.shortname === result[1]) { + ctx.hasidentity = true; + identity.push(customfield.value); + } + + }); + } + } else { + if (typeof user[k] !== 'undefined' && user[k] !== '') { + ctx.hasidentity = true; + identity.push(user[k]); + } } }); ctx.identity = identity.join(', '); diff --git a/enrol/manual/classes/enrol_users_form.php b/enrol/manual/classes/enrol_users_form.php index 8fde2485872..959cf7224b8 100644 --- a/enrol/manual/classes/enrol_users_form.php +++ b/enrol/manual/classes/enrol_users_form.php @@ -93,8 +93,7 @@ class enrol_manual_enrol_users_form extends moodleform { 'courseid' => $course->id, 'enrolid' => $instance->id, 'perpage' => $CFG->maxusersperpage, - // TODO Does not support custom user profile fields (MDL-70456). - 'userfields' => implode(',', \core\user_fields::get_identity_fields($context, false)) + 'userfields' => implode(',', \core\user_fields::get_identity_fields($context, true)) ); $mform->addElement('autocomplete', 'userlist', get_string('selectusers', 'enrol_manual'), array(), $options); diff --git a/enrol/manual/tests/behat/quickenrolment.feature b/enrol/manual/tests/behat/quickenrolment.feature index 564f69f02e5..d0b720abc67 100644 --- a/enrol/manual/tests/behat/quickenrolment.feature +++ b/enrol/manual/tests/behat/quickenrolment.feature @@ -5,108 +5,111 @@ Feature: Teacher can search and enrol users one by one into the course I can search for the students and enrol them into the course Background: - Given the following "users" exist: - | username | firstname | lastname | email | - | teacher001 | Teacher | 001 | teacher001@example.com | - | student001 | Student | 001 | student001@example.com | - | student002 | Student | 002 | student002@example.com | - | student003 | Student | 003 | student003@example.com | - | student004 | Student | 004 | student004@example.com | - | student005 | Student | 005 | student005@example.com | - | student006 | Student | 006 | student006@example.com | - | student007 | Student | 007 | student007@example.com | - | student008 | Student | 008 | student008@example.com | - | student009 | Student | 009 | student009@example.com | - | student010 | Student | 010 | student010@example.com | - | student011 | Student | 011 | student011@example.com | - | student012 | Student | 012 | student012@example.com | - | student013 | Student | 013 | student013@example.com | - | student014 | Student | 014 | student014@example.com | - | student015 | Student | 015 | student015@example.com | - | student016 | Student | 016 | student016@example.com | - | student017 | Student | 017 | student017@example.com | - | student018 | Student | 018 | student018@example.com | - | student019 | Student | 019 | student019@example.com | - | student020 | Student | 020 | student020@example.com | - | student021 | Student | 021 | student021@example.com | - | student022 | Student | 022 | student022@example.com | - | student023 | Student | 023 | student023@example.com | - | student024 | Student | 024 | student024@example.com | - | student025 | Student | 025 | student025@example.com | - | student026 | Student | 026 | student026@example.com | - | student027 | Student | 027 | student027@example.com | - | student028 | Student | 028 | student028@example.com | - | student029 | Student | 029 | student029@example.com | - | student030 | Student | 030 | student030@example.com | - | student031 | Student | 031 | student031@example.com | - | student032 | Student | 032 | student032@example.com | - | student033 | Student | 033 | student033@example.com | - | student034 | Student | 034 | student034@example.com | - | student035 | Student | 035 | student035@example.com | - | student036 | Student | 036 | student036@example.com | - | student037 | Student | 037 | student037@example.com | - | student038 | Student | 038 | student038@example.com | - | student039 | Student | 039 | student039@example.com | - | student040 | Student | 040 | student040@example.com | - | student041 | Student | 041 | student041@example.com | - | student042 | Student | 042 | student042@example.com | - | student043 | Student | 043 | student043@example.com | - | student044 | Student | 044 | student044@example.com | - | student045 | Student | 045 | student045@example.com | - | student046 | Student | 046 | student046@example.com | - | student047 | Student | 047 | student047@example.com | - | student048 | Student | 048 | student048@example.com | - | student049 | Student | 049 | student049@example.com | - | student050 | Student | 050 | student050@example.com | - | student051 | Student | 051 | student051@example.com | - | student052 | Student | 052 | student052@example.com | - | student053 | Student | 053 | student053@example.com | - | student054 | Student | 054 | student054@example.com | - | student055 | Student | 055 | student055@example.com | - | student056 | Student | 056 | student056@example.com | - | student057 | Student | 057 | student057@example.com | - | student058 | Student | 058 | student058@example.com | - | student059 | Student | 059 | student059@example.com | - | student060 | Student | 060 | student060@example.com | - | student061 | Student | 061 | student061@example.com | - | student062 | Student | 062 | student062@example.com | - | student063 | Student | 063 | student063@example.com | - | student064 | Student | 064 | student064@example.com | - | student065 | Student | 065 | student065@example.com | - | student066 | Student | 066 | student066@example.com | - | student067 | Student | 067 | student067@example.com | - | student068 | Student | 068 | student068@example.com | - | student069 | Student | 069 | student069@example.com | - | student070 | Student | 070 | student070@example.com | - | student071 | Student | 071 | student071@example.com | - | student072 | Student | 072 | student072@example.com | - | student073 | Student | 073 | student073@example.com | - | student074 | Student | 074 | student074@example.com | - | student075 | Student | 075 | student075@example.com | - | student076 | Student | 076 | student076@example.com | - | student077 | Student | 077 | student077@example.com | - | student078 | Student | 078 | student078@example.com | - | student079 | Student | 079 | student079@example.com | - | student080 | Student | 080 | student080@example.com | - | student081 | Student | 081 | student081@example.com | - | student082 | Student | 082 | student082@example.com | - | student083 | Student | 083 | student083@example.com | - | student084 | Student | 084 | student084@example.com | - | student085 | Student | 085 | student085@example.com | - | student086 | Student | 086 | student086@example.com | - | student087 | Student | 087 | student087@example.com | - | student088 | Student | 088 | student088@example.com | - | student089 | Student | 089 | student089@example.com | - | student090 | Student | 090 | student090@example.com | - | student091 | Student | 091 | student091@example.com | - | student092 | Student | 092 | student092@example.com | - | student093 | Student | 093 | student093@example.com | - | student094 | Student | 094 | student094@example.com | - | student095 | Student | 095 | student095@example.com | - | student096 | Student | 096 | student096@example.com | - | student097 | Student | 097 | student097@example.com | - | student098 | Student | 098 | student098@example.com | - | student099 | Student | 099 | student099@example.com | + Given the following "custom profile fields" exist: + | datatype | shortname | name | + | text | customid | Custom user id | + And the following "users" exist: + | username | firstname | lastname | email | profile_field_customid | + | teacher001 | Teacher | 001 | teacher001@example.com | | + | student001 | Student | 001 | student001@example.com | Q994 | + | student002 | Student | 002 | student002@example.com | Q008 | + | student003 | Student | 003 | student003@example.com | Z442 | + | student004 | Student | 004 | student004@example.com | | + | student005 | Student | 005 | student005@example.com | | + | student006 | Student | 006 | student006@example.com | | + | student007 | Student | 007 | student007@example.com | | + | student008 | Student | 008 | student008@example.com | | + | student009 | Student | 009 | student009@example.com | | + | student010 | Student | 010 | student010@example.com | | + | student011 | Student | 011 | student011@example.com | | + | student012 | Student | 012 | student012@example.com | | + | student013 | Student | 013 | student013@example.com | | + | student014 | Student | 014 | student014@example.com | | + | student015 | Student | 015 | student015@example.com | | + | student016 | Student | 016 | student016@example.com | | + | student017 | Student | 017 | student017@example.com | | + | student018 | Student | 018 | student018@example.com | | + | student019 | Student | 019 | student019@example.com | | + | student020 | Student | 020 | student020@example.com | | + | student021 | Student | 021 | student021@example.com | | + | student022 | Student | 022 | student022@example.com | | + | student023 | Student | 023 | student023@example.com | | + | student024 | Student | 024 | student024@example.com | | + | student025 | Student | 025 | student025@example.com | | + | student026 | Student | 026 | student026@example.com | | + | student027 | Student | 027 | student027@example.com | | + | student028 | Student | 028 | student028@example.com | | + | student029 | Student | 029 | student029@example.com | | + | student030 | Student | 030 | student030@example.com | | + | student031 | Student | 031 | student031@example.com | | + | student032 | Student | 032 | student032@example.com | | + | student033 | Student | 033 | student033@example.com | | + | student034 | Student | 034 | student034@example.com | | + | student035 | Student | 035 | student035@example.com | | + | student036 | Student | 036 | student036@example.com | | + | student037 | Student | 037 | student037@example.com | | + | student038 | Student | 038 | student038@example.com | | + | student039 | Student | 039 | student039@example.com | | + | student040 | Student | 040 | student040@example.com | | + | student041 | Student | 041 | student041@example.com | | + | student042 | Student | 042 | student042@example.com | | + | student043 | Student | 043 | student043@example.com | | + | student044 | Student | 044 | student044@example.com | | + | student045 | Student | 045 | student045@example.com | | + | student046 | Student | 046 | student046@example.com | | + | student047 | Student | 047 | student047@example.com | | + | student048 | Student | 048 | student048@example.com | | + | student049 | Student | 049 | student049@example.com | | + | student050 | Student | 050 | student050@example.com | | + | student051 | Student | 051 | student051@example.com | | + | student052 | Student | 052 | student052@example.com | | + | student053 | Student | 053 | student053@example.com | | + | student054 | Student | 054 | student054@example.com | | + | student055 | Student | 055 | student055@example.com | | + | student056 | Student | 056 | student056@example.com | | + | student057 | Student | 057 | student057@example.com | | + | student058 | Student | 058 | student058@example.com | | + | student059 | Student | 059 | student059@example.com | | + | student060 | Student | 060 | student060@example.com | | + | student061 | Student | 061 | student061@example.com | | + | student062 | Student | 062 | student062@example.com | | + | student063 | Student | 063 | student063@example.com | | + | student064 | Student | 064 | student064@example.com | | + | student065 | Student | 065 | student065@example.com | | + | student066 | Student | 066 | student066@example.com | | + | student067 | Student | 067 | student067@example.com | | + | student068 | Student | 068 | student068@example.com | | + | student069 | Student | 069 | student069@example.com | | + | student070 | Student | 070 | student070@example.com | | + | student071 | Student | 071 | student071@example.com | | + | student072 | Student | 072 | student072@example.com | | + | student073 | Student | 073 | student073@example.com | | + | student074 | Student | 074 | student074@example.com | | + | student075 | Student | 075 | student075@example.com | | + | student076 | Student | 076 | student076@example.com | | + | student077 | Student | 077 | student077@example.com | | + | student078 | Student | 078 | student078@example.com | | + | student079 | Student | 079 | student079@example.com | | + | student080 | Student | 080 | student080@example.com | | + | student081 | Student | 081 | student081@example.com | | + | student082 | Student | 082 | student082@example.com | | + | student083 | Student | 083 | student083@example.com | | + | student084 | Student | 084 | student084@example.com | | + | student085 | Student | 085 | student085@example.com | | + | student086 | Student | 086 | student086@example.com | | + | student087 | Student | 087 | student087@example.com | | + | student088 | Student | 088 | student088@example.com | | + | student089 | Student | 089 | student089@example.com | | + | student090 | Student | 090 | student090@example.com | | + | student091 | Student | 091 | student091@example.com | | + | student092 | Student | 092 | student092@example.com | | + | student093 | Student | 093 | student093@example.com | | + | student094 | Student | 094 | student094@example.com | | + | student095 | Student | 095 | student095@example.com | | + | student096 | Student | 096 | student096@example.com | | + | student097 | Student | 097 | student097@example.com | | + | student098 | Student | 098 | student098@example.com | | + | student099 | Student | 099 | student099@example.com | | And the following "courses" exist: | fullname | shortname | format | startdate | | Course 001 | C001 | weeks | ##1 month ago## | @@ -189,6 +192,26 @@ Feature: Teacher can search and enrol users one by one into the course And I type "student100@example.com" And I should see "student100@example.com, 1234567892, 1234567893, ABC1, ABC2" + @javascript + Scenario: Custom user profile fields work for search and display, if user has permission + Given the following config values are set as admin: + | showuseridentity | email,profile_field_customid | + And I navigate to course participants + And I press "Enrol users" + When I set the field "Select users" to "Q994" + Then I should see "student001@example.com, Q994" + And I click on "Cancel" "button" in the "Enrol users" "dialogue" + And the following "permission overrides" exist: + | capability | permission | role | contextlevel | reference | + | moodle/site:viewuseridentity | Prevent | editingteacher | Course | C001 | + And I press "Enrol users" + # Do this by keyboard because the 'I set the field' step doesn't let you set it to a missing value. + And I press tab + And I press tab + And I press tab + And I type "Q994" + And I should see "No suggestions" + # The following tests are commented out as a result of MDL-66339. # @javascript # Scenario: Enrol user from participants page diff --git a/enrol/tests/course_enrolment_manager_test.php b/enrol/tests/course_enrolment_manager_test.php index 11c4146d410..0eaa3fe24f2 100644 --- a/enrol/tests/course_enrolment_manager_test.php +++ b/enrol/tests/course_enrolment_manager_test.php @@ -254,6 +254,127 @@ class core_course_enrolment_manager_testcase extends advanced_testcase { $this->assertArrayHasKey($this->users['user22']->id, $users); } + /** + * Sets up a custom profile field and the showuseridentity option, and creates a test user + * with suitable values set. + * + * @return stdClass Test user + */ + protected function setup_for_user_identity_tests(): stdClass { + // Configure extra fields to include one normal user field and one profile field, and + // set the values for a new test user. + $generator = $this->getDataGenerator(); + $generator->create_custom_profile_field(['datatype' => 'text', + 'shortname' => 'researchtopic', 'name' => 'Research topic']); + set_config('showuseridentity', 'email,department,profile_field_researchtopic'); + return $generator->create_user( + ['username' => 'newuser', 'department' => 'Amphibian studies', 'email' => 'x@x.org', + 'profile_field_researchtopic' => 'Frogs', 'imagealt' => 'Smart suit']); + } + + /** + * Checks that the get_users function returns the correct user fields. + */ + public function test_get_users_fields() { + global $PAGE; + + $this->resetAfterTest(); + $newuser = $this->setup_for_user_identity_tests(); + + // Enrol the user in test course. + $this->getDataGenerator()->enrol_user($newuser->id, $this->course->id, 'student'); + + // Get all users and fish out the one we're interested in. + $manager = new course_enrolment_manager($PAGE, $this->course); + $users = $manager->get_users('id'); + $user = $users[$newuser->id]; + + // Should include core required fields... + $this->assertEquals($newuser->id, $user->id); + + // ...And the ones specified in showuseridentity (one of which is also needed for user pics). + $this->assertEquals('Amphibian studies', $user->department); + $this->assertEquals('Frogs', $user->profile_field_researchtopic); + $this->assertEquals('x@x.org', $user->email); + + // And the ones necessary for user pics. + $this->assertEquals('Smart suit', $user->imagealt); + + // But not some random other field like city. + $this->assertObjectNotHasAttribute('city', $user); + } + + /** + * Checks that the get_other_users function returns the correct user fields. + */ + public function test_get_other_users_fields() { + global $PAGE, $DB; + + $this->resetAfterTest(); + + // Configure extra fields to include one normal user field and one profile field, and + // set the values for a new test user. + $newuser = $this->setup_for_user_identity_tests(); + $context = \context_course::instance($this->course->id); + role_assign($DB->get_field('role', 'id', ['shortname' => 'manager']), $newuser->id, $context->id); + + // Get the 'other' (role but not enrolled) users and fish out the one we're interested in. + $manager = new course_enrolment_manager($PAGE, $this->course); + $users = array_values($manager->get_other_users('id')); + $user = $users[0]; + + // Should include core required fields... + $this->assertEquals($newuser->id, $user->id); + + // ...And the ones specified in showuseridentity (one of which is also needed for user pics). + $this->assertEquals('Amphibian studies', $user->department); + $this->assertEquals('Frogs', $user->profile_field_researchtopic); + $this->assertEquals('x@x.org', $user->email); + + // And the ones necessary for user pics. + $this->assertEquals('Smart suit', $user->imagealt); + + // But not some random other field like city. + $this->assertObjectNotHasAttribute('city', $user); + } + + /** + * Checks that the get_potential_users function returns the correct user fields. + */ + public function test_get_potential_users_fields() { + global $PAGE; + + $this->resetAfterTest(); + + // Configure extra fields to include one normal user field and one profile field, and + // set the values for a new test user. + $newuser = $this->setup_for_user_identity_tests(); + + // Get the 'potential' (not enrolled) users and fish out the one we're interested in. + $manager = new course_enrolment_manager($PAGE, $this->course); + foreach (enrol_get_instances($this->course->id, true) as $enrolinstance) { + if ($enrolinstance->enrol === 'manual') { + $enrolid = $enrolinstance->id; + } + } + $users = array_values($manager->get_potential_users($enrolid)); + $user = $users[0][$newuser->id]; + + // Should include core required fields... + $this->assertEquals($newuser->id, $user->id); + + // ...And the ones specified in showuseridentity (one of which is also needed for user pics). + $this->assertEquals('Amphibian studies', $user->department); + $this->assertEquals('Frogs', $user->profile_field_researchtopic); + $this->assertEquals('x@x.org', $user->email); + + // And the ones necessary for user pics. + $this->assertEquals('Smart suit', $user->imagealt); + + // But not some random other field like city. + $this->assertObjectNotHasAttribute('city', $user); + } + /** * Test get_potential_users without returnexactcount param. * @@ -290,6 +411,58 @@ class core_course_enrolment_manager_testcase extends advanced_testcase { } } + /** + * Tests get_potential_users when the search term includes a custom field. + */ + public function test_get_potential_users_search_fields() { + global $PAGE; + + $this->resetAfterTest(); + + // Configure extra fields to include one normal user field and one profile field, and + // set the values for a new test user. + $newuser = $this->setup_for_user_identity_tests(); + + // Set up the enrolment manager. + $manager = new course_enrolment_manager($PAGE, $this->course); + foreach (enrol_get_instances($this->course->id, true) as $enrolinstance) { + if ($enrolinstance->enrol === 'manual') { + $enrolid = $enrolinstance->id; + } + } + + // Search for text included in a 'standard' (user table) identity field. + $users = array_values($manager->get_potential_users($enrolid, 'Amphibian studies')); + $this->assertEquals([$newuser->id], array_keys($users[0])); + + // And for text included in a custom field. + $users = array_values($manager->get_potential_users($enrolid, 'Frogs')); + $this->assertEquals([$newuser->id], array_keys($users[0])); + + // With partial matches. + $users = array_values($manager->get_potential_users($enrolid, 'Amphibian')); + $this->assertEquals([$newuser->id], array_keys($users[0])); + $users = array_values($manager->get_potential_users($enrolid, 'Fro')); + $this->assertEquals([$newuser->id], array_keys($users[0])); + + // With partial in-the-middle matches. + $users = array_values($manager->get_potential_users($enrolid, 'phibian')); + $this->assertEquals([], array_keys($users[0])); + $users = array_values($manager->get_potential_users($enrolid, 'rog')); + $this->assertEquals([], array_keys($users[0])); + $users = array_values($manager->get_potential_users($enrolid, 'phibian', true)); + $this->assertEquals([$newuser->id], array_keys($users[0])); + $users = array_values($manager->get_potential_users($enrolid, 'rog', true)); + $this->assertEquals([$newuser->id], array_keys($users[0])); + + // If the current user doesn't have access to identity fields then these searches won't work. + $this->setUser($this->getDataGenerator()->create_user()); + $users = array_values($manager->get_potential_users($enrolid, 'Amphibian studies')); + $this->assertEquals([], array_keys($users[0])); + $users = array_values($manager->get_potential_users($enrolid, 'Frogs')); + $this->assertEquals([], array_keys($users[0])); + } + /** * Test search_other_users with returnexactcount param. * diff --git a/enrol/tests/externallib_test.php b/enrol/tests/externallib_test.php index c126c90b312..3db6a354395 100644 --- a/enrol/tests/externallib_test.php +++ b/enrol/tests/externallib_test.php @@ -1476,4 +1476,79 @@ class core_enrol_externallib_testcase extends externallib_advanced_testcase { $result = core_enrol_external::search_users($course1->id, 'yada yada', true, 0, 30); $this->assertCount(0, $result); } + + /** + * Tests the get_potential_users external function (not too much detail because the back-end + * is covered in another test). + */ + public function test_get_potential_users(): void { + $this->resetAfterTest(); + + // Create a couple of custom profile fields, one of which is in user identity. + $generator = $this->getDataGenerator(); + $generator->create_custom_profile_field(['datatype' => 'text', + 'shortname' => 'researchtopic', 'name' => 'Research topic']); + $generator->create_custom_profile_field(['datatype' => 'text', + 'shortname' => 'specialid', 'name' => 'Special id']); + set_config('showuseridentity', 'department,profile_field_specialid'); + + // Create a course. + $course = $generator->create_course(); + + // Get enrol id for manual enrol plugin. + foreach (enrol_get_instances($course->id, true) as $instance) { + if ($instance->enrol === 'manual') { + $enrolid = $instance->id; + } + } + + // Create a couple of test users. + $user1 = $generator->create_user(['firstname' => 'Eigh', 'lastname' => 'User', + 'department' => 'Amphibians', 'profile_field_specialid' => 'Q123', + 'profile_field_researchtopic' => 'Frogs']); + $user2 = $generator->create_user(['firstname' => 'Anne', 'lastname' => 'Other', + 'department' => 'Amphibians', 'profile_field_specialid' => 'Q456', + 'profile_field_researchtopic' => 'Toads']); + + // Do this as admin user. + $this->setAdminUser(); + + // Get potential users and extract the 2 we care about. + $result = core_enrol_external::get_potential_users($course->id, $enrolid, '', false, 0, 10); + $result1 = $this->extract_user_from_result($result, $user1->id); + $result2 = $this->extract_user_from_result($result, $user2->id); + + // Check the fields are the expected ones. + $this->assertEquals(['id', 'fullname', 'customfields', + 'profileimageurl', 'profileimageurlsmall', 'department'], array_keys($result1)); + $this->assertEquals('Eigh User', $result1['fullname']); + $this->assertEquals('Amphibians', $result1['department']); + + // Check the custom fields ONLY include the user identity one. + $fieldvalues = []; + foreach ($result1['customfields'] as $customfield) { + $fieldvalues[$customfield['shortname']] = $customfield['value']; + } + $this->assertEquals(['specialid'], array_keys($fieldvalues)); + $this->AssertEquals('Q123', $fieldvalues['specialid']); + + // Just check user 2 is the right user. + $this->assertEquals('Anne Other', $result2['fullname']); + } + + /** + * Utility function to get one user out of the get_potential_users result. + * + * @param array $result Result array + * @param int $userid User id + * @return array Data for that user + */ + protected function extract_user_from_result(array $result, int $userid): array { + foreach ($result as $item) { + if ($item['id'] == $userid) { + return $item; + } + } + $this->fail('User not in result: ' . $userid); + } } diff --git a/grade/report/grader/lib.php b/grade/report/grader/lib.php index 4409d500963..38a6659edaf 100644 --- a/grade/report/grader/lib.php +++ b/grade/report/grader/lib.php @@ -1963,7 +1963,7 @@ class grade_report_grader extends grade_report { foreach ($extrafields as $field) { $fieldlink = html_writer::link(new moodle_url($this->baseurl, - array('sortitemid'=>$field)), \core\user_fields::get_display_name($field)); + array('sortitemid' => $field)), \core\user_fields::get_display_name($field)); $arrows[$field] = $fieldlink; if ($field == $this->sortitemid) { diff --git a/lib/moodlelib.php b/lib/moodlelib.php index f6fea8ee107..107016a06a5 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -3645,9 +3645,9 @@ function fullname($user, $override=false) { * @return object User name fields. */ function username_load_fields_from_object($addtoobject, $secondobject, $prefix = null, $additionalfields = null) { - $fields = \core\user_fields::get_name_fields(); - foreach ($fields as &$field) { - $field = $prefix . $field; + $fields = []; + foreach (\core\user_fields::get_name_fields() as $field) { + $fields[$field] = $prefix . $field; } if ($additionalfields) { // Additional fields can specify their own 'alias' such as 'id' => 'userid'. This checks to see if diff --git a/lib/outputcomponents.php b/lib/outputcomponents.php index a3d3b1f3c86..2a76c0f34e9 100644 --- a/lib/outputcomponents.php +++ b/lib/outputcomponents.php @@ -230,7 +230,7 @@ class user_picture implements renderable { } if ($needrec) { - $this->user = $DB->get_record('user', array('id'=>$user->id), + $this->user = $DB->get_record('user', array('id' => $user->id), implode(',', \core\user_fields::get_picture_fields()), MUST_EXIST); } else { $this->user = clone($user); diff --git a/mod/chat/lib.php b/mod/chat/lib.php index 9a2b1bf0793..ee019d6088e 100644 --- a/mod/chat/lib.php +++ b/mod/chat/lib.php @@ -907,7 +907,7 @@ function chat_format_message($message, $courseid, $currentuser, $chatlastrow=nul if (isset($users[$message->userid])) { $user = $users[$message->userid]; - } else if ($user = $DB->get_record('user', array('id' => $message->userid), implode(',', \core\user_fields::get_picture_fields()))) { + } else if ($user = $DB->get_record('user', ['id' => $message->userid], implode(',', \core\user_fields::get_picture_fields()))) { $users[$message->userid] = $user; } else { return null; @@ -938,7 +938,8 @@ function chat_format_message_theme ($message, $chatuser, $currentuser, $grouping if (isset($users[$message->userid])) { $sender = $users[$message->userid]; - } else if ($sender = $DB->get_record('user', array('id' => $message->userid), implode(',', \core\user_fields::get_picture_fields()))) { + } else if ($sender = $DB->get_record('user', array('id' => $message->userid), + implode(',', \core\user_fields::get_picture_fields()))) { $users[$message->userid] = $sender; } else { return null; diff --git a/report/completion/index.php b/report/completion/index.php index a94b83ee2fd..dd862a8fdee 100644 --- a/report/completion/index.php +++ b/report/completion/index.php @@ -512,7 +512,7 @@ if (!$csv) { $row[] = get_string('id', 'report_completion'); $row[] = get_string('name', 'report_completion'); foreach ($extrafields as $field) { - $row[] = \core\user_fields::get_display_name($field); + $row[] = \core\user_fields::get_display_name($field); } // Add activity headers diff --git a/report/log/locallib.php b/report/log/locallib.php index 4440e1dd76d..4b6c70bf5a8 100644 --- a/report/log/locallib.php +++ b/report/log/locallib.php @@ -296,7 +296,7 @@ function report_log_print_mnet_selector_form($hostid, $course, $selecteduser=0, } else { // this may be a lot of users :-( $userfieldsapi = \core\user_fields::for_name(); - $courseusers = $DB->get_records('user', array('deleted'=>0), 'lastaccess DESC', 'id, ' . + $courseusers = $DB->get_records('user', array('deleted' => 0), 'lastaccess DESC', 'id, ' . $userfieldsapi->get_sql('', false, '', '', false)->selects, $limitfrom, $limitnum); } diff --git a/user/lib.php b/user/lib.php index 9d01bc00321..65ffbcebd95 100644 --- a/user/lib.php +++ b/user/lib.php @@ -302,7 +302,8 @@ function user_get_user_details($user, $course = null, array $userfields = array( $currentuser = ($user->id == $USER->id); $isadmin = is_siteadmin($USER); - // TODO Does not support custom user profile fields (MDL-70456). + // This does not need to include custom profile fields as it is only used to check specific + // fields below. $showuseridentityfields = \core\user_fields::get_identity_fields($context, false); if (!empty($course)) { diff --git a/userpix/index.php b/userpix/index.php index 99a7758b62f..f3d035cb01b 100644 --- a/userpix/index.php +++ b/userpix/index.php @@ -23,7 +23,8 @@ $PAGE->set_title($title); $PAGE->set_heading($title); echo $OUTPUT->header(); -$rs = $DB->get_recordset_select("user", "deleted = 0 AND picture > 0", array(), "lastaccess DESC", implode(',', \core\user_fields::get_picture_fields())); +$rs = $DB->get_recordset_select("user", "deleted = 0 AND picture > 0", array(), "lastaccess DESC", + implode(',', \core\user_fields::get_picture_fields())); foreach ($rs as $user) { $fullname = s(fullname($user)); echo "wwwroot/user/view.php?id=$user->id&course=1\" ".