Merge branch 'MDL-45242-m311' of https://github.com/sammarshallou/moodle into MOODLE_311_STABLE
This commit is contained in:
@@ -124,8 +124,9 @@ class task_log_table extends \table_sql {
|
||||
$sort = "ORDER BY $sort";
|
||||
}
|
||||
|
||||
$extrafields = get_extra_user_fields(\context_system::instance());
|
||||
$userfields = \user_picture::fields('u', $extrafields, 'userid2', 'user');
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$userfieldsapi = \core\user_fields::for_identity(\context_system::instance(), false)->with_userpic();
|
||||
$userfields = $userfieldsapi->get_sql('u', false, 'user', 'userid2', false)->selects;
|
||||
|
||||
$where = '';
|
||||
if (!empty($this->sql->where)) {
|
||||
|
||||
@@ -271,7 +271,8 @@ if ($roleid) {
|
||||
foreach ($assignableroles as $roleid => $notused) {
|
||||
$roleusers = '';
|
||||
if (0 < $assigncounts[$roleid] && $assigncounts[$roleid] <= MAX_USERS_TO_LIST_PER_ROLE) {
|
||||
$userfields = 'u.id, u.username, ' . get_all_user_name_fields(true, 'u');
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$userfields = 'u.id, u.username' . $userfieldsapi->get_sql('u')->selects;
|
||||
$roleusers = get_role_users($roleid, $context, false, $userfields);
|
||||
if (!empty($roleusers)) {
|
||||
$strroleusers = array();
|
||||
|
||||
+30
-13
@@ -214,21 +214,38 @@ if ($hassiteconfig
|
||||
// with moodle/site:viewuseridentity).
|
||||
// Options include fields from the user table that might be helpful to
|
||||
// distinguish when adding or listing users ('I want to add the John
|
||||
// Smith from Science faculty').
|
||||
// Custom user profile fields are not currently supported.
|
||||
// Smith from Science faculty') and any custom profile fields.
|
||||
$temp->add(new admin_setting_configmulticheckbox('showuseridentity',
|
||||
new lang_string('showuseridentity', 'admin'),
|
||||
new lang_string('showuseridentity_desc', 'admin'), array('email' => 1), array(
|
||||
'username' => new lang_string('username'),
|
||||
'idnumber' => new lang_string('idnumber'),
|
||||
'email' => new lang_string('email'),
|
||||
'phone1' => new lang_string('phone1'),
|
||||
'phone2' => new lang_string('phone2'),
|
||||
'department' => new lang_string('department'),
|
||||
'institution' => new lang_string('institution'),
|
||||
'city' => new lang_string('city'),
|
||||
'country' => new lang_string('country'),
|
||||
)));
|
||||
new lang_string('showuseridentity_desc', 'admin'), ['email' => 1],
|
||||
function() {
|
||||
global $DB;
|
||||
|
||||
// Basic fields available in user table.
|
||||
$fields = [
|
||||
'username' => new lang_string('username'),
|
||||
'idnumber' => new lang_string('idnumber'),
|
||||
'email' => new lang_string('email'),
|
||||
'phone1' => new lang_string('phone1'),
|
||||
'phone2' => new lang_string('phone2'),
|
||||
'department' => new lang_string('department'),
|
||||
'institution' => new lang_string('institution'),
|
||||
'city' => new lang_string('city'),
|
||||
'country' => new lang_string('country'),
|
||||
];
|
||||
|
||||
// Custom profile fields.
|
||||
$profilefields = $DB->get_records('user_info_field', ['datatype' => 'text'], 'sortorder ASC');
|
||||
foreach ($profilefields as $key => $field) {
|
||||
// Only reasonable-length fields can be used as identity fields.
|
||||
if ($field->param2 > 255) {
|
||||
continue;
|
||||
}
|
||||
$fields['profile_field_' . $field->shortname] = $field->name . ' *';
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}));
|
||||
$setting = new admin_setting_configtext('fullnamedisplay', new lang_string('fullnamedisplay', 'admin'),
|
||||
new lang_string('configfullnamedisplay', 'admin'), 'language', PARAM_TEXT, 50);
|
||||
$setting->set_force_ltr(true);
|
||||
|
||||
@@ -5,12 +5,16 @@ Feature: An administrator can filter user accounts by role, cohort and other pro
|
||||
I need to filter the users account list using different filter
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname | email | auth | confirmed | lastip | institution | department |
|
||||
| user1 | User | One | one@example.com | manual | 0 | 127.0.1.1 | moodle | red |
|
||||
| user2 | User | Two | two@example.com | ldap | 1 | 0.0.0.0 | moodle | blue |
|
||||
| user3 | User | Three | three@example.com | manual | 1 | 0.0.0.0 | | |
|
||||
| user4 | User | Four | four@example.com | ldap | 0 | 127.0.1.2 | | |
|
||||
Given the following "custom profile fields" exist:
|
||||
| datatype | shortname | name |
|
||||
| text | frog | Favourite frog |
|
||||
| text | undead | Type of undead |
|
||||
And the following "users" exist:
|
||||
| username | firstname | lastname | email | auth | confirmed | lastip | institution | department | profile_field_frog | profile_field_undead |
|
||||
| user1 | User | One | one@example.com | manual | 0 | 127.0.1.1 | moodle | red | Kermit | |
|
||||
| user2 | User | Two | two@example.com | ldap | 1 | 0.0.0.0 | moodle | blue | Mr Toad | Zombie |
|
||||
| user3 | User | Three | three@example.com | manual | 1 | 0.0.0.0 | | | | |
|
||||
| user4 | User | Four | four@example.com | ldap | 0 | 127.0.1.2 | | | | |
|
||||
And the following "cohorts" exist:
|
||||
| name | idnumber |
|
||||
| Cohort 1 | CH1 |
|
||||
@@ -116,3 +120,20 @@ Feature: An administrator can filter user accounts by role, cohort and other pro
|
||||
And I press "Add filter"
|
||||
And I should see "User One"
|
||||
And I should not see "User Two"
|
||||
|
||||
Scenario: Filter users by custom profile field (specific or any)
|
||||
When I set the field "id_profile_fld" to "Favourite frog"
|
||||
And I set the field "id_profile" to "Kermit"
|
||||
And I press "Add filter"
|
||||
Then I should see "User One"
|
||||
And I should not see "User Two"
|
||||
And I should not see "User Three"
|
||||
And I should not see "User Four"
|
||||
And I press "Remove all filters"
|
||||
And I set the field "id_profile_fld" to "any field"
|
||||
And I set the field "id_profile" to "Zombie"
|
||||
And I press "Add filter"
|
||||
And I should see "User Two"
|
||||
And I should not see "User One"
|
||||
And I should not see "User Three"
|
||||
And I should not see "User Four"
|
||||
|
||||
@@ -126,7 +126,8 @@ class cohort_role_assignments_table extends table_sql {
|
||||
* Setup the headers for the table.
|
||||
*/
|
||||
protected function define_table_columns() {
|
||||
$extrafields = get_extra_user_fields($this->context);
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$extrafields = \core\user_fields::get_identity_fields($this->context, false);
|
||||
|
||||
// Define headers and columns.
|
||||
$cols = array(
|
||||
@@ -170,14 +171,12 @@ class cohort_role_assignments_table extends table_sql {
|
||||
protected function get_sql_and_params($count = false) {
|
||||
$fields = 'uca.id, uca.cohortid, uca.userid, uca.roleid, ';
|
||||
$fields .= 'c.name as cohortname, c.idnumber as cohortidnumber, c.contextid as cohortcontextid, ';
|
||||
$fields .= 'c.visible as cohortvisible, c.description as cohortdescription, c.theme as cohorttheme, ';
|
||||
$fields .= 'c.visible as cohortvisible, c.description as cohortdescription, c.theme as cohorttheme';
|
||||
|
||||
// Add extra user fields that we need for the graded user.
|
||||
$extrafields = get_extra_user_fields($this->context);
|
||||
foreach ($extrafields as $field) {
|
||||
$fields .= 'u.' . $field . ', ';
|
||||
}
|
||||
$fields .= get_all_user_name_fields(true, 'u');
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$userfieldsapi = \core\user_fields::for_identity($this->context, false)->with_name();
|
||||
$fields .= $userfieldsapi->get_sql('u')->selects;
|
||||
|
||||
if ($count) {
|
||||
$select = "COUNT(1)";
|
||||
|
||||
@@ -189,7 +189,8 @@ class api {
|
||||
$dpos = [];
|
||||
$context = context_system::instance();
|
||||
foreach ($dporoles as $roleid) {
|
||||
$allnames = get_all_user_name_fields(true, 'u');
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$allnames = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$fields = 'u.id, u.confirmed, u.username, '. $allnames . ', ' .
|
||||
'u.maildisplay, u.mailformat, u.maildigest, u.email, u.emailstop, u.city, '.
|
||||
'u.country, u.picture, u.idnumber, u.department, u.institution, '.
|
||||
|
||||
@@ -700,13 +700,15 @@ class external extends external_api {
|
||||
self::validate_context($context);
|
||||
require_capability('tool/dataprivacy:managedatarequests', $context);
|
||||
|
||||
$allusernames = get_all_user_name_fields(true);
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$allusernames = $userfieldsapi->get_sql('', false, '', '', false)->selects;
|
||||
// Exclude admins and guest user.
|
||||
$excludedusers = array_keys(get_admins()) + [guest_user()->id];
|
||||
$sort = 'lastname ASC, firstname ASC';
|
||||
$fields = 'id,' . $allusernames;
|
||||
|
||||
$extrafields = get_extra_user_fields($context);
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$extrafields = \core\user_fields::get_identity_fields($context, false);
|
||||
if (!empty($extrafields)) {
|
||||
$fields .= ',' . implode(',', $extrafields);
|
||||
}
|
||||
|
||||
@@ -187,7 +187,8 @@ class helper {
|
||||
global $DB;
|
||||
|
||||
// Get users that the user has role assignments to.
|
||||
$allusernames = get_all_user_name_fields(true, 'u');
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$allusernames = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$sql = "SELECT u.id, $allusernames
|
||||
FROM {role_assignments} ra, {context} c, {user} u
|
||||
WHERE ra.userid = :userid
|
||||
|
||||
@@ -62,7 +62,8 @@ class tool_dataprivacy_data_request_form extends \core\form\persistent {
|
||||
'valuehtmlcallback' => function($value) {
|
||||
global $OUTPUT;
|
||||
|
||||
$allusernames = get_all_user_name_fields(true);
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$allusernames = $userfieldsapi->get_sql('', false, '', '', false)->selects;
|
||||
$fields = 'id, email, ' . $allusernames;
|
||||
$user = \core_user::get_user($value, $fields);
|
||||
$useroptiondata = [
|
||||
|
||||
@@ -878,11 +878,10 @@ class external extends external_api {
|
||||
list($filtercapsql, $filtercapparams) = api::filter_users_with_capability_on_user_context_sql($cap,
|
||||
$USER->id, SQL_PARAMS_NAMED);
|
||||
|
||||
$extrasearchfields = array();
|
||||
if (!empty($CFG->showuseridentity) && has_capability('moodle/site:viewuseridentity', $context)) {
|
||||
$extrasearchfields = explode(',', $CFG->showuseridentity);
|
||||
}
|
||||
$fields = \user_picture::fields('u', $extrasearchfields);
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$userfieldsapi = \core\user_fields::for_identity($context, false)->with_userpic();
|
||||
$fields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$extrasearchfields = $userfieldsapi->get_required_fields([\core\user_fields::PURPOSE_IDENTITY]);
|
||||
|
||||
list($wheresql, $whereparams) = users_search_sql($query, 'u', true, $extrasearchfields);
|
||||
list($sortsql, $sortparams) = users_order_by_sql('u', $query, $context);
|
||||
|
||||
@@ -92,7 +92,8 @@ class template_plans_table extends table_sql {
|
||||
* Setup the headers for the table.
|
||||
*/
|
||||
protected function define_table_columns() {
|
||||
$extrafields = get_extra_user_fields($this->context);
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$extrafields = \core\user_fields::get_identity_fields($this->context, false);
|
||||
|
||||
// Define headers and columns.
|
||||
$cols = array(
|
||||
@@ -132,14 +133,12 @@ class template_plans_table extends table_sql {
|
||||
* @return array containing sql to use and an array of params.
|
||||
*/
|
||||
protected function get_sql_and_params($count = false) {
|
||||
$fields = 'p.id, p.userid, p.name, ';
|
||||
$fields = 'p.id, p.userid, p.name';
|
||||
|
||||
// Add extra user fields that we need for the graded user.
|
||||
$extrafields = get_extra_user_fields($this->context);
|
||||
foreach ($extrafields as $field) {
|
||||
$fields .= 'u.' . $field . ', ';
|
||||
}
|
||||
$fields .= get_all_user_name_fields(true, 'u');
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$userfieldsapi = \core\user_fields::for_identity($this->context, false)->with_name();
|
||||
$fields .= $userfieldsapi->get_sql('u')->selects;
|
||||
|
||||
if ($count) {
|
||||
$select = "COUNT(1)";
|
||||
|
||||
@@ -91,8 +91,10 @@ class acceptances_table extends \table_sql {
|
||||
}
|
||||
}
|
||||
|
||||
$extrafields = get_extra_user_fields(\context_system::instance());
|
||||
$userfields = \user_picture::fields('u', $extrafields);
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$userfieldsapi = \core\user_fields::for_identity(\context_system::instance(), false)->with_userpic();
|
||||
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$extrafields = $userfieldsapi->get_required_fields([\core\user_fields::PURPOSE_IDENTITY]);
|
||||
|
||||
$this->set_sql("$userfields",
|
||||
"{user} u",
|
||||
@@ -103,7 +105,7 @@ class acceptances_table extends \table_sql {
|
||||
}
|
||||
$this->add_column_header('fullname', get_string('fullnameuser', 'core'));
|
||||
foreach ($extrafields as $field) {
|
||||
$this->add_column_header($field, get_user_field_name($field));
|
||||
$this->add_column_header($field, \core\user_fields::get_display_name($field));
|
||||
}
|
||||
|
||||
if (!$this->is_downloading() && !has_capability('tool/policy:acceptbehalf', \context_system::instance())) {
|
||||
@@ -168,7 +170,8 @@ class acceptances_table extends \table_sql {
|
||||
* Helper configuration method.
|
||||
*/
|
||||
protected function configure_for_single_version() {
|
||||
$userfieldsmod = get_all_user_name_fields(true, 'm', null, 'mod');
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$userfieldsmod = $userfieldsapi->get_sql('m', false, 'mod', '', false)->selects;
|
||||
$v = key($this->versionids);
|
||||
$this->sql->fields .= ", $userfieldsmod, a{$v}.status AS status{$v}, a{$v}.note, ".
|
||||
"a{$v}.timemodified, a{$v}.usermodified AS usermodified{$v}";
|
||||
@@ -643,4 +646,4 @@ class acceptances_table extends \table_sql {
|
||||
}
|
||||
return parent::other_cols($column, $row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,10 +343,10 @@ class api {
|
||||
global $DB;
|
||||
|
||||
$ctxfields = context_helper::get_preload_record_columns_sql('c');
|
||||
$namefields = get_all_user_name_fields(true, 'u');
|
||||
$pixfields = user_picture::fields('u', $extrafields);
|
||||
$userfieldsapi = \core\user_fields::for_name()->with_userpic()->including(...($extrafields ?? []));
|
||||
$userfields = $userfieldsapi->get_sql('u')->selects;
|
||||
|
||||
$sql = "SELECT $ctxfields, $namefields, $pixfields
|
||||
$sql = "SELECT $ctxfields $userfields
|
||||
FROM {role_assignments} ra
|
||||
JOIN {context} c ON c.contextlevel = ".CONTEXT_USER." AND ra.contextid = c.id
|
||||
JOIN {user} u ON c.instanceid = u.id
|
||||
@@ -682,7 +682,8 @@ class api {
|
||||
$vsql = ' AND a.policyversionid ' . $vsql;
|
||||
}
|
||||
|
||||
$userfieldsmod = get_all_user_name_fields(true, 'm', null, 'mod');
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$userfieldsmod = $userfieldsapi->get_sql('m', false, 'mod', '', false)->selects;
|
||||
$sql = "SELECT u.id AS mainuserid, a.policyversionid, a.status, a.lang, a.timemodified, a.usermodified, a.note,
|
||||
u.policyagreed, $userfieldsmod
|
||||
FROM {user} u
|
||||
|
||||
@@ -128,7 +128,8 @@ class accept_policy extends \moodleform {
|
||||
$usernames = [];
|
||||
list($sql, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED);
|
||||
$params['usercontextlevel'] = CONTEXT_USER;
|
||||
$users = $DB->get_records_sql("SELECT u.id, " . get_all_user_name_fields(true, 'u') . ", " .
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$users = $DB->get_records_sql("SELECT u.id" . $userfieldsapi->get_sql('u')->selects . ", " .
|
||||
\context_helper::get_preload_record_columns_sql('ctx') .
|
||||
" FROM {user} u JOIN {context} ctx ON ctx.contextlevel=:usercontextlevel AND ctx.instanceid = u.id
|
||||
WHERE u.id " . $sql, $params);
|
||||
|
||||
@@ -155,7 +155,7 @@ class process {
|
||||
'interests',
|
||||
);
|
||||
// Include all name fields.
|
||||
$this->standardfields = array_merge($this->standardfields, get_all_user_name_fields());
|
||||
$this->standardfields = array_merge($this->standardfields, \core\user_fields::get_name_fields());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+5
-5
@@ -183,13 +183,13 @@
|
||||
// These columns are always shown in the users list.
|
||||
$requiredcolumns = array('city', 'country', 'lastaccess');
|
||||
// Extra columns containing the extra user fields, excluding the required columns (city and country, to be specific).
|
||||
$extracolumns = get_extra_user_fields($context, $requiredcolumns);
|
||||
$userfields = \core\user_fields::for_identity($context, true)->with_name()->excluding(...$requiredcolumns);
|
||||
$extracolumns = $userfields->get_required_fields();
|
||||
// Get all user name fields as an array.
|
||||
$allusernamefields = get_all_user_name_fields(false, null, null, null, true);
|
||||
$columns = array_merge($allusernamefields, $extracolumns, $requiredcolumns);
|
||||
$columns = array_merge($extracolumns, $requiredcolumns);
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$string[$column] = get_user_field_name($column);
|
||||
$string[$column] = \core\user_fields::get_display_name($column);
|
||||
if ($sort != $column) {
|
||||
$columnicon = "";
|
||||
if ($column == "lastaccess") {
|
||||
@@ -226,7 +226,7 @@
|
||||
}
|
||||
|
||||
// Order in string will ensure that the name columns are in the correct order.
|
||||
$usernames = order_in_string($allusernamefields, $fullnamesetting);
|
||||
$usernames = order_in_string($extracolumns, $fullnamesetting);
|
||||
$fullnamedisplay = array();
|
||||
foreach ($usernames as $name) {
|
||||
// Use the link from $$column for sorting on the user's name.
|
||||
|
||||
@@ -64,7 +64,8 @@ if (count($cohorts) < 2) {
|
||||
}
|
||||
|
||||
$countries = get_string_manager()->get_list_of_countries(true);
|
||||
$namefields = get_all_user_name_fields(true);
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$namefields = $userfieldsapi->get_sql('', false, '', '', false)->selects;
|
||||
foreach ($users as $key => $id) {
|
||||
$user = $DB->get_record('user', array('id' => $id), 'id, ' . $namefields . ', username,
|
||||
email, country, lastaccess, city, deleted');
|
||||
|
||||
@@ -24,7 +24,8 @@ echo $OUTPUT->header();
|
||||
|
||||
$countries = get_string_manager()->get_list_of_countries(true);
|
||||
|
||||
$namefields = get_all_user_name_fields(true);
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$namefields = $userfieldsapi->get_sql('', false, '', '', false)->selects;
|
||||
foreach ($users as $key => $id) {
|
||||
$user = $DB->get_record('user', array('id'=>$id), 'id, ' . $namefields . ', username, email, country, lastaccess, city');
|
||||
$user->fullname = fullname($user, true);
|
||||
|
||||
@@ -241,7 +241,8 @@ class web_service_token_form extends moodleform {
|
||||
if ($usertotal < 500) {
|
||||
list($sort, $params) = users_order_by_sql('u');
|
||||
// User searchable selector - return users who are confirmed, not deleted, not suspended and not a guest.
|
||||
$sql = 'SELECT u.id, ' . get_all_user_name_fields(true, 'u') . '
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$sql = 'SELECT u.id' . $userfieldsapi->get_sql('u')->selects . '
|
||||
FROM {user} u
|
||||
WHERE u.deleted = 0
|
||||
AND u.confirmed = 1
|
||||
|
||||
@@ -197,7 +197,7 @@ class condition extends \core_availability\condition {
|
||||
$this->customfield);
|
||||
}
|
||||
} else {
|
||||
$translatedfieldname = get_user_field_name($this->standardfield);
|
||||
$translatedfieldname = \core\user_fields::get_display_name($this->standardfield);
|
||||
}
|
||||
$context = \context_course::instance($course->id);
|
||||
$a = new \stdClass();
|
||||
|
||||
@@ -44,23 +44,23 @@ class frontend extends \core_availability\frontend {
|
||||
\section_info $section = null) {
|
||||
// Standard user fields.
|
||||
$standardfields = array(
|
||||
'firstname' => get_user_field_name('firstname'),
|
||||
'lastname' => get_user_field_name('lastname'),
|
||||
'email' => get_user_field_name('email'),
|
||||
'city' => get_user_field_name('city'),
|
||||
'country' => get_user_field_name('country'),
|
||||
'url' => get_user_field_name('url'),
|
||||
'icq' => get_user_field_name('icq'),
|
||||
'skype' => get_user_field_name('skype'),
|
||||
'aim' => get_user_field_name('aim'),
|
||||
'yahoo' => get_user_field_name('yahoo'),
|
||||
'msn' => get_user_field_name('msn'),
|
||||
'idnumber' => get_user_field_name('idnumber'),
|
||||
'institution' => get_user_field_name('institution'),
|
||||
'department' => get_user_field_name('department'),
|
||||
'phone1' => get_user_field_name('phone1'),
|
||||
'phone2' => get_user_field_name('phone2'),
|
||||
'address' => get_user_field_name('address')
|
||||
'firstname' => \core\user_fields::get_display_name('firstname'),
|
||||
'lastname' => \core\user_fields::get_display_name('lastname'),
|
||||
'email' => \core\user_fields::get_display_name('email'),
|
||||
'city' => \core\user_fields::get_display_name('city'),
|
||||
'country' => \core\user_fields::get_display_name('country'),
|
||||
'url' => \core\user_fields::get_display_name('url'),
|
||||
'icq' => \core\user_fields::get_display_name('icq'),
|
||||
'skype' => \core\user_fields::get_display_name('skype'),
|
||||
'aim' => \core\user_fields::get_display_name('aim'),
|
||||
'yahoo' => \core\user_fields::get_display_name('yahoo'),
|
||||
'msn' => \core\user_fields::get_display_name('msn'),
|
||||
'idnumber' => \core\user_fields::get_display_name('idnumber'),
|
||||
'institution' => \core\user_fields::get_display_name('institution'),
|
||||
'department' => \core\user_fields::get_display_name('department'),
|
||||
'phone1' => \core\user_fields::get_display_name('phone1'),
|
||||
'phone2' => \core\user_fields::get_display_name('phone2'),
|
||||
'address' => \core\user_fields::get_display_name('address')
|
||||
);
|
||||
\core_collator::asort($standardfields);
|
||||
|
||||
|
||||
@@ -1369,7 +1369,7 @@ class backup_users_structure_step extends backup_structure_step {
|
||||
'phone2', 'institution', 'department', 'address',
|
||||
'city', 'country', 'lastip', 'picture',
|
||||
'url', 'description', 'descriptionformat', 'imagealt', 'auth');
|
||||
$anonfields = array_merge($anonfields, get_all_user_name_fields());
|
||||
$anonfields = array_merge($anonfields, \core\user_fields::get_name_fields());
|
||||
|
||||
// Add anonymized fields to $userfields with custom final element
|
||||
foreach ($anonfields as $field) {
|
||||
|
||||
@@ -58,7 +58,8 @@ class external_badge implements renderable {
|
||||
global $DB;
|
||||
// At this point a user has connected a backpack. So, we are going to get
|
||||
// their backpack email rather than their account email.
|
||||
$namefields = get_all_user_name_fields(true, 'u');
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$namefields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$user = $DB->get_record_sql("SELECT {$namefields}, b.email
|
||||
FROM {user} u INNER JOIN {badge_backpack} b ON u.id = b.userid
|
||||
WHERE b.userid = :userid", array('userid' => $recipient), IGNORE_MISSING);
|
||||
|
||||
@@ -76,7 +76,8 @@ class issued_badge implements renderable {
|
||||
array('hash' => $hash), IGNORE_MISSING);
|
||||
if ($rec) {
|
||||
// Get a recipient from database.
|
||||
$namefields = get_all_user_name_fields(true, 'u');
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$namefields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$user = $DB->get_record_sql("SELECT u.id, $namefields, u.deleted, u.email
|
||||
FROM {user} u WHERE u.id = :userid", array('userid' => $rec->userid));
|
||||
$this->recipient = $user;
|
||||
|
||||
@@ -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' => get_user_field_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;
|
||||
}
|
||||
}
|
||||
@@ -138,7 +139,7 @@ class award_criteria_profile extends award_criteria {
|
||||
if (is_numeric($p['field'])) {
|
||||
$str = $DB->get_field('user_info_field', 'name', array('id' => $p['field']));
|
||||
} else {
|
||||
$str = get_user_field_name($p['field']);
|
||||
$str = \core\user_fields::get_display_name($p['field']);
|
||||
}
|
||||
if (!$str) {
|
||||
$output[] = $OUTPUT->error_text(get_string('error:nosuchfield', 'badges'));
|
||||
|
||||
@@ -87,7 +87,8 @@ if ($badge->has_manual_award_criteria() && has_capability('moodle/badges:awardba
|
||||
echo $OUTPUT->box($OUTPUT->single_button($url, get_string('award', 'badges')), 'clearfix mdl-align');
|
||||
}
|
||||
|
||||
$namefields = get_all_user_name_fields(true, 'u');
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$namefields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$sql = "SELECT b.userid, b.dateissued, b.uniquehash, $namefields
|
||||
FROM {badge_issued} b INNER JOIN {user} u
|
||||
ON b.userid = u.id
|
||||
|
||||
@@ -508,12 +508,12 @@ class block_activity_results extends block_base {
|
||||
|
||||
// Now grab all the users from the database.
|
||||
$userids = array_merge(array_keys($best), array_keys($worst));
|
||||
$fields = array_merge(array('id', 'idnumber'), get_all_user_name_fields());
|
||||
$fields = array_merge(array('id', 'idnumber'), \core\user_fields::get_name_fields());
|
||||
$fields = implode(',', $fields);
|
||||
$users = $DB->get_records_list('user', 'id', $userids, '', $fields);
|
||||
|
||||
// If configured to view user idnumber, ensure current user can see it.
|
||||
$extrafields = get_extra_user_fields($this->context);
|
||||
$extrafields = \core\user_fields::for_identity($this->context)->get_required_fields();
|
||||
$canviewidnumber = (array_search('idnumber', $extrafields) !== false);
|
||||
|
||||
// Ready for output!
|
||||
|
||||
@@ -50,7 +50,8 @@ class block_mentees extends block_base {
|
||||
$this->content = new stdClass();
|
||||
|
||||
// get all the mentees, i.e. users you have a direct assignment to
|
||||
$allusernames = get_all_user_name_fields(true, 'u');
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$allusernames = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
if ($usercontexts = $DB->get_records_sql("SELECT c.instanceid, c.instanceid, $allusernames
|
||||
FROM {role_assignments} ra, {context} c, {user} u
|
||||
WHERE ra.userid = ?
|
||||
|
||||
@@ -86,7 +86,8 @@ class fetcher {
|
||||
}
|
||||
$params = array();
|
||||
|
||||
$userfields = \user_picture::fields('u', array('username', 'deleted'));
|
||||
$userfieldsapi = \core\user_fields::for_userpic()->including('username', 'deleted');
|
||||
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
|
||||
// Add this to the SQL to show only group users.
|
||||
if ($currentgroup !== null) {
|
||||
|
||||
+2
-2
@@ -646,8 +646,8 @@ class blog_listing {
|
||||
if (!$userid) {
|
||||
$userid = $USER->id;
|
||||
}
|
||||
|
||||
$allnamefields = \user_picture::fields('u', null, 'useridalias');
|
||||
$userfieldsapi = \core\user_fields::for_userpic();
|
||||
$allnamefields = $userfieldsapi->get_sql('u', false, '', 'useridalias', false)->selects;
|
||||
// The query used to locate blog entries is complicated. It will be built from the following components:
|
||||
$requiredfields = "p.*, $allnamefields"; // The SELECT clause.
|
||||
$tables = array('p' => 'post', 'u' => 'user'); // Components of the FROM clause (table_id => table_name).
|
||||
|
||||
+3
-1
@@ -234,7 +234,9 @@ function blog_rss_get_feed($context, $args) {
|
||||
|
||||
switch ($type) {
|
||||
case 'user':
|
||||
$info = fullname($DB->get_record('user', array('id' => $id), get_all_user_name_fields(true)));
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$info = fullname($DB->get_record('user', array('id' => $id),
|
||||
$userfieldsapi->get_sql('', false, '', '', false)->selects));
|
||||
break;
|
||||
case 'course':
|
||||
$info = $DB->get_field('course', 'fullname', array('id' => $id));
|
||||
|
||||
+2
-1
@@ -551,7 +551,8 @@ class comment {
|
||||
$params = array();
|
||||
$perpage = (!empty($CFG->commentsperpage))?$CFG->commentsperpage:15;
|
||||
$start = $page * $perpage;
|
||||
$ufields = user_picture::fields('u');
|
||||
$userfieldsapi = \core\user_fields::for_userpic();
|
||||
$ufields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
|
||||
list($componentwhere, $component) = $this->get_component_select_sql('c');
|
||||
if ($component) {
|
||||
|
||||
@@ -61,7 +61,8 @@ class comment_manager {
|
||||
}
|
||||
$comments = array();
|
||||
|
||||
$usernamefields = get_all_user_name_fields(true, 'u');
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$usernamefields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$sql = "SELECT c.id, c.contextid, c.itemid, c.component, c.commentarea, c.userid, c.content, $usernamefields, c.timecreated
|
||||
FROM {comments} c
|
||||
JOIN {user} u
|
||||
@@ -75,7 +76,7 @@ class comment_manager {
|
||||
$item->time = userdate($item->timecreated);
|
||||
$item->content = format_text($item->content, FORMAT_MOODLE, $formatoptions);
|
||||
// Unset fields not related to the comment
|
||||
foreach (get_all_user_name_fields() as $namefield) {
|
||||
foreach (\core\user_fields::get_name_fields() as $namefield) {
|
||||
unset($item->$namefield);
|
||||
}
|
||||
unset($item->timecreated);
|
||||
|
||||
@@ -1033,7 +1033,8 @@ class core_course_category implements renderable, cacheable_object, IteratorAggr
|
||||
list($sql2, $params2) = $DB->get_in_or_equal($managerroles, SQL_PARAMS_NAMED, 'rid');
|
||||
list($sort, $sortparams) = users_order_by_sql('u');
|
||||
$notdeleted = array('notdeleted' => 0);
|
||||
$allnames = get_all_user_name_fields(true, 'u');
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$allnames = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$sql = "SELECT ra.contextid, ra.id AS raid,
|
||||
r.id AS roleid, r.name AS rolename, r.shortname AS roleshortname,
|
||||
rn.name AS rolecoursealias, u.id, u.username, $allnames
|
||||
|
||||
@@ -77,9 +77,12 @@ class recent_form extends moodleform {
|
||||
$options[0] = get_string('allparticipants');
|
||||
$options[$CFG->siteguest] = get_string('guestuser');
|
||||
|
||||
$userfieldsapi = \core\user_fields::for_userpic();
|
||||
$ufields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
|
||||
if (isset($groupoptions[0])) {
|
||||
// can see all enrolled users
|
||||
if ($enrolled = get_enrolled_users($context, null, 0, user_picture::fields('u'))) {
|
||||
if ($enrolled = get_enrolled_users($context, null, 0, $ufields)) {
|
||||
foreach ($enrolled as $euser) {
|
||||
$options[$euser->id] = fullname($euser, $viewfullnames);
|
||||
}
|
||||
@@ -87,7 +90,7 @@ class recent_form extends moodleform {
|
||||
} else {
|
||||
// can see users from some groups only
|
||||
foreach ($groupoptions as $groupid=>$unused) {
|
||||
if ($enrolled = get_enrolled_users($context, null, $groupid, user_picture::fields('u'))) {
|
||||
if ($enrolled = get_enrolled_users($context, null, $groupid, $ufields)) {
|
||||
foreach ($enrolled as $euser) {
|
||||
if (!array_key_exists($euser->id, $options)) {
|
||||
$options[$euser->id] = fullname($euser, $viewfullnames);
|
||||
|
||||
+2
-1
@@ -94,7 +94,8 @@ switch ($action) {
|
||||
$search = optional_param('search', '', PARAM_RAW);
|
||||
$page = optional_param('page', 0, PARAM_INT);
|
||||
$outcome->response = $manager->search_other_users($search, $searchanywhere, $page);
|
||||
$extrafields = get_extra_user_fields($context);
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$extrafields = \core\user_fields::get_identity_fields($context, false);
|
||||
$useroptions = array();
|
||||
// User is not enrolled, either link to site profile or do not link at all.
|
||||
if (has_capability('moodle/user:viewdetails', context_system::instance())) {
|
||||
|
||||
+23
-2
@@ -558,9 +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'],
|
||||
get_extra_user_fields($context)
|
||||
$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.
|
||||
@@ -568,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;
|
||||
}
|
||||
}
|
||||
@@ -652,7 +672,8 @@ class core_enrol_external extends external_api {
|
||||
// Add also extra user fields.
|
||||
$requiredfields = array_merge(
|
||||
['id', 'fullname', 'profileimageurl', 'profileimageurlsmall'],
|
||||
get_extra_user_fields($context)
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
\core\user_fields::get_identity_fields($context, false)
|
||||
);
|
||||
foreach ($users['users'] as $user) {
|
||||
if ($userdetails = user_get_user_details($user, $course, $requiredfields)) {
|
||||
|
||||
+57
-33
@@ -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,13 +240,15 @@ class course_enrolment_manager {
|
||||
list($instancessql, $params, $filter) = $this->get_instance_sql();
|
||||
list($filtersql, $moreparams) = $this->get_filter_sql();
|
||||
$params += $moreparams;
|
||||
$extrafields = get_extra_user_fields($this->get_context());
|
||||
$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))
|
||||
@@ -268,7 +272,8 @@ class course_enrolment_manager {
|
||||
global $DB;
|
||||
|
||||
// Search condition.
|
||||
$extrafields = get_extra_user_fields($this->get_context());
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$extrafields = user_fields::get_identity_fields($this->get_context(), false);
|
||||
list($sql, $params) = users_search_sql($this->searchfilter, 'u', true, $extrafields);
|
||||
|
||||
// Role condition.
|
||||
@@ -341,22 +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;
|
||||
$extrafields = get_extra_user_fields($this->get_context());
|
||||
$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];
|
||||
@@ -369,19 +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)) {
|
||||
$conditions = get_extra_user_fields($this->get_context());
|
||||
foreach (get_all_user_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) {
|
||||
@@ -399,13 +422,8 @@ class course_enrolment_manager {
|
||||
}
|
||||
$wherecondition = implode(' AND ', $tests);
|
||||
|
||||
$extrafields = get_extra_user_fields($this->get_context(), array('username', 'lastaccess'));
|
||||
$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];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -486,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";
|
||||
@@ -518,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";
|
||||
@@ -546,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
|
||||
@@ -1046,7 +1067,8 @@ class course_enrolment_manager {
|
||||
|
||||
$context = $this->get_context();
|
||||
$now = time();
|
||||
$extrafields = get_extra_user_fields($context);
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$extrafields = user_fields::get_identity_fields($context, false);
|
||||
|
||||
$users = array();
|
||||
foreach ($userroles as $userrole) {
|
||||
@@ -1124,7 +1146,8 @@ class course_enrolment_manager {
|
||||
$canmanagegroups = has_capability('moodle/course:managegroups', $context);
|
||||
|
||||
$url = new moodle_url($pageurl, $this->get_url_params());
|
||||
$extrafields = get_extra_user_fields($context);
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$extrafields = user_fields::get_identity_fields($context, false);
|
||||
|
||||
$enabledplugins = $this->get_enrolment_plugins(true);
|
||||
|
||||
@@ -1301,7 +1324,8 @@ class course_enrolment_manager {
|
||||
list($instancesql, $instanceparams) = $DB->get_in_or_equal(array_keys($instances), SQL_PARAMS_NAMED, 'instanceid0000');
|
||||
}
|
||||
|
||||
$userfields = user_picture::fields('u');
|
||||
$userfieldsapi = \core\user_fields::for_userpic();
|
||||
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
list($idsql, $idparams) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED, 'userid0000');
|
||||
|
||||
list($sort, $sortparams) = users_order_by_sql('u');
|
||||
|
||||
@@ -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
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -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(', ');
|
||||
|
||||
@@ -93,7 +93,7 @@ class enrol_manual_enrol_users_form extends moodleform {
|
||||
'courseid' => $course->id,
|
||||
'enrolid' => $instance->id,
|
||||
'perpage' => $CFG->maxusersperpage,
|
||||
'userfields' => implode(',', get_extra_user_fields($context))
|
||||
'userfields' => implode(',', \core\user_fields::get_identity_fields($context, true))
|
||||
);
|
||||
$mform->addElement('autocomplete', 'userlist', get_string('selectusers', 'enrol_manual'), array(), $options);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -54,9 +54,10 @@ $userdetails = array (
|
||||
'firstname' => get_string('firstname'),
|
||||
'lastname' => get_string('lastname'),
|
||||
);
|
||||
$extrafields = get_extra_user_fields($context);
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$extrafields = \core\user_fields::get_identity_fields($context, false);
|
||||
foreach ($extrafields as $field) {
|
||||
$userdetails[$field] = get_user_field_name($field);
|
||||
$userdetails[$field] = \core\user_fields::get_display_name($field);
|
||||
}
|
||||
|
||||
$fields = array(
|
||||
|
||||
+2
-1
@@ -1021,7 +1021,8 @@ class enrol_self_plugin extends enrol_plugin {
|
||||
// We only use the first user.
|
||||
$i = 0;
|
||||
do {
|
||||
$allnames = get_all_user_name_fields(true, 'u');
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$allnames = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$rusers = get_role_users($croles[$i], $context, true, 'u.id, u.confirmed, u.username, '. $allnames . ',
|
||||
u.email, r.sortorder, ra.id', 'r.sortorder, ra.id ASC, ' . $sort, null, '', '', '', '', $sortparams);
|
||||
$i++;
|
||||
|
||||
@@ -82,7 +82,9 @@ class enrol_self_enrol_form extends moodleform {
|
||||
$mform->addElement('password', 'enrolpassword', get_string('password', 'enrol_self'),
|
||||
array('id' => 'enrolpassword_'.$instance->id));
|
||||
$context = context_course::instance($this->instance->courseid);
|
||||
$keyholders = get_users_by_capability($context, 'enrol/self:holdkey', user_picture::fields('u'));
|
||||
$userfieldsapi = \core\user_fields::for_userpic();
|
||||
$ufields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$keyholders = get_users_by_capability($context, 'enrol/self:holdkey', $ufields);
|
||||
$keyholdercount = 0;
|
||||
foreach ($keyholders as $keyholder) {
|
||||
$keyholdercount++;
|
||||
|
||||
@@ -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' => '[email protected]',
|
||||
'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('[email protected]', $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('[email protected]', $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('[email protected]', $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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,8 @@ switch ($action) {
|
||||
}
|
||||
|
||||
if ($errorstr) {
|
||||
$user = $DB->get_record('user', array('id' => $userid), 'id, ' . get_all_user_name_fields(true));
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$user = $DB->get_record('user', array('id' => $userid), 'id' . $userfieldsapi->get_sql()->selects);
|
||||
$gradestr = new stdClass();
|
||||
$gradestr->username = fullname($user);
|
||||
$gradestr->itemname = $grade_item->get_name();
|
||||
|
||||
@@ -293,7 +293,8 @@ class grade_report_grader extends grade_report {
|
||||
}
|
||||
|
||||
if ($errorstr) {
|
||||
$userfields = 'id, ' . get_all_user_name_fields(true);
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$userfields = 'id, ' . $userfieldsapi->get_sql('', false, '', '', false)->selects;
|
||||
$user = $DB->get_record('user', array('id' => $userid), $userfields);
|
||||
$gradestr = new stdClass();
|
||||
$gradestr->username = fullname($user, $viewfullnames);
|
||||
@@ -437,7 +438,9 @@ class grade_report_grader extends grade_report {
|
||||
list($enrolledsql, $enrolledparams) = get_enrolled_sql($this->context, '', 0, $showonlyactiveenrol);
|
||||
|
||||
// Fields we need from the user table.
|
||||
$userfields = user_picture::fields('u', get_extra_user_fields($this->context));
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$userfieldsapi = \core\user_fields::for_identity($this->context, false)->with_userpic();
|
||||
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
|
||||
// We want to query both the current context and parent contexts.
|
||||
list($relatedctxsql, $relatedctxparams) = $DB->get_in_or_equal($this->context->get_parent_context_ids(true), SQL_PARAMS_NAMED, 'relatedctx');
|
||||
@@ -657,7 +660,8 @@ class grade_report_grader extends grade_report {
|
||||
$strfeedback = $this->get_lang_string("feedback");
|
||||
$strgrade = $this->get_lang_string('grade');
|
||||
|
||||
$extrafields = get_extra_user_fields($this->context);
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$extrafields = \core\user_fields::get_identity_fields($this->context, false);
|
||||
|
||||
$arrows = $this->get_sort_arrows($extrafields);
|
||||
|
||||
@@ -1942,7 +1946,7 @@ class grade_report_grader extends grade_report {
|
||||
}
|
||||
|
||||
$arrows['studentname'] = '';
|
||||
$requirednames = order_in_string(get_all_user_name_fields(), $nameformat);
|
||||
$requirednames = order_in_string(\core\user_fields::get_name_fields(), $nameformat);
|
||||
if (!empty($requirednames)) {
|
||||
foreach ($requirednames as $name) {
|
||||
$arrows['studentname'] .= html_writer::link(
|
||||
@@ -1959,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)), get_user_field_name($field));
|
||||
array('sortitemid' => $field)), \core\user_fields::get_display_name($field));
|
||||
$arrows[$field] = $fieldlink;
|
||||
|
||||
if ($field == $this->sortitemid) {
|
||||
|
||||
@@ -131,7 +131,8 @@ class helper {
|
||||
global $DB, $USER;
|
||||
|
||||
// Fields we need from the user table.
|
||||
$extrafields = get_extra_user_fields($context);
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$extrafields = \core\user_fields::get_identity_fields($context, false);
|
||||
$params = array();
|
||||
if (!empty($search)) {
|
||||
list($filtersql, $params) = users_search_sql($search, 'u', true, $extrafields);
|
||||
@@ -140,7 +141,8 @@ class helper {
|
||||
$filtersql = '';
|
||||
}
|
||||
|
||||
$ufields = \user_picture::fields('u', $extrafields).',u.username';
|
||||
$userfieldsapi = \core\user_fields::for_userpic()->including(...(array_merge($extrafields, ['username'])));
|
||||
$ufields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
if ($count) {
|
||||
$select = "SELECT COUNT(DISTINCT u.id) ";
|
||||
$orderby = "";
|
||||
@@ -201,7 +203,8 @@ class helper {
|
||||
$groupwheresql = " AND gm.groupid $insql ";
|
||||
}
|
||||
|
||||
$ufields = get_all_user_name_fields(true, 'u');
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$ufields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$sql = "SELECT u.id, $ufields
|
||||
FROM {user} u
|
||||
JOIN {grade_grades_history} ggh ON ggh.usermodified = u.id
|
||||
|
||||
@@ -140,7 +140,8 @@ class tablelog extends \table_sql implements \renderable {
|
||||
* Setup the headers for the html table.
|
||||
*/
|
||||
protected function define_table_columns() {
|
||||
$extrafields = get_extra_user_fields($this->context);
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$extrafields = \core\user_fields::get_identity_fields($this->context, false);
|
||||
|
||||
// Define headers and columns.
|
||||
$cols = array(
|
||||
@@ -394,17 +395,19 @@ class tablelog extends \table_sql implements \renderable {
|
||||
gi.itemtype, gi.itemmodule, gi.iteminstance, gi.itemnumber, ';
|
||||
|
||||
// Add extra user fields that we need for the graded user.
|
||||
$extrafields = get_extra_user_fields($this->context);
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$extrafields = \core\user_fields::get_identity_fields($this->context, false);
|
||||
foreach ($extrafields as $field) {
|
||||
$fields .= 'u.' . $field . ', ';
|
||||
}
|
||||
$gradeduserfields = get_all_user_name_fields(true, 'u');
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$gradeduserfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$fields .= $gradeduserfields . ', ';
|
||||
$groupby = $fields;
|
||||
|
||||
// Add extra user fields that we need for the grader user.
|
||||
$fields .= get_all_user_name_fields(true, 'ug', '', 'grader');
|
||||
$groupby .= get_all_user_name_fields(true, 'ug');
|
||||
$fields .= $userfieldsapi->get_sql('ug', false, 'grader', '', false)->selects;
|
||||
$groupby .= $userfieldsapi->get_sql('ug', false, '', '', false)->selects;
|
||||
|
||||
// Filtering on revised grades only.
|
||||
$revisedonly = !empty($this->filters->revisedonly);
|
||||
|
||||
@@ -51,7 +51,8 @@ $users = \gradereport_history\helper::get_users($context, $search, $page, 25);
|
||||
$outcome->response = array('users' => array());
|
||||
$outcome->response['totalusers'] = \gradereport_history\helper::get_users_count($context, $search);;
|
||||
|
||||
$extrafields = get_extra_user_fields($context);
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$extrafields = \core\user_fields::get_identity_fields($context, false);
|
||||
$useroptions = array('link' => false, 'visibletoscreenreaders' => false);
|
||||
|
||||
// Format the user record.
|
||||
|
||||
+2
-1
@@ -99,7 +99,8 @@ if ($editform->is_cancelled()) {
|
||||
// Display only active users if the option was selected or they do not have the capability to view suspended users.
|
||||
$onlyactive = !empty($data->includeonlyactiveenrol) || !has_capability('moodle/course:viewsuspendedusers', $context);
|
||||
|
||||
$extrafields = get_extra_user_fields($context);
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$extrafields = \core\user_fields::get_identity_fields($context, false);
|
||||
$users = groups_get_potential_members($data->courseid, $data->roleid, $source, $orderby, !empty($data->notingroup),
|
||||
$onlyactive, $extrafields);
|
||||
$usercnt = count($users);
|
||||
|
||||
+10
-4
@@ -81,9 +81,12 @@ switch ($action) {
|
||||
case 'ajax_getmembersingroup':
|
||||
$roles = array();
|
||||
|
||||
$extrafields = get_extra_user_fields($context);
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$userfieldsapi = \core\user_fields::for_identity($context, false)->with_userpic();
|
||||
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$extrafields = $userfieldsapi->get_required_fields([\core\user_fields::PURPOSE_IDENTITY]);
|
||||
if ($groupmemberroles = groups_get_members_by_role($groupids[0], $courseid,
|
||||
'u.id, ' . user_picture::fields('u', $extrafields))) {
|
||||
'u.id, ' . $userfields)) {
|
||||
|
||||
$viewfullnames = has_capability('moodle/site:viewfullnames', $context);
|
||||
|
||||
@@ -202,9 +205,12 @@ if ($groups) {
|
||||
// Get list of group members to render if there is a single selected group.
|
||||
$members = array();
|
||||
if ($singlegroup) {
|
||||
$extrafields = get_extra_user_fields($context);
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$userfieldsapi = \core\user_fields::for_identity($context, false)->with_userpic();
|
||||
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$extrafields = $userfieldsapi->get_required_fields([\core\user_fields::PURPOSE_IDENTITY]);
|
||||
if ($groupmemberroles = groups_get_members_by_role(reset($groupids), $courseid,
|
||||
'u.id, ' . user_picture::fields('u', $extrafields))) {
|
||||
'u.id, ' . $userfields)) {
|
||||
|
||||
$viewfullnames = has_capability('moodle/site:viewfullnames', $context);
|
||||
|
||||
|
||||
+2
-1
@@ -848,7 +848,8 @@ function groups_get_potential_members($courseid, $roleid = null, $source = null,
|
||||
}
|
||||
}
|
||||
|
||||
$allusernamefields = user_picture::fields('u', $extrafields);
|
||||
$userfieldsapi = \core\user_fields::for_userpic()->including(...$extrafields);
|
||||
$allusernamefields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$sql = "SELECT DISTINCT u.id, u.username, $allusernamefields, u.idnumber
|
||||
FROM {user} u
|
||||
JOIN ($esql) e ON e.id = u.id
|
||||
|
||||
+5
-2
@@ -110,8 +110,11 @@ if ($groupingid) {
|
||||
|
||||
list($sort, $sortparams) = users_order_by_sql('u');
|
||||
|
||||
$extrafields = get_extra_user_fields($context);
|
||||
$allnames = 'u.id, ' . user_picture::fields('u', $extrafields);
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$userfieldsapi = \core\user_fields::for_identity($context, false)->with_userpic();
|
||||
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$extrafields = $userfieldsapi->get_required_fields([\core\user_fields::PURPOSE_IDENTITY]);
|
||||
$allnames = 'u.id, ' . $userfields;
|
||||
|
||||
$sql = "SELECT g.id AS groupid, gg.groupingid, u.id AS userid, $allnames, u.idnumber, u.username
|
||||
FROM {groups} g
|
||||
|
||||
+3
-1
@@ -1205,7 +1205,9 @@ $string['setupsearchengine'] = 'Setup search engine';
|
||||
$string['showcommentscount'] = 'Show comments count';
|
||||
$string['showdetails'] = 'Show details';
|
||||
$string['showuseridentity'] = 'Show user identity';
|
||||
$string['showuseridentity_desc'] = 'When selecting or searching for users, and when displaying lists of users, these fields may be shown in addition to their full name. The fields are only shown to users who have the moodle/site:viewuseridentity capability; by default, teachers and managers. (This option makes most sense if you choose one or two fields that are mandatory at your institution.)';
|
||||
$string['showuseridentity_desc'] = 'When selecting or searching for users, and when displaying lists of users, these fields may be shown in addition to their full name. The fields are only shown to users who have the moodle/site:viewuseridentity capability; by default, teachers and managers. (This option makes most sense if you choose one or two fields that are mandatory at your institution.)
|
||||
|
||||
Fields marked * are custom user profile fields. You can select these fields, but there are currently some screens on which they will not appear.';
|
||||
$string['simplexmlrequired'] = 'The SimpleXML PHP extension is now required by Moodle.';
|
||||
$string['sitemenubar'] = 'Site navigation';
|
||||
$string['sitemailcharset'] = 'Character set';
|
||||
|
||||
+2
-1
@@ -3923,7 +3923,8 @@ function get_role_users($roleid, context $context, $parent = false, $fields = ''
|
||||
global $DB;
|
||||
|
||||
if (empty($fields)) {
|
||||
$allnames = get_all_user_name_fields(true, 'u');
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$allnames = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$fields = 'u.id, u.confirmed, u.username, '. $allnames . ', ' .
|
||||
'u.maildisplay, u.mailformat, u.maildigest, u.email, u.emailstop, u.city, '.
|
||||
'u.country, u.picture, u.idnumber, u.department, u.institution, '.
|
||||
|
||||
+23
-10
@@ -3066,34 +3066,46 @@ class admin_setting_configcheckbox extends admin_setting {
|
||||
class admin_setting_configmulticheckbox extends admin_setting {
|
||||
/** @var array Array of choices value=>label */
|
||||
public $choices;
|
||||
/** @var callable|null Loader function for choices */
|
||||
protected $choiceloader = null;
|
||||
|
||||
/**
|
||||
* Constructor: uses parent::__construct
|
||||
*
|
||||
* The $choices parameter may be either an array of $value => $label format,
|
||||
* e.g. [1 => get_string('yes')], or a callback function which takes no parameters and
|
||||
* returns an array in that format.
|
||||
*
|
||||
* @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
|
||||
* @param string $visiblename localised
|
||||
* @param string $description long localised info
|
||||
* @param array $defaultsetting array of selected
|
||||
* @param array $choices array of $value=>$label for each checkbox
|
||||
* @param array|callable $choices array of $value => $label for each checkbox, or a callback
|
||||
*/
|
||||
public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
|
||||
$this->choices = $choices;
|
||||
if (is_array($choices)) {
|
||||
$this->choices = $choices;
|
||||
}
|
||||
if (is_callable($choices)) {
|
||||
$this->choiceloader = $choices;
|
||||
}
|
||||
parent::__construct($name, $visiblename, $description, $defaultsetting);
|
||||
}
|
||||
|
||||
/**
|
||||
* This public function may be used in ancestors for lazy loading of choices
|
||||
* This function may be used in ancestors for lazy loading of choices
|
||||
*
|
||||
* Override this method if loading of choices is expensive, such
|
||||
* as when it requires multiple db requests.
|
||||
*
|
||||
* @todo Check if this function is still required content commented out only returns true
|
||||
* @return bool true if loaded, false if error
|
||||
*/
|
||||
public function load_choices() {
|
||||
/*
|
||||
if (is_array($this->choices)) {
|
||||
return true;
|
||||
if ($this->choiceloader) {
|
||||
if (!is_array($this->choices)) {
|
||||
$this->choices = call_user_func($this->choiceloader);
|
||||
}
|
||||
}
|
||||
.... load choices here
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -4299,7 +4311,8 @@ class admin_setting_users_with_capability extends admin_setting_configmultiselec
|
||||
'This is unexpected, and a problem because there is no way to pass these ' .
|
||||
'parameters to get_users_by_capability. See MDL-34657.');
|
||||
}
|
||||
$userfields = 'u.id, u.username, ' . get_all_user_name_fields(true, 'u');
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$userfields = 'u.id, u.username, ' . $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$users = get_users_by_capability(context_system::instance(), $this->capability, $userfields, $sort);
|
||||
$this->choices = array(
|
||||
'$@NONE@$' => get_string('nobody'),
|
||||
|
||||
+1
-1
@@ -1102,7 +1102,7 @@ function signup_setup_new_user($user) {
|
||||
$user->secret = random_string(15);
|
||||
$user->auth = $CFG->registerauth;
|
||||
// Initialize alternate name fields to empty strings.
|
||||
$namefields = array_diff(get_all_user_name_fields(), useredit_get_required_name_fields());
|
||||
$namefields = array_diff(\core\user_fields::get_name_fields(), useredit_get_required_name_fields());
|
||||
foreach ($namefields as $namefield) {
|
||||
$user->$namefield = '';
|
||||
}
|
||||
|
||||
+1
-1
@@ -148,7 +148,7 @@ function badges_notify_badge_award(badge $badge, $userid, $issued, $filepathhash
|
||||
$userfrom = new stdClass();
|
||||
$userfrom->id = $admin->id;
|
||||
$userfrom->email = !empty($CFG->badges_defaultissuercontact) ? $CFG->badges_defaultissuercontact : $admin->email;
|
||||
foreach (get_all_user_name_fields() as $addname) {
|
||||
foreach (\core\user_fields::get_name_fields() as $addname) {
|
||||
$userfrom->$addname = !empty($CFG->badges_defaultissuername) ? '' : $admin->$addname;
|
||||
}
|
||||
$userfrom->firstname = !empty($CFG->badges_defaultissuername) ? $CFG->badges_defaultissuername : $admin->firstname;
|
||||
|
||||
@@ -87,6 +87,18 @@ class behat_core_generator extends behat_generator_base {
|
||||
'required' => ['name', 'category', 'type', 'shortname'],
|
||||
'switchids' => [],
|
||||
],
|
||||
'custom profile field categories' => [
|
||||
'singular' => 'custom profile field category',
|
||||
'datagenerator' => 'custom_profile_field_category',
|
||||
'required' => ['name'],
|
||||
'switchids' => [],
|
||||
],
|
||||
'custom profile fields' => [
|
||||
'singular' => 'custom profile field',
|
||||
'datagenerator' => 'custom_profile_field',
|
||||
'required' => ['datatype', 'shortname', 'name'],
|
||||
'switchids' => [],
|
||||
],
|
||||
'permission overrides' => [
|
||||
'singular' => 'permission override',
|
||||
'datagenerator' => 'permission_override',
|
||||
|
||||
@@ -66,7 +66,8 @@ class riskadmin extends check {
|
||||
*/
|
||||
public function get_result(): result {
|
||||
global $DB, $CFG;
|
||||
$userfields = \user_picture::fields('u');
|
||||
$userfieldsapi = \core\user_fields::for_userpic();
|
||||
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$sql = "SELECT $userfields
|
||||
FROM {user} u
|
||||
WHERE u.id IN ($CFG->siteadmins)";
|
||||
|
||||
@@ -165,7 +165,8 @@ class riskbackup_result extends \core\check\result {
|
||||
'context1' => CONTEXT_COURSE,
|
||||
'context2' => CONTEXT_COURSE,
|
||||
];
|
||||
$userfields = \user_picture::fields('u');
|
||||
$userfieldsapi = \core\user_fields::for_userpic();
|
||||
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$rs = $DB->get_recordset_sql("
|
||||
SELECT DISTINCT $userfields,
|
||||
ra.contextid,
|
||||
|
||||
@@ -90,7 +90,8 @@ class riskxss_result extends \core\check\result {
|
||||
|
||||
global $CFG, $DB;
|
||||
|
||||
$userfields = \user_picture::fields('u');
|
||||
$userfieldsapi = \core\user_fields::for_userpic();
|
||||
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$users = $DB->get_records_sql("SELECT DISTINCT $userfields $this->sqlfrom", $this->params);
|
||||
foreach ($users as $uid => $user) {
|
||||
$url = "$CFG->wwwroot/user/view.php?id=$user->id";
|
||||
|
||||
@@ -115,7 +115,8 @@ class send_failed_login_notifications_task extends scheduled_task {
|
||||
|
||||
// Now, select all the login error logged records belonging to the ips and infos
|
||||
// since lastnotifyfailure, that we have stored in the cache_flags table.
|
||||
$namefields = get_all_user_name_fields(true, 'u');
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$namefields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$sql = "SELECT * FROM (
|
||||
SELECT l.*, u.username, $namefields
|
||||
FROM {" . $logtable . "} l
|
||||
|
||||
@@ -47,7 +47,8 @@ class send_new_user_passwords_task extends scheduled_task {
|
||||
// Generate new password emails for users - ppl expect these generated asap.
|
||||
if ($DB->count_records('user_preferences', array('name' => 'create_password', 'value' => '1'))) {
|
||||
mtrace('Creating passwords for new users...');
|
||||
$usernamefields = get_all_user_name_fields(true, 'u');
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$usernamefields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$newusers = $DB->get_recordset_sql("SELECT u.id as id, u.email, u.auth, u.deleted,
|
||||
u.suspended, u.emailstop, u.mnethostid, u.mailformat,
|
||||
$usernamefields, u.username, u.lang,
|
||||
|
||||
+5
-19
@@ -251,25 +251,11 @@ class core_user {
|
||||
$extrasql = '';
|
||||
$extraparams = [];
|
||||
|
||||
if (empty($CFG->showuseridentity)) {
|
||||
// Explode gives wrong result with empty string.
|
||||
$extra = [];
|
||||
} else {
|
||||
$extra = explode(',', $CFG->showuseridentity);
|
||||
}
|
||||
|
||||
// We need the username just to skip guests.
|
||||
$extrafieldlist = $extra;
|
||||
if (!in_array('username', $extra)) {
|
||||
$extrafieldlist[] = 'username';
|
||||
}
|
||||
// The deleted flag will always be false because users_search_sql excludes deleted users,
|
||||
// but it must be present or it causes PHP warnings in some functions below.
|
||||
if (!in_array('deleted', $extra)) {
|
||||
$extrafieldlist[] = 'deleted';
|
||||
}
|
||||
$selectfields = \user_picture::fields('u',
|
||||
array_merge(get_all_user_name_fields(), $extrafieldlist));
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$userfieldsapi = \core\user_fields::for_identity(null, false)->with_userpic()->with_name()
|
||||
->including('username', 'deleted');
|
||||
$selectfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$extra = $userfieldsapi->get_required_fields([\core\user_fields::PURPOSE_IDENTITY]);
|
||||
|
||||
$index = 1;
|
||||
foreach ($extra as $fieldname) {
|
||||
|
||||
@@ -0,0 +1,644 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core;
|
||||
|
||||
/**
|
||||
* Class for retrieving information about user fields that are needed for displaying user identity.
|
||||
*
|
||||
* @package core
|
||||
*/
|
||||
class user_fields {
|
||||
/** @var string Prefix used to identify custom profile fields */
|
||||
const PROFILE_FIELD_PREFIX = 'profile_field_';
|
||||
/** @var string Regular expression used to match a field name against the prefix */
|
||||
const PROFILE_FIELD_REGEX = '~^' . self::PROFILE_FIELD_PREFIX . '(.*)$~';
|
||||
|
||||
/** @var int All fields required to display user's identity, based on server configuration */
|
||||
const PURPOSE_IDENTITY = 0;
|
||||
/** @var int All fields required to display a user picture */
|
||||
const PURPOSE_USERPIC = 1;
|
||||
/** @var int All fields required for somebody's name */
|
||||
const PURPOSE_NAME = 2;
|
||||
/** @var int Field required by custom include list */
|
||||
const CUSTOM_INCLUDE = 3;
|
||||
|
||||
/** @var \context|null Context in use */
|
||||
protected $context;
|
||||
|
||||
/** @var bool True to allow custom user fields */
|
||||
protected $allowcustom;
|
||||
|
||||
/** @var bool[] Array of purposes (from PURPOSE_xx to true/false) */
|
||||
protected $purposes;
|
||||
|
||||
/** @var string[] List of extra fields to include */
|
||||
protected $include;
|
||||
|
||||
/** @var string[] List of fields to exclude */
|
||||
protected $exclude;
|
||||
|
||||
/** @var int Unique identifier for different queries generated in same request */
|
||||
protected static $uniqueidentifier = 1;
|
||||
|
||||
/** @var array|null Associative array from field => array of purposes it was used for => true */
|
||||
protected $fields = null;
|
||||
|
||||
/**
|
||||
* Protected constructor - use one of the for_xx methods to create an object.
|
||||
*
|
||||
* @param int $purpose Initial purpose for object or -1 for none
|
||||
*/
|
||||
protected function __construct(int $purpose = -1) {
|
||||
$this->purposes = [
|
||||
self::PURPOSE_IDENTITY => false,
|
||||
self::PURPOSE_USERPIC => false,
|
||||
self::PURPOSE_NAME => false,
|
||||
];
|
||||
if ($purpose != -1) {
|
||||
$this->purposes[$purpose] = true;
|
||||
}
|
||||
$this->include = [];
|
||||
$this->exclude = [];
|
||||
$this->context = null;
|
||||
$this->allowcustom = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an empty user fields object to get arbitrary user fields.
|
||||
*
|
||||
* You can add fields to retrieve with the including() function.
|
||||
*
|
||||
* @return user_fields User fields object ready for use
|
||||
*/
|
||||
public static function empty(): user_fields {
|
||||
return new user_fields();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a user fields object to get identity information for display.
|
||||
*
|
||||
* The function does all the required capability checks to see if the current user is allowed
|
||||
* to see them in the specified context. You can pass context null to get all the fields without
|
||||
* checking permissions.
|
||||
*
|
||||
* If the code can only handle fields in the main user table, and not custom profile fields,
|
||||
* then set $allowcustom to false.
|
||||
*
|
||||
* Note: After constructing the object you can use the ->with_xx, ->including, and ->excluding
|
||||
* functions to control the required fields in more detail. For example:
|
||||
*
|
||||
* $fields = user_fields::for_identity($context)->with_userpic()->excluding('email');
|
||||
*
|
||||
* @param \context|null $context Context; if supplied, includes only fields the current user should see
|
||||
* @param bool $allowcustom If true, custom profile fields may be included
|
||||
* @return user_fields User fields object ready for use
|
||||
*/
|
||||
public static function for_identity(?\context $context, bool $allowcustom = true): user_fields {
|
||||
$fields = new user_fields(self::PURPOSE_IDENTITY);
|
||||
$fields->context = $context;
|
||||
$fields->allowcustom = $allowcustom;
|
||||
return $fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a user fields object to get information required for displaying a user picture.
|
||||
*
|
||||
* Note: After constructing the object you can use the ->with_xx, ->including, and ->excluding
|
||||
* functions to control the required fields in more detail. For example:
|
||||
*
|
||||
* $fields = user_fields::for_userpic()->with_name()->excluding('email');
|
||||
*
|
||||
* @return user_fields User fields object ready for use
|
||||
*/
|
||||
public static function for_userpic(): user_fields {
|
||||
return new user_fields(self::PURPOSE_USERPIC);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a user fields object to get information required for displaying a user full name.
|
||||
*
|
||||
* Note: After constructing the object you can use the ->with_xx, ->including, and ->excluding
|
||||
* functions to control the required fields in more detail. For example:
|
||||
*
|
||||
* $fields = user_fields::for_name()->with_userpic()->excluding('email');
|
||||
*
|
||||
* @return user_fields User fields object ready for use
|
||||
*/
|
||||
public static function for_name(): user_fields {
|
||||
return new user_fields(self::PURPOSE_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* On an existing user_fields object, adds the fields required for displaying user pictures.
|
||||
*
|
||||
* @return $this Same object for chaining function calls
|
||||
*/
|
||||
public function with_userpic(): user_fields {
|
||||
$this->purposes[self::PURPOSE_USERPIC] = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* On an existing user_fields object, adds the fields required for displaying user full names.
|
||||
*
|
||||
* @return $this Same object for chaining function calls
|
||||
*/
|
||||
public function with_name(): user_fields {
|
||||
$this->purposes[self::PURPOSE_NAME] = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* On an existing user_fields object, adds the fields required for displaying user identity.
|
||||
*
|
||||
* The function does all the required capability checks to see if the current user is allowed
|
||||
* to see them in the specified context. You can pass context null to get all the fields without
|
||||
* checking permissions.
|
||||
*
|
||||
* If the code can only handle fields in the main user table, and not custom profile fields,
|
||||
* then set $allowcustom to false.
|
||||
*
|
||||
* @param \context|null Context; if supplied, includes only fields the current user should see
|
||||
* @param bool $allowcustom If true, custom profile fields may be included
|
||||
* @return $this Same object for chaining function calls
|
||||
*/
|
||||
public function with_identity(?\context $context, bool $allowcustom = true): user_fields {
|
||||
$this->context = $context;
|
||||
$this->allowcustom = $allowcustom;
|
||||
$this->purposes[self::PURPOSE_IDENTITY] = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* On an existing user_fields object, adds extra fields to be retrieved. You can specify either
|
||||
* fields from the user table e.g. 'email', or profile fields e.g. 'profile_field_height'.
|
||||
*
|
||||
* @param string ...$include One or more fields to add
|
||||
* @return $this Same object for chaining function calls
|
||||
*/
|
||||
public function including(string ...$include): user_fields {
|
||||
$this->include = array_merge($this->include, $include);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* On an existing user_fields object, excludes fields from retrieval. You can specify either
|
||||
* fields from the user table e.g. 'email', or profile fields e.g. 'profile_field_height'.
|
||||
*
|
||||
* This is useful when constructing queries where your query already explicitly references
|
||||
* certain fields, so you don't want to retrieve them twice.
|
||||
*
|
||||
* @param string ...$exclude One or more fields to exclude
|
||||
* @return $this Same object for chaining function calls
|
||||
*/
|
||||
public function excluding(...$exclude): user_fields {
|
||||
$this->exclude = array_merge($this->exclude, $exclude);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an array of all fields that are required for the specified purposes, also taking
|
||||
* into account the $includes and $excludes settings.
|
||||
*
|
||||
* The results may include basic field names (columns from the 'user' database table) and,
|
||||
* unless turned off, custom profile field names in the format 'profile_field_myfield'.
|
||||
*
|
||||
* You should not rely on the order of fields, with one exception: if there is an id field
|
||||
* it will be returned first. This is in case it is used with get_records calls.
|
||||
*
|
||||
* The $limitpurposes parameter is useful if you want to get a different set of fields than the
|
||||
* purposes in the constructor. For example, if you want to get SQL for identity + user picture
|
||||
* fields, but you then want to only get the identity fields as a list. (You can only specify
|
||||
* purposes that were also passed to the constructor i.e. it can only be used to restrict the
|
||||
* list, not add to it.)
|
||||
*
|
||||
* @param array $limitpurposes If specified, gets fields only for these purposes
|
||||
* @return string[] Array of required fields
|
||||
* @throws \coding_exception If any unknown purpose is listed
|
||||
*/
|
||||
public function get_required_fields(array $limitpurposes = []): array {
|
||||
// The first time this is called, actually work out the list. There is no way to 'un-cache'
|
||||
// it, but these objects are designed to be short-lived so it doesn't need one.
|
||||
if ($this->fields === null) {
|
||||
// Add all the fields as array keys so that there are no duplicates.
|
||||
$this->fields = [];
|
||||
if ($this->purposes[self::PURPOSE_IDENTITY]) {
|
||||
foreach (self::get_identity_fields($this->context, $this->allowcustom) as $field) {
|
||||
$this->fields[$field] = [self::PURPOSE_IDENTITY => true];
|
||||
}
|
||||
}
|
||||
if ($this->purposes[self::PURPOSE_USERPIC]) {
|
||||
foreach (self::get_picture_fields() as $field) {
|
||||
if (!array_key_exists($field, $this->fields)) {
|
||||
$this->fields[$field] = [];
|
||||
}
|
||||
$this->fields[$field][self::PURPOSE_USERPIC] = true;
|
||||
}
|
||||
}
|
||||
if ($this->purposes[self::PURPOSE_NAME]) {
|
||||
foreach (self::get_name_fields() as $field) {
|
||||
if (!array_key_exists($field, $this->fields)) {
|
||||
$this->fields[$field] = [];
|
||||
}
|
||||
$this->fields[$field][self::PURPOSE_NAME] = true;
|
||||
}
|
||||
}
|
||||
foreach ($this->include as $field) {
|
||||
if ($this->allowcustom || !preg_match(self::PROFILE_FIELD_REGEX, $field)) {
|
||||
if (!array_key_exists($field, $this->fields)) {
|
||||
$this->fields[$field] = [];
|
||||
}
|
||||
$this->fields[$field][self::CUSTOM_INCLUDE] = true;
|
||||
}
|
||||
}
|
||||
foreach ($this->exclude as $field) {
|
||||
unset($this->fields[$field]);
|
||||
}
|
||||
|
||||
// If the id field is included, make sure it's first in the list.
|
||||
if (array_key_exists('id', $this->fields)) {
|
||||
$newfields = ['id' => $this->fields['id']];
|
||||
foreach ($this->fields as $field => $purposes) {
|
||||
if ($field !== 'id') {
|
||||
$newfields[$field] = $purposes;
|
||||
}
|
||||
}
|
||||
$this->fields = $newfields;
|
||||
}
|
||||
}
|
||||
|
||||
if ($limitpurposes) {
|
||||
// Check the value was legitimate.
|
||||
foreach ($limitpurposes as $purpose) {
|
||||
if ($purpose != self::CUSTOM_INCLUDE && empty($this->purposes[$purpose])) {
|
||||
throw new \coding_exception('$limitpurposes can only include purposes defined in object');
|
||||
}
|
||||
}
|
||||
|
||||
// Filter the fields to include only those matching the purposes.
|
||||
$result = [];
|
||||
foreach ($this->fields as $key => $purposes) {
|
||||
foreach ($limitpurposes as $purpose) {
|
||||
if (array_key_exists($purpose, $purposes)) {
|
||||
$result[] = $key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
} else {
|
||||
return array_keys($this->fields);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets fields required for user pictures.
|
||||
*
|
||||
* The results include only basic field names (columns from the 'user' database table).
|
||||
*
|
||||
* @return string[] All fields required for user pictures
|
||||
*/
|
||||
public static function get_picture_fields(): array {
|
||||
return ['id', 'picture', 'firstname', 'lastname', 'firstnamephonetic', 'lastnamephonetic',
|
||||
'middlename', 'alternatename', 'imagealt', 'email'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets fields required for user names.
|
||||
*
|
||||
* The results include only basic field names (columns from the 'user' database table).
|
||||
*
|
||||
* Fields are usually returned in a specific order, which the fullname() function depends on.
|
||||
* If you specify 'true' to the $strangeorder flag, then the firstname and lastname fields
|
||||
* are moved to the front; this is useful in a few places in existing code. New code should
|
||||
* avoid requiring a particular order.
|
||||
*
|
||||
* @param bool $differentorder In a few places, a different order of fields is required
|
||||
* @return string[] All fields used to display user names
|
||||
*/
|
||||
public static function get_name_fields(bool $differentorder = false): array {
|
||||
$fields = ['firstnamephonetic', 'lastnamephonetic', 'middlename', 'alternatename',
|
||||
'firstname', 'lastname'];
|
||||
if ($differentorder) {
|
||||
return array_merge(array_slice($fields, -2), array_slice($fields, 0, -2));
|
||||
} else {
|
||||
return $fields;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all fields required for user identity. These fields should be included in tables
|
||||
* showing lists of users (in addition to the user's name which is included as standard).
|
||||
*
|
||||
* The results include basic field names (columns from the 'user' database table) and, unless
|
||||
* turned off, custom profile field names in the format 'profile_field_myfield'.
|
||||
*
|
||||
* This function does all the required capability checks to see if the current user is allowed
|
||||
* to see them in the specified context. You can pass context null to get all the fields
|
||||
* without checking permissions.
|
||||
*
|
||||
* @param \context|null $context Context; if not supplied, all fields will be included without checks
|
||||
* @param bool $allowcustom If true, custom profile fields will be included
|
||||
* @return string[] Array of required fields
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
public static function get_identity_fields(?\context $context, bool $allowcustom = true): array {
|
||||
global $CFG;
|
||||
|
||||
// Only users with permission get the extra fields.
|
||||
if ($context && !has_capability('moodle/site:viewuseridentity', $context)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Split showuseridentity on comma (filter needed in case the showuseridentity is empty).
|
||||
$extra = array_filter(explode(',', $CFG->showuseridentity));
|
||||
|
||||
// If there are any custom fields, remove them if necessary (either if allowcustom is false,
|
||||
// or if the user doesn't have access to see them).
|
||||
foreach ($extra as $key => $field) {
|
||||
if (preg_match(self::PROFILE_FIELD_REGEX, $field, $matches)) {
|
||||
if ($allowcustom) {
|
||||
require_once($CFG->dirroot . '/user/profile/lib.php');
|
||||
$fieldinfo = profile_get_custom_field_data_by_shortname($matches[1]);
|
||||
switch ($fieldinfo['visible']) {
|
||||
case PROFILE_VISIBLE_NONE:
|
||||
case PROFILE_VISIBLE_PRIVATE:
|
||||
$allowed = !$context || has_capability('moodle/user:viewalldetails', $context);
|
||||
break;
|
||||
case PROFILE_VISIBLE_ALL:
|
||||
$allowed = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
$allowed = false;
|
||||
}
|
||||
if (!$allowed) {
|
||||
unset($extra[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For standard user fields, access is controlled by the hiddenuserfields option and
|
||||
// some different capabilities. Check and remove these if the user can't access them.
|
||||
$hiddenfields = array_filter(explode(',', $CFG->hiddenuserfields));
|
||||
$hiddenidentifiers = array_intersect($extra, $hiddenfields);
|
||||
|
||||
if ($hiddenidentifiers) {
|
||||
if (!$context) {
|
||||
$canviewhiddenuserfields = true;
|
||||
} else if ($context->get_course_context(false)) {
|
||||
// We are somewhere inside a course.
|
||||
$canviewhiddenuserfields = has_capability('moodle/course:viewhiddenuserfields', $context);
|
||||
} else {
|
||||
// We are not inside a course.
|
||||
$canviewhiddenuserfields = has_capability('moodle/user:viewhiddendetails', $context);
|
||||
}
|
||||
|
||||
if (!$canviewhiddenuserfields) {
|
||||
// Remove hidden identifiers from the list.
|
||||
$extra = array_diff($extra, $hiddenidentifiers);
|
||||
}
|
||||
}
|
||||
|
||||
// Re-index the entries and return.
|
||||
$extra = array_values($extra);
|
||||
return $extra;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets SQL that can be used in a query to get the necessary fields.
|
||||
*
|
||||
* The result of this function is an object with fields 'selects', 'joins', 'params', and
|
||||
* 'mappings'.
|
||||
*
|
||||
* If not empty, the list of selects will begin with a comma and the list of joins will begin
|
||||
* and end with a space. You can include the result in your existing query like this:
|
||||
*
|
||||
* SELECT (your existing fields)
|
||||
* $selects
|
||||
* FROM {user} u
|
||||
* JOIN (your existing joins)
|
||||
* $joins
|
||||
*
|
||||
* When there are no custom fields then the 'joins' result will always be an empty string, and
|
||||
* 'params' will be an empty array.
|
||||
*
|
||||
* The $fieldmappings value is often not needed. It is an associative array from each field
|
||||
* name to an SQL expression for the value of that field, e.g.:
|
||||
* 'profile_field_frog' => 'uf1d_3.data'
|
||||
* 'city' => 'u.city'
|
||||
* This is helpful if you want to use the profile fields in a WHERE clause, becuase you can't
|
||||
* refer to the aliases used in the SELECT list there.
|
||||
*
|
||||
* The leading comma is included because this makes it work in the pattern above even if there
|
||||
* are no fields from the user_fields data (which can happen if doing identity fields and none
|
||||
* are selected). If you want the result without a leading comma, set $leadingcomma to false.
|
||||
*
|
||||
* If the 'id' field is included then it will always be first in the list. Otherwise, you
|
||||
* should not rely on the field order.
|
||||
*
|
||||
* For identity fields, the function does all the required capability checks to see if the
|
||||
* current user is allowed to see them in the specified context. You can pass context null
|
||||
* to get all the fields without checking permissions.
|
||||
*
|
||||
* If your code for any reason cannot cope with custom fields then you can turn them off.
|
||||
*
|
||||
* You can have either named or ? params. If you use named params, they are of the form
|
||||
* uf1s_2; the first number increments in each call using a static variable in this class and
|
||||
* the second number refers to the field being queried. A similar pattern is used to make
|
||||
* join aliases unique.
|
||||
*
|
||||
* If your query refers to the user table by an alias e.g. 'u' then specify this in the $alias
|
||||
* parameter; otherwise it will use {user} (if there are any joins for custom profile fields)
|
||||
* or simply refer to the field by name only (if there aren't).
|
||||
*
|
||||
* If you need to use a prefix on the field names (for example in case they might coincide with
|
||||
* existing result columns from your query, or if you want a convenient way to split out all
|
||||
* the user data into a separate object) then you can specify one here. For example, if you
|
||||
* include name fields and the prefix is 'u_' then the results will include 'u_firstname'.
|
||||
*
|
||||
* If you don't want to prefix all the field names but only change the id field name, use
|
||||
* the $renameid parameter. (When you use this parameter, it takes precedence over any prefix;
|
||||
* the id field will not be prefixed, while all others will.)
|
||||
*
|
||||
* @param string $alias Optional (but recommended) alias for user table in query, e.g. 'u'
|
||||
* @param bool $namedparams If true, uses named :parameters instead of indexed ? parameters
|
||||
* @param string $prefix Optional prefix for all field names in result, e.g. 'u_'
|
||||
* @param string $renameid Renames the 'id' field if specified, e.g. 'userid'
|
||||
* @param bool $leadingcomma If true the 'selects' list will start with a comma
|
||||
* @return \stdClass Object with necessary SQL components
|
||||
*/
|
||||
public function get_sql(string $alias = '', bool $namedparams = false, string $prefix = '',
|
||||
string $renameid = '', bool $leadingcomma = true): \stdClass {
|
||||
global $DB;
|
||||
|
||||
$fields = $this->get_required_fields();
|
||||
|
||||
$selects = '';
|
||||
$joins = '';
|
||||
$params = [];
|
||||
$mappings = [];
|
||||
|
||||
$unique = self::$uniqueidentifier++;
|
||||
$fieldcount = 0;
|
||||
|
||||
if ($alias) {
|
||||
$usertable = $alias . '.';
|
||||
} else {
|
||||
// If there is no alias, we still need to use {user} to identify the table when there
|
||||
// are joins with other tables. When there are no customfields then there are no joins
|
||||
// so we can refer to the fields by name alone.
|
||||
$gotcustomfields = false;
|
||||
foreach ($fields as $field) {
|
||||
if (preg_match(self::PROFILE_FIELD_REGEX, $field, $matches)) {
|
||||
$gotcustomfields = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($gotcustomfields) {
|
||||
$usertable = '{user}.';
|
||||
} else {
|
||||
$usertable = '';
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if (preg_match(self::PROFILE_FIELD_REGEX, $field, $matches)) {
|
||||
// Custom profile field.
|
||||
$shortname = $matches[1];
|
||||
|
||||
$fieldcount++;
|
||||
|
||||
$fieldalias = 'uf' . $unique . 'f_' . $fieldcount;
|
||||
$dataalias = 'uf' . $unique . 'd_' . $fieldcount;
|
||||
if ($namedparams) {
|
||||
$withoutcolon = 'uf' . $unique . 's' . $fieldcount;
|
||||
$placeholder = ':' . $withoutcolon;
|
||||
$params[$withoutcolon] = $shortname;
|
||||
} else {
|
||||
$placeholder = '?';
|
||||
$params[] = $shortname;
|
||||
}
|
||||
$joins .= " JOIN {user_info_field} $fieldalias ON $fieldalias.shortname = $placeholder
|
||||
LEFT JOIN {user_info_data} $dataalias ON $dataalias.fieldid = $fieldalias.id
|
||||
AND $dataalias.userid = {$usertable}id";
|
||||
// For Oracle we need to convert the field into a usable format.
|
||||
$fieldsql = $DB->sql_compare_text($dataalias . '.data', 255);
|
||||
$selects .= ", $fieldsql AS $prefix$field";
|
||||
$mappings[$field] = $fieldsql;
|
||||
} else {
|
||||
// Standard user table field.
|
||||
$selects .= ", $usertable$field";
|
||||
if ($field === 'id' && $renameid && $renameid !== 'id') {
|
||||
$selects .= " AS $renameid";
|
||||
} else if ($prefix) {
|
||||
$selects .= " AS $prefix$field";
|
||||
}
|
||||
$mappings[$field] = "$usertable$field";
|
||||
}
|
||||
}
|
||||
|
||||
// Add a space to the end of the joins list; this means it can be appended directly into
|
||||
// any existing query without worrying about whether the developer has remembered to add
|
||||
// whitespace after it.
|
||||
if ($joins) {
|
||||
$joins .= ' ';
|
||||
}
|
||||
|
||||
// Optionally remove the leading comma.
|
||||
if (!$leadingcomma) {
|
||||
$selects = ltrim($selects, ' ,');
|
||||
}
|
||||
|
||||
return (object)['selects' => $selects, 'joins' => $joins, 'params' => $params,
|
||||
'mappings' => $mappings];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the display name of a given user field.
|
||||
*
|
||||
* Supports field names from the 'user' database table, and custom profile fields supplied in
|
||||
* the format 'profile_field_xx'.
|
||||
*
|
||||
* @param string $field Field name in database
|
||||
* @return string Field name for display to user
|
||||
* @throws \coding_exception
|
||||
*/
|
||||
public static function get_display_name(string $field): string {
|
||||
global $CFG;
|
||||
|
||||
// Custom fields have special handling.
|
||||
if (preg_match(self::PROFILE_FIELD_REGEX, $field, $matches)) {
|
||||
require_once($CFG->dirroot . '/user/profile/lib.php');
|
||||
$fieldinfo = profile_get_custom_field_data_by_shortname($matches[1]);
|
||||
// Use format_string so it can be translated with multilang filter if necessary.
|
||||
return format_string($fieldinfo['name']);
|
||||
}
|
||||
|
||||
// Some fields have language strings which are not the same as field name.
|
||||
switch ($field) {
|
||||
case 'url' : {
|
||||
return get_string('webpage');
|
||||
}
|
||||
case 'icq' : {
|
||||
return get_string('icqnumber');
|
||||
}
|
||||
case 'skype' : {
|
||||
return get_string('skypeid');
|
||||
}
|
||||
case 'aim' : {
|
||||
return get_string('aimid');
|
||||
}
|
||||
case 'yahoo' : {
|
||||
return get_string('yahooid');
|
||||
}
|
||||
case 'msn' : {
|
||||
return get_string('msnid');
|
||||
}
|
||||
case 'picture' : {
|
||||
return get_string('pictureofuser');
|
||||
}
|
||||
}
|
||||
// Otherwise just use the same lang string.
|
||||
return get_string($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the unique identifier used to ensure that multiple SQL fragments generated in the
|
||||
* same request will have different identifiers for parameters and table aliases.
|
||||
*
|
||||
* This is intended only for use in unit testing.
|
||||
*/
|
||||
public static function reset_unique_identifier() {
|
||||
self::$uniqueidentifier = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a field name looks like a custom profile field i.e. it begins with profile_field_
|
||||
* (does not check if that profile field actually exists).
|
||||
*
|
||||
* @param string $fieldname Field name
|
||||
* @return string Empty string if not a profile field, or profile field name (without profile_field_)
|
||||
*/
|
||||
public static function match_custom_field(string $fieldname): string {
|
||||
if (preg_match(self::PROFILE_FIELD_REGEX, $fieldname, $matches)) {
|
||||
return $matches[1];
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1292,11 +1292,10 @@ class completion_info {
|
||||
context_course::instance($this->course->id),
|
||||
'moodle/course:isincompletionreports', $groupid, true);
|
||||
|
||||
$allusernames = get_all_user_name_fields(true, 'u');
|
||||
$sql = 'SELECT u.id, u.idnumber, ' . $allusernames;
|
||||
if ($extracontext) {
|
||||
$sql .= get_extra_user_fields_sql($extracontext, 'u', '', array('idnumber'));
|
||||
}
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$userfieldsapi = \core\user_fields::for_identity($extracontext, false)->with_name();
|
||||
$allusernames = $userfieldsapi->get_sql('u')->selects;
|
||||
$sql = 'SELECT u.id, u.idnumber ' . $allusernames;
|
||||
$sql .= ' FROM (' . $enrolledsql . ') eu JOIN {user} u ON u.id = eu.id';
|
||||
|
||||
if ($where) {
|
||||
|
||||
+17
-12
@@ -368,7 +368,8 @@ function users_order_by_sql($usertablealias = '', $search = null, context $conte
|
||||
$params[$paramkey] = $search;
|
||||
$paramkey++;
|
||||
|
||||
$fieldstocheck = array_merge(array('firstname', 'lastname'), get_extra_user_fields($context));
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$fieldstocheck = array_merge(array('firstname', 'lastname'), \core\user_fields::get_identity_fields($context, false));
|
||||
foreach ($fieldstocheck as $key => $field) {
|
||||
$exactconditions[] = 'LOWER(' . $tableprefix . $field . ') = LOWER(:' . $paramkey . ')';
|
||||
$params[$paramkey] = $search;
|
||||
@@ -479,7 +480,7 @@ function get_users_listing($sort='lastaccess', $dir='ASC', $page=0, $recordsperp
|
||||
|
||||
$fullname = $DB->sql_fullname();
|
||||
|
||||
$select = "deleted <> 1 AND id <> :guestid";
|
||||
$select = "deleted <> 1 AND u.id <> :guestid";
|
||||
$params = array('guestid' => $CFG->siteguest);
|
||||
|
||||
if (!empty($search)) {
|
||||
@@ -502,6 +503,10 @@ function get_users_listing($sort='lastaccess', $dir='ASC', $page=0, $recordsperp
|
||||
}
|
||||
|
||||
if ($extraselect) {
|
||||
// The extra WHERE clause may refer to the 'id' column which can now be ambiguous because we
|
||||
// changed the query to include joins, so replace any 'id' that is on its own (no alias)
|
||||
// with 'u.id'.
|
||||
$extraselect = preg_replace('~([ =]|^)id([ =]|$)~', '$1u.id$2', $extraselect);
|
||||
$select .= " AND $extraselect";
|
||||
$params = $params + (array)$extraparams;
|
||||
}
|
||||
@@ -511,21 +516,21 @@ function get_users_listing($sort='lastaccess', $dir='ASC', $page=0, $recordsperp
|
||||
}
|
||||
|
||||
// If a context is specified, get extra user fields that the current user
|
||||
// is supposed to see.
|
||||
$extrafields = '';
|
||||
// is supposed to see, otherwise just get the name fields.
|
||||
$userfields = \core\user_fields::for_name();
|
||||
if ($extracontext) {
|
||||
$extrafields = get_extra_user_fields_sql($extracontext, '', '',
|
||||
array('id', 'username', 'email', 'firstname', 'lastname', 'city', 'country',
|
||||
'lastaccess', 'confirmed', 'mnethostid'));
|
||||
$userfields->with_identity($extracontext, true);
|
||||
}
|
||||
$namefields = get_all_user_name_fields(true);
|
||||
$extrafields = "$extrafields, $namefields";
|
||||
$userfields->excluding('id', 'username', 'email', 'city', 'country', 'lastaccess', 'confirmed', 'mnethostid');
|
||||
['selects' => $selects, 'joins' => $joins, 'params' => $joinparams] =
|
||||
(array)$userfields->get_sql('u', true);
|
||||
|
||||
// warning: will return UNCONFIRMED USERS
|
||||
return $DB->get_records_sql("SELECT id, username, email, city, country, lastaccess, confirmed, mnethostid, suspended $extrafields
|
||||
FROM {user}
|
||||
return $DB->get_records_sql("SELECT u.id, username, email, city, country, lastaccess, confirmed, mnethostid, suspended $selects
|
||||
FROM {user} u
|
||||
$joins
|
||||
WHERE $select
|
||||
$sort", $params, $page, $recordsperpage);
|
||||
$sort", array_merge($params, $joinparams), $page, $recordsperpage);
|
||||
|
||||
}
|
||||
|
||||
|
||||
+128
-4
@@ -3083,8 +3083,10 @@ function user_get_participants_sql($courseid, $groupid = 0, $accesssince = 0, $r
|
||||
$joins = array('FROM {user} u');
|
||||
$wheres = array();
|
||||
|
||||
$userfields = get_extra_user_fields($context);
|
||||
$userfieldssql = user_picture::fields('u', $userfields);
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$userfields = \core\user_fields::get_identity_fields($context, false);
|
||||
$userfieldsapi = \core\user_fields::for_userpic()->including(...$userfields);
|
||||
$userfieldssql = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
|
||||
if ($isfrontpage) {
|
||||
$select = "SELECT $userfieldssql, u.lastaccess";
|
||||
@@ -3171,9 +3173,10 @@ function user_get_participants_sql($courseid, $groupid = 0, $accesssince = 0, $r
|
||||
}
|
||||
$conditions[] = $idnumber;
|
||||
|
||||
if (!empty($CFG->showuseridentity)) {
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$extrasearchfields = \core\user_fields::get_identity_fields($context, false);
|
||||
if (!empty($extrasearchfields)) {
|
||||
// Search all user identify fields.
|
||||
$extrasearchfields = explode(',', $CFG->showuseridentity);
|
||||
foreach ($extrasearchfields as $extrasearchfield) {
|
||||
if (in_array($extrasearchfield, ['email', 'idnumber', 'country'])) {
|
||||
// Already covered above. Search by country not supported.
|
||||
@@ -3310,3 +3313,124 @@ function make_categories_options() {
|
||||
|
||||
return core_course_category::make_categories_list('', 0, ' / ');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current user is shown any extra fields when listing users.
|
||||
*
|
||||
* Does not include any custom profile fields.
|
||||
*
|
||||
* @param object $context Context
|
||||
* @param array $already Array of fields that we're going to show anyway
|
||||
* so don't bother listing them
|
||||
* @return array Array of field names from user table, not including anything
|
||||
* listed in $already
|
||||
* @deprecated since Moodle 3.11 MDL-45242
|
||||
* @see \core\user_fields
|
||||
*/
|
||||
function get_extra_user_fields($context, $already = array()) {
|
||||
debugging('get_extra_user_fields() is deprecated. Please use the \core\user_fields API instead.', DEBUG_DEVELOPER);
|
||||
|
||||
$fields = \core\user_fields::for_identity($context, false)->excluding(...$already);
|
||||
return $fields->get_required_fields();
|
||||
}
|
||||
|
||||
/**
|
||||
* If the current user is to be shown extra user fields when listing or
|
||||
* selecting users, returns a string suitable for including in an SQL select
|
||||
* clause to retrieve those fields.
|
||||
*
|
||||
* Does not include any custom profile fields.
|
||||
*
|
||||
* @param context $context Context
|
||||
* @param string $alias Alias of user table, e.g. 'u' (default none)
|
||||
* @param string $prefix Prefix for field names using AS, e.g. 'u_' (default none)
|
||||
* @param array $already Array of fields that we're going to include anyway so don't list them (default none)
|
||||
* @return string Partial SQL select clause, beginning with comma, for example ',u.idnumber,u.department' unless it is blank
|
||||
* @deprecated since Moodle 3.11 MDL-45242
|
||||
* @see \core\user_fields
|
||||
*/
|
||||
function get_extra_user_fields_sql($context, $alias='', $prefix='', $already = array()) {
|
||||
debugging('get_extra_user_fields_sql() is deprecated. Please use the \core\user_fields API instead.', DEBUG_DEVELOPER);
|
||||
|
||||
$fields = \core\user_fields::for_identity($context, false)->excluding(...$already);
|
||||
// Note: There will never be any joins or join params because we turned off profile fields.
|
||||
$selects = $fields->get_sql($alias, false, $prefix)->selects;
|
||||
|
||||
return $selects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the display name of a field in the user table. Works for most fields that are commonly displayed to users.
|
||||
*
|
||||
* Also works for custom fields.
|
||||
*
|
||||
* @param string $field Field name, e.g. 'phone1'
|
||||
* @return string Text description taken from language file, e.g. 'Phone number'
|
||||
* @deprecated since Moodle 3.11 MDL-45242
|
||||
* @see \core\user_fields
|
||||
*/
|
||||
function get_user_field_name($field) {
|
||||
debugging('get_user_field_name() is deprecated. Please use \core\user_fields::get_display_name() instead', DEBUG_DEVELOPER);
|
||||
|
||||
return \core\user_fields::get_display_name($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* A centralised location for the all name fields. Returns an array / sql string snippet.
|
||||
*
|
||||
* @param bool $returnsql True for an sql select field snippet.
|
||||
* @param string $tableprefix table query prefix to use in front of each field.
|
||||
* @param string $prefix prefix added to the name fields e.g. authorfirstname.
|
||||
* @param string $fieldprefix sql field prefix e.g. id AS userid.
|
||||
* @param bool $order moves firstname and lastname to the top of the array / start of the string.
|
||||
* @return array|string All name fields.
|
||||
* @deprecated since Moodle 3.11 MDL-45242
|
||||
* @see \core\user_fields
|
||||
*/
|
||||
function get_all_user_name_fields($returnsql = false, $tableprefix = null, $prefix = null, $fieldprefix = null, $order = false) {
|
||||
debugging('get_all_user_name_fields() is deprecated. Please use the \core\user_fields API instead', DEBUG_DEVELOPER);
|
||||
|
||||
// This array is provided in this order because when called by fullname() (above) if firstname is before
|
||||
// firstnamephonetic str_replace() will change the wrong placeholder.
|
||||
$alternatenames = [];
|
||||
foreach (\core\user_fields::get_name_fields() as $field) {
|
||||
$alternatenames[$field] = $field;
|
||||
}
|
||||
|
||||
// Let's add a prefix to the array of user name fields if provided.
|
||||
if ($prefix) {
|
||||
foreach ($alternatenames as $key => $altname) {
|
||||
$alternatenames[$key] = $prefix . $altname;
|
||||
}
|
||||
}
|
||||
|
||||
// If we want the end result to have firstname and lastname at the front / top of the result.
|
||||
if ($order) {
|
||||
// Move the last two elements (firstname, lastname) off the array and put them at the top.
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
// Get the last element.
|
||||
$lastelement = end($alternatenames);
|
||||
// Remove it from the array.
|
||||
unset($alternatenames[$lastelement]);
|
||||
// Put the element back on the top of the array.
|
||||
$alternatenames = array_merge(array($lastelement => $lastelement), $alternatenames);
|
||||
}
|
||||
}
|
||||
|
||||
// Create an sql field snippet if requested.
|
||||
if ($returnsql) {
|
||||
if ($tableprefix) {
|
||||
if ($fieldprefix) {
|
||||
foreach ($alternatenames as $key => $altname) {
|
||||
$alternatenames[$key] = $tableprefix . '.' . $altname . ' AS ' . $fieldprefix . $altname;
|
||||
}
|
||||
} else {
|
||||
foreach ($alternatenames as $key => $altname) {
|
||||
$alternatenames[$key] = $tableprefix . '.' . $altname;
|
||||
}
|
||||
}
|
||||
}
|
||||
$alternatenames = implode(',', $alternatenames);
|
||||
}
|
||||
return $alternatenames;
|
||||
}
|
||||
|
||||
+2
-1
@@ -1321,7 +1321,8 @@ function groups_user_groups_visible($course, $userid, $cm = null) {
|
||||
function groups_get_groups_members($groupsids, $extrafields=null, $sort='lastname ASC') {
|
||||
global $DB;
|
||||
|
||||
$userfields = user_picture::fields('u', $extrafields);
|
||||
$userfieldsapi = \core\user_fields::for_userpic()->including(...($extrafields ?? []));
|
||||
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
list($insql, $params) = $DB->get_in_or_equal($groupsids);
|
||||
|
||||
return $DB->get_records_sql("SELECT $userfields
|
||||
|
||||
+5
-173
@@ -3540,7 +3540,7 @@ function fullname($user, $override=false) {
|
||||
}
|
||||
|
||||
// Get all of the name fields.
|
||||
$allnames = get_all_user_name_fields();
|
||||
$allnames = \core\user_fields::get_name_fields();
|
||||
if ($CFG->debugdeveloper) {
|
||||
foreach ($allnames as $allname) {
|
||||
if (!property_exists($user, $allname)) {
|
||||
@@ -3632,64 +3632,6 @@ function fullname($user, $override=false) {
|
||||
return $displayname;
|
||||
}
|
||||
|
||||
/**
|
||||
* A centralised location for the all name fields. Returns an array / sql string snippet.
|
||||
*
|
||||
* @param bool $returnsql True for an sql select field snippet.
|
||||
* @param string $tableprefix table query prefix to use in front of each field.
|
||||
* @param string $prefix prefix added to the name fields e.g. authorfirstname.
|
||||
* @param string $fieldprefix sql field prefix e.g. id AS userid.
|
||||
* @param bool $order moves firstname and lastname to the top of the array / start of the string.
|
||||
* @return array|string All name fields.
|
||||
*/
|
||||
function get_all_user_name_fields($returnsql = false, $tableprefix = null, $prefix = null, $fieldprefix = null, $order = false) {
|
||||
// This array is provided in this order because when called by fullname() (above) if firstname is before
|
||||
// firstnamephonetic str_replace() will change the wrong placeholder.
|
||||
$alternatenames = array('firstnamephonetic' => 'firstnamephonetic',
|
||||
'lastnamephonetic' => 'lastnamephonetic',
|
||||
'middlename' => 'middlename',
|
||||
'alternatename' => 'alternatename',
|
||||
'firstname' => 'firstname',
|
||||
'lastname' => 'lastname');
|
||||
|
||||
// Let's add a prefix to the array of user name fields if provided.
|
||||
if ($prefix) {
|
||||
foreach ($alternatenames as $key => $altname) {
|
||||
$alternatenames[$key] = $prefix . $altname;
|
||||
}
|
||||
}
|
||||
|
||||
// If we want the end result to have firstname and lastname at the front / top of the result.
|
||||
if ($order) {
|
||||
// Move the last two elements (firstname, lastname) off the array and put them at the top.
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
// Get the last element.
|
||||
$lastelement = end($alternatenames);
|
||||
// Remove it from the array.
|
||||
unset($alternatenames[$lastelement]);
|
||||
// Put the element back on the top of the array.
|
||||
$alternatenames = array_merge(array($lastelement => $lastelement), $alternatenames);
|
||||
}
|
||||
}
|
||||
|
||||
// Create an sql field snippet if requested.
|
||||
if ($returnsql) {
|
||||
if ($tableprefix) {
|
||||
if ($fieldprefix) {
|
||||
foreach ($alternatenames as $key => $altname) {
|
||||
$alternatenames[$key] = $tableprefix . '.' . $altname . ' AS ' . $fieldprefix . $altname;
|
||||
}
|
||||
} else {
|
||||
foreach ($alternatenames as $key => $altname) {
|
||||
$alternatenames[$key] = $tableprefix . '.' . $altname;
|
||||
}
|
||||
}
|
||||
}
|
||||
$alternatenames = implode(',', $alternatenames);
|
||||
}
|
||||
return $alternatenames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduces lines of duplicated code for getting user name fields.
|
||||
*
|
||||
@@ -3703,7 +3645,10 @@ function get_all_user_name_fields($returnsql = false, $tableprefix = null, $pref
|
||||
* @return object User name fields.
|
||||
*/
|
||||
function username_load_fields_from_object($addtoobject, $secondobject, $prefix = null, $additionalfields = null) {
|
||||
$fields = get_all_user_name_fields(false, null, $prefix);
|
||||
$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
|
||||
// the key is a number and then sets the key to the array value.
|
||||
@@ -3752,119 +3697,6 @@ function order_in_string($values, $stringformat) {
|
||||
return $valuearray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if current user is shown any extra fields when listing users.
|
||||
*
|
||||
* @param object $context Context
|
||||
* @param array $already Array of fields that we're going to show anyway
|
||||
* so don't bother listing them
|
||||
* @return array Array of field names from user table, not including anything
|
||||
* listed in $already
|
||||
*/
|
||||
function get_extra_user_fields($context, $already = array()) {
|
||||
global $CFG;
|
||||
|
||||
// Only users with permission get the extra fields.
|
||||
if (!has_capability('moodle/site:viewuseridentity', $context)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Split showuseridentity on comma (filter needed in case the showuseridentity is empty).
|
||||
$extra = array_filter(explode(',', $CFG->showuseridentity));
|
||||
|
||||
foreach ($extra as $key => $field) {
|
||||
if (in_array($field, $already)) {
|
||||
unset($extra[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
// If the identity fields are also among hidden fields, make sure the user can see them.
|
||||
$hiddenfields = array_filter(explode(',', $CFG->hiddenuserfields));
|
||||
$hiddenidentifiers = array_intersect($extra, $hiddenfields);
|
||||
|
||||
if ($hiddenidentifiers) {
|
||||
if ($context->get_course_context(false)) {
|
||||
// We are somewhere inside a course.
|
||||
$canviewhiddenuserfields = has_capability('moodle/course:viewhiddenuserfields', $context);
|
||||
|
||||
} else {
|
||||
// We are not inside a course.
|
||||
$canviewhiddenuserfields = has_capability('moodle/user:viewhiddendetails', $context);
|
||||
}
|
||||
|
||||
if (!$canviewhiddenuserfields) {
|
||||
// Remove hidden identifiers from the list.
|
||||
$extra = array_diff($extra, $hiddenidentifiers);
|
||||
}
|
||||
}
|
||||
|
||||
// Re-index the entries.
|
||||
$extra = array_values($extra);
|
||||
|
||||
return $extra;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the current user is to be shown extra user fields when listing or
|
||||
* selecting users, returns a string suitable for including in an SQL select
|
||||
* clause to retrieve those fields.
|
||||
*
|
||||
* @param context $context Context
|
||||
* @param string $alias Alias of user table, e.g. 'u' (default none)
|
||||
* @param string $prefix Prefix for field names using AS, e.g. 'u_' (default none)
|
||||
* @param array $already Array of fields that we're going to include anyway so don't list them (default none)
|
||||
* @return string Partial SQL select clause, beginning with comma, for example ',u.idnumber,u.department' unless it is blank
|
||||
*/
|
||||
function get_extra_user_fields_sql($context, $alias='', $prefix='', $already = array()) {
|
||||
$fields = get_extra_user_fields($context, $already);
|
||||
$result = '';
|
||||
// Add punctuation for alias.
|
||||
if ($alias !== '') {
|
||||
$alias .= '.';
|
||||
}
|
||||
foreach ($fields as $field) {
|
||||
$result .= ', ' . $alias . $field;
|
||||
if ($prefix) {
|
||||
$result .= ' AS ' . $prefix . $field;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the display name of a field in the user table. Works for most fields that are commonly displayed to users.
|
||||
* @param string $field Field name, e.g. 'phone1'
|
||||
* @return string Text description taken from language file, e.g. 'Phone number'
|
||||
*/
|
||||
function get_user_field_name($field) {
|
||||
// Some fields have language strings which are not the same as field name.
|
||||
switch ($field) {
|
||||
case 'url' : {
|
||||
return get_string('webpage');
|
||||
}
|
||||
case 'icq' : {
|
||||
return get_string('icqnumber');
|
||||
}
|
||||
case 'skype' : {
|
||||
return get_string('skypeid');
|
||||
}
|
||||
case 'aim' : {
|
||||
return get_string('aimid');
|
||||
}
|
||||
case 'yahoo' : {
|
||||
return get_string('yahooid');
|
||||
}
|
||||
case 'msn' : {
|
||||
return get_string('msnid');
|
||||
}
|
||||
case 'picture' : {
|
||||
return get_string('pictureofuser');
|
||||
}
|
||||
}
|
||||
// Otherwise just use the same lang string.
|
||||
return get_string($field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether a given authentication plugin exists.
|
||||
*
|
||||
|
||||
@@ -125,12 +125,8 @@ function core_myprofile_navigation(core_user\output\myprofile\tree $tree, $user,
|
||||
} else {
|
||||
$hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
|
||||
}
|
||||
$canviewuseridentity = has_capability('moodle/site:viewuseridentity', $courseorusercontext);
|
||||
if ($canviewuseridentity) {
|
||||
$identityfields = array_flip(explode(',', $CFG->showuseridentity));
|
||||
} else {
|
||||
$identityfields = array();
|
||||
}
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$identityfields = array_flip(\core\user_fields::get_identity_fields($courseorusercontext, false));
|
||||
|
||||
if (is_mnet_remote_user($user)) {
|
||||
$sql = "SELECT h.id, h.name, h.wwwroot,
|
||||
@@ -156,7 +152,7 @@ function core_myprofile_navigation(core_user\output\myprofile\tree $tree, $user,
|
||||
or ($user->maildisplay == core_user::MAILDISPLAY_COURSE_MEMBERS_ONLY and enrol_sharing_course($user, $USER))
|
||||
or has_capability('moodle/course:useremail', $courseorusercontext) // TODO: Deprecate/remove for MDL-37479.
|
||||
))
|
||||
or (isset($identityfields['email']) and $canviewuseridentity)
|
||||
or (isset($identityfields['email']))
|
||||
) {
|
||||
$maildisplay = obfuscate_mailto($user->email, '');
|
||||
if ($iscurrentuser) {
|
||||
|
||||
+18
-40
@@ -149,13 +149,6 @@ class file_picker implements renderable {
|
||||
* @category output
|
||||
*/
|
||||
class user_picture implements renderable {
|
||||
/**
|
||||
* @var array List of mandatory fields in user record here. (do not include
|
||||
* TEXT columns because it would break SELECT DISTINCT in MSSQL and ORACLE)
|
||||
*/
|
||||
protected static $fields = array('id', 'picture', 'firstname', 'lastname', 'firstnamephonetic', 'lastnamephonetic',
|
||||
'middlename', 'alternatename', 'imagealt', 'email');
|
||||
|
||||
/**
|
||||
* @var stdClass A user object with at least fields all columns specified
|
||||
* in $fields array constant set.
|
||||
@@ -227,17 +220,18 @@ class user_picture implements renderable {
|
||||
|
||||
// only touch the DB if we are missing data and complain loudly...
|
||||
$needrec = false;
|
||||
foreach (self::$fields as $field) {
|
||||
foreach (\core\user_fields::get_picture_fields() as $field) {
|
||||
if (!property_exists($user, $field)) {
|
||||
$needrec = true;
|
||||
debugging('Missing '.$field.' property in $user object, this is a performance problem that needs to be fixed by a developer. '
|
||||
.'Please use user_picture::fields() to get the full list of required fields.', DEBUG_DEVELOPER);
|
||||
.'Please use the \core\user_fields API to get the full list of required fields.', DEBUG_DEVELOPER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($needrec) {
|
||||
$this->user = $DB->get_record('user', array('id'=>$user->id), self::fields(), MUST_EXIST);
|
||||
$this->user = $DB->get_record('user', array('id' => $user->id),
|
||||
implode(',', \core\user_fields::get_picture_fields()), MUST_EXIST);
|
||||
} else {
|
||||
$this->user = clone($user);
|
||||
}
|
||||
@@ -255,39 +249,23 @@ class user_picture implements renderable {
|
||||
* @param string $idalias alias of id field
|
||||
* @param string $fieldprefix prefix to add to all columns in their aliases, does not apply to 'id'
|
||||
* @return string
|
||||
* @deprecated since Moodle 3.11 MDL-45242
|
||||
* @see \core\user_fields
|
||||
*/
|
||||
public static function fields($tableprefix = '', array $extrafields = NULL, $idalias = 'id', $fieldprefix = '') {
|
||||
if (!$tableprefix and !$extrafields and !$idalias) {
|
||||
return implode(',', self::$fields);
|
||||
}
|
||||
if ($tableprefix) {
|
||||
$tableprefix .= '.';
|
||||
}
|
||||
foreach (self::$fields as $field) {
|
||||
if ($field === 'id' and $idalias and $idalias !== 'id') {
|
||||
$fields[$field] = "$tableprefix$field AS $idalias";
|
||||
} else {
|
||||
if ($fieldprefix and $field !== 'id') {
|
||||
$fields[$field] = "$tableprefix$field AS $fieldprefix$field";
|
||||
} else {
|
||||
$fields[$field] = "$tableprefix$field";
|
||||
}
|
||||
}
|
||||
}
|
||||
// add extra fields if not already there
|
||||
debugging('user_picture::fields() is deprecated. Please use the \core\user_fields API instead.', DEBUG_DEVELOPER);
|
||||
$userfields = \core\user_fields::for_userpic();
|
||||
if ($extrafields) {
|
||||
foreach ($extrafields as $e) {
|
||||
if ($e === 'id' or isset($fields[$e])) {
|
||||
continue;
|
||||
}
|
||||
if ($fieldprefix) {
|
||||
$fields[$e] = "$tableprefix$e AS $fieldprefix$e";
|
||||
} else {
|
||||
$fields[$e] = "$tableprefix$e";
|
||||
}
|
||||
}
|
||||
$userfields->including(...$extrafields);
|
||||
}
|
||||
return implode(',', $fields);
|
||||
$selects = $userfields->get_sql($tableprefix, false, $fieldprefix, $idalias, false)->selects;
|
||||
if ($tableprefix === '') {
|
||||
// If no table alias is specified, don't add {user}. in front of fields.
|
||||
$selects = str_replace('{user}.', '', $selects);
|
||||
}
|
||||
// Maintain legacy behaviour where the field list was done with 'implode' and no spaces.
|
||||
$selects = str_replace(', ', ',', $selects);
|
||||
return $selects;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -310,7 +288,7 @@ class user_picture implements renderable {
|
||||
|
||||
$return = new stdClass();
|
||||
|
||||
foreach (self::$fields as $field) {
|
||||
foreach (\core\user_fields::get_picture_fields() as $field) {
|
||||
if ($field === 'id') {
|
||||
if (property_exists($record, $idalias)) {
|
||||
$return->id = $record->{$idalias};
|
||||
|
||||
+3
-3
@@ -608,7 +608,7 @@ class flexible_table {
|
||||
if (isset($this->columns[$column])) {
|
||||
continue; // This column is OK.
|
||||
}
|
||||
if (in_array($column, get_all_user_name_fields()) &&
|
||||
if (in_array($column, \core\user_fields::get_name_fields()) &&
|
||||
isset($this->columns['fullname'])) {
|
||||
continue; // This column is OK.
|
||||
}
|
||||
@@ -1235,7 +1235,7 @@ class flexible_table {
|
||||
$nameformat = get_string('fullnamedisplay');
|
||||
}
|
||||
|
||||
$requirednames = order_in_string(get_all_user_name_fields(), $nameformat);
|
||||
$requirednames = order_in_string(\core\user_fields::get_name_fields(), $nameformat);
|
||||
|
||||
if (!empty($requirednames)) {
|
||||
if ($this->is_sortable($column)) {
|
||||
@@ -1315,7 +1315,7 @@ class flexible_table {
|
||||
$sortdata = array_merge([$sortby => $sortorder], $sortdata);
|
||||
}
|
||||
|
||||
$usernamefields = get_all_user_name_fields();
|
||||
$usernamefields = \core\user_fields::get_name_fields();
|
||||
$sortdata = array_filter($sortdata, function($sortby) use ($usernamefields) {
|
||||
$isvalidsort = $sortby && $this->is_sortable($sortby);
|
||||
$isvalidsort = $isvalidsort && empty($this->prefs['collapse'][$sortby]);
|
||||
|
||||
@@ -1190,6 +1190,123 @@ EOD;
|
||||
return $this->get_plugin_generator('core_customfield')->create_field($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new category for custom profile fields.
|
||||
*
|
||||
* @param array $data Array with 'name' and optionally 'sortorder'
|
||||
* @return \stdClass New category object
|
||||
*/
|
||||
public function create_custom_profile_field_category(array $data): \stdClass {
|
||||
global $DB;
|
||||
|
||||
// Pick next sortorder if not defined.
|
||||
if (!array_key_exists('sortorder', $data)) {
|
||||
$data['sortorder'] = (int)$DB->get_field_sql('SELECT MAX(sortorder) FROM {user_info_category}') + 1;
|
||||
}
|
||||
|
||||
$category = (object)[
|
||||
'name' => $data['name'],
|
||||
'sortorder' => $data['sortorder']
|
||||
];
|
||||
$category->id = $DB->insert_record('user_info_category', $category);
|
||||
|
||||
return $category;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new custom profile field.
|
||||
*
|
||||
* Optional fields are:
|
||||
*
|
||||
* categoryid (or use 'category' to specify by name). If you don't specify
|
||||
* either, it will add the field to a 'Testing' category, which will be created for you if
|
||||
* necessary.
|
||||
*
|
||||
* sortorder (if you don't specify this, it will pick the next one in the category).
|
||||
*
|
||||
* all the other database fields (if you don't specify this, it will pick sensible defaults
|
||||
* based on the data type).
|
||||
*
|
||||
* @param array $data Array with 'datatype', 'shortname', and 'name'
|
||||
* @return \stdClass Database object from the user_info_field table
|
||||
*/
|
||||
public function create_custom_profile_field(array $data): \stdClass {
|
||||
global $DB, $CFG;
|
||||
require_once($CFG->dirroot . '/user/profile/lib.php');
|
||||
|
||||
// Set up category if necessary.
|
||||
if (!array_key_exists('categoryid', $data)) {
|
||||
if (array_key_exists('category', $data)) {
|
||||
$data['categoryid'] = $DB->get_field('user_info_category', 'id',
|
||||
['name' => $data['category']], MUST_EXIST);
|
||||
} else {
|
||||
// Make up a 'Testing' category or use existing.
|
||||
$data['categoryid'] = $DB->get_field('user_info_category', 'id', ['name' => 'Testing']);
|
||||
if (!$data['categoryid']) {
|
||||
$created = $this->create_custom_profile_field_category(['name' => 'Testing']);
|
||||
$data['categoryid'] = $created->id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pick sort order if necessary.
|
||||
if (!array_key_exists('sortorder', $data)) {
|
||||
$data['sortorder'] = (int)$DB->get_field_sql(
|
||||
'SELECT MAX(sortorder) FROM {user_info_field} WHERE categoryid = ?',
|
||||
[$data['categoryid']]) + 1;
|
||||
}
|
||||
|
||||
// Defaults for other values.
|
||||
$defaults = [
|
||||
'description' => '',
|
||||
'descriptionformat' => 0,
|
||||
'required' => 0,
|
||||
'locked' => 0,
|
||||
'visible' => PROFILE_VISIBLE_ALL,
|
||||
'forceunique' => 0,
|
||||
'signup' => 0,
|
||||
'defaultdata' => '',
|
||||
'defaultdataformat' => 0,
|
||||
'param1' => '',
|
||||
'param2' => '',
|
||||
'param3' => '',
|
||||
'param4' => '',
|
||||
'param5' => ''
|
||||
];
|
||||
|
||||
// Type-specific defaults for other values.
|
||||
$typedefaults = [
|
||||
'text' => [
|
||||
'param1' => 30,
|
||||
'param2' => 2048
|
||||
],
|
||||
'menu' => [
|
||||
'param1' => "Yes\nNo",
|
||||
'defaultdata' => 'No'
|
||||
],
|
||||
'datetime' => [
|
||||
'param1' => '2010',
|
||||
'param2' => '2015',
|
||||
'param3' => 1
|
||||
],
|
||||
'checkbox' => [
|
||||
'defaultdata' => 0
|
||||
]
|
||||
];
|
||||
foreach ($typedefaults[$data['datatype']] as $field => $value) {
|
||||
$defaults[$field] = $value;
|
||||
}
|
||||
|
||||
foreach ($defaults as $field => $value) {
|
||||
if (!array_key_exists($field, $data)) {
|
||||
$data[$field] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
$data['id'] = $DB->insert_record('user_info_field', $data);
|
||||
return (object)$data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new user, and enrol them in the specified course as the supplied role.
|
||||
*
|
||||
|
||||
@@ -466,4 +466,119 @@ class core_test_generator_testcase extends advanced_testcase {
|
||||
'parent' => $gradecategory->id));
|
||||
$this->assertEquals($gradecategory->id, $gradecategory2->parent);
|
||||
}
|
||||
|
||||
public function test_create_custom_profile_field_category() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$generator = $this->getDataGenerator();
|
||||
|
||||
// Insert first category without specified sortorder.
|
||||
$result = $generator->create_custom_profile_field_category(['name' => 'Frogs']);
|
||||
$record = $DB->get_record('user_info_category', ['name' => 'Frogs']);
|
||||
$this->assertEquals(1, $record->sortorder);
|
||||
|
||||
// Also check the return value.
|
||||
$this->assertEquals(1, $result->sortorder);
|
||||
$this->assertEquals('Frogs', $result->name);
|
||||
$this->assertEquals($record->id, $result->id);
|
||||
|
||||
// Insert next category without specified sortorder.
|
||||
$generator->create_custom_profile_field_category(['name' => 'Zombies']);
|
||||
$record = $DB->get_record('user_info_category', ['name' => 'Zombies']);
|
||||
$this->assertEquals(2, $record->sortorder);
|
||||
|
||||
// Insert category with specified sortorder.
|
||||
$generator->create_custom_profile_field_category(['name' => 'Toads', 'sortorder' => 9]);
|
||||
$record = $DB->get_record('user_info_category', ['name' => 'Toads']);
|
||||
$this->assertEquals(9, $record->sortorder);
|
||||
|
||||
// Insert another with unspecified sortorder.
|
||||
$generator->create_custom_profile_field_category(['name' => 'Werewolves']);
|
||||
$record = $DB->get_record('user_info_category', ['name' => 'Werewolves']);
|
||||
$this->assertEquals(10, $record->sortorder);
|
||||
}
|
||||
|
||||
public function test_create_custom_profile_field() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$generator = $this->getDataGenerator();
|
||||
|
||||
// Insert minimal field without specified category.
|
||||
$field1 = $generator->create_custom_profile_field(
|
||||
['datatype' => 'text', 'shortname' => 'colour', 'name' => 'Colour']);
|
||||
$record = $DB->get_record('user_info_field', ['shortname' => 'colour']);
|
||||
|
||||
// Check specified values.
|
||||
$this->assertEquals('Colour', $record->name);
|
||||
$this->assertEquals('text', $record->datatype);
|
||||
|
||||
// Check sortorder (first in category).
|
||||
$this->assertEquals(1, $record->sortorder);
|
||||
|
||||
// Check shared defaults for most datatypes.
|
||||
$this->assertEquals('', $record->description);
|
||||
$this->assertEquals(0, $record->descriptionformat);
|
||||
$this->assertEquals(0, $record->required);
|
||||
$this->assertEquals(0, $record->locked);
|
||||
$this->assertEquals(PROFILE_VISIBLE_ALL, $record->visible);
|
||||
$this->assertEquals(0, $record->forceunique);
|
||||
$this->assertEquals(0, $record->signup);
|
||||
$this->assertEquals('', $record->defaultdata);
|
||||
$this->assertEquals(0, $record->defaultdataformat);
|
||||
|
||||
// Check specific defaults for text datatype.
|
||||
$this->assertEquals(30, $record->param1);
|
||||
$this->assertEquals(2048, $record->param2);
|
||||
|
||||
// Check the returned value matches the database data.
|
||||
$this->assertEquals($record, $field1);
|
||||
|
||||
// The category should relate to a new 'testing' category.
|
||||
$catrecord = $DB->get_record('user_info_category', ['id' => $record->categoryid]);
|
||||
$this->assertEquals('Testing', $catrecord->name);
|
||||
$this->assertEquals(1, $catrecord->sortorder);
|
||||
|
||||
// Create another field, this time supplying values for a few of the fields.
|
||||
$generator->create_custom_profile_field(
|
||||
['datatype' => 'text', 'shortname' => 'brightness', 'name' => 'Brightness',
|
||||
'required' => 1, 'forceunique' => 1]);
|
||||
$record = $DB->get_record('user_info_field', ['shortname' => 'brightness']);
|
||||
|
||||
// Same testing category, next sortorder.
|
||||
$this->assertEquals($catrecord->id, $record->categoryid);
|
||||
$this->assertEquals(2, $record->sortorder);
|
||||
|
||||
// Check modified fields.
|
||||
$this->assertEquals(1, $record->required);
|
||||
$this->assertEquals(1, $record->forceunique);
|
||||
|
||||
// Create a field in specified category by id or name...
|
||||
$category = $generator->create_custom_profile_field_category(['name' => 'Amphibians']);
|
||||
$field3 = $generator->create_custom_profile_field(
|
||||
['datatype' => 'text', 'shortname' => 'frog', 'name' => 'Frog',
|
||||
'categoryid' => $category->id]);
|
||||
$this->assertEquals($category->id, $field3->categoryid);
|
||||
$this->assertEquals(1, $field3->sortorder);
|
||||
$field4 = $generator->create_custom_profile_field(
|
||||
['datatype' => 'text', 'shortname' => 'toad', 'name' => 'Toad',
|
||||
'category' => 'Amphibians', 'sortorder' => 4]);
|
||||
$this->assertEquals($category->id, $field4->categoryid);
|
||||
$this->assertEquals(4, $field4->sortorder);
|
||||
|
||||
// Check defaults for menu, datetime, and checkbox.
|
||||
$field5 = $generator->create_custom_profile_field(
|
||||
['datatype' => 'menu', 'shortname' => 'cuisine', 'name' => 'Cuisine']);
|
||||
$this->assertEquals("Yes\nNo", $field5->param1);
|
||||
$this->assertEquals('No', $field5->defaultdata);
|
||||
$field6 = $generator->create_custom_profile_field(
|
||||
['datatype' => 'datetime', 'shortname' => 'epoch', 'name' => 'Epoch']);
|
||||
$this->assertEquals(2010, $field6->param1);
|
||||
$this->assertEquals(2015, $field6->param2);
|
||||
$this->assertEquals(1, $field6->param3);
|
||||
$field7 = $generator->create_custom_profile_field(
|
||||
['datatype' => 'checkbox', 'shortname' => 'areyousure', 'name' => 'Are you sure?']);
|
||||
$this->assertEquals(0, $field7->defaultdata);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
@core
|
||||
Feature: Select user identity fields
|
||||
In order to see who users are at my institution
|
||||
As an administrator
|
||||
I can configure which user fields show with lists of users
|
||||
|
||||
Background:
|
||||
Given the following "custom profile fields" exist:
|
||||
| datatype | shortname | name | param2 |
|
||||
| text | speciality | Speciality | 255 |
|
||||
| checkbox | fool | Foolish | |
|
||||
| text | thesis | Thesis | 100000 |
|
||||
And the following "users" exist:
|
||||
| username | department | profile_field_speciality | email |
|
||||
| user1 | Amphibians | Frogs | email1@example.org |
|
||||
| user2 | Undead | Zombies | email2@example.org |
|
||||
And the following "courses" exist:
|
||||
| shortname | fullname |
|
||||
| C1 | Course 1 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| user1 | C1 | manager |
|
||||
| user2 | C1 | manager |
|
||||
|
||||
Scenario: The admin settings screen should show text custom fields (and let you choose them)
|
||||
When I log in as "admin"
|
||||
And I navigate to "Users > Permissions > User policies" in site administration
|
||||
Then I should see "Speciality" in the "#admin-showuseridentity" "css_element"
|
||||
And I should not see "Foolish" in the "#admin-showuseridentity" "css_element"
|
||||
And I should not see "Thesis" in the "#admin-showuseridentity" "css_element"
|
||||
And I set the field "Speciality" to "1"
|
||||
And I press "Save changes"
|
||||
And the field "Speciality" matches value "1"
|
||||
|
||||
Scenario: When you choose custom fields, these should be displayed in the 'Browse list of users' screen
|
||||
Given the following config values are set as admin:
|
||||
| showuseridentity | username,department,profile_field_speciality |
|
||||
When I log in as "admin"
|
||||
And I navigate to "Users > Accounts > Browse list of users" in site administration
|
||||
Then I should see "Speciality" in the "thead" "css_element"
|
||||
And I should see "Department" in the "thead" "css_element"
|
||||
And I should not see "Email" in the "thead" "css_element"
|
||||
Then I should see "Amphibians" in the "user1" "table_row"
|
||||
And I should see "Frogs" in the "user1" "table_row"
|
||||
And I should not see "email1@example.org"
|
||||
And I should see "Undead" in the "user2" "table_row"
|
||||
And I should see "Zombies" in the "user2" "table_row"
|
||||
And I should not see "email2@example.org"
|
||||
|
||||
Scenario: When you choose custom fields, these should be displayed in the 'Participants' screen
|
||||
Given the following config values are set as admin:
|
||||
| showuseridentity | username,department,profile_field_speciality |
|
||||
When I am on the "C1" "Course" page logged in as "user1"
|
||||
And I navigate to course participants
|
||||
Then I should see "Frogs" in the "user1" "table_row"
|
||||
And I should see "Zombies" in the "user2" "table_row"
|
||||
|
||||
@javascript
|
||||
Scenario: The user filtering options on the participants screen should work for custom profile fields
|
||||
Given the following config values are set as admin:
|
||||
| showuseridentity | username,department,profile_field_speciality |
|
||||
When I am on the "C1" "Course" page logged in as "admin"
|
||||
And I navigate to course participants
|
||||
And I set the field "type" in the "Filter 1" "fieldset" to "Keyword"
|
||||
And I set the field "Type..." in the "Filter 1" "fieldset" to "Frogs"
|
||||
# You have to tab out to make it actually apply.
|
||||
And I press tab
|
||||
And I click on "Apply filters" "button"
|
||||
Then I should see "user1" in the "participants" "table"
|
||||
And I should not see "user2" in the "participants" "table"
|
||||
@@ -746,4 +746,100 @@ class core_datalib_testcase extends advanced_testcase {
|
||||
"Please also make sure \$CFG->maxcoursesincategory * MAX_COURSE_CATEGORIES less than max integer. " .
|
||||
"See tracker issues: MDL-25669 and MDL-69573");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the get_users_listing function.
|
||||
*/
|
||||
public function test_get_users_listing(): void {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$generator = $this->getDataGenerator();
|
||||
|
||||
// Set up profile field.
|
||||
$generator->create_custom_profile_field(['datatype' => 'text',
|
||||
'shortname' => 'specialid', 'name' => 'Special user id']);
|
||||
|
||||
// Set up the show user identity option.
|
||||
set_config('showuseridentity', 'department,profile_field_specialid');
|
||||
|
||||
// Get all the existing user ids (we're going to remove these from test results).
|
||||
$existingids = array_fill_keys($DB->get_fieldset_select('user', 'id', '1 = 1'), true);
|
||||
|
||||
// Create some test user accounts.
|
||||
$userids = [];
|
||||
foreach (['a', 'b', 'c', 'd'] as $key) {
|
||||
$record = [
|
||||
'username' => 'user_' . $key,
|
||||
'firstname' => $key . '_first',
|
||||
'lastname' => 'last_' . $key,
|
||||
'department' => 'department_' . $key,
|
||||
'profile_field_specialid' => 'special_' . $key,
|
||||
'lastaccess' => ord($key)
|
||||
];
|
||||
$user = $generator->create_user($record);
|
||||
$userids[] = $user->id;
|
||||
}
|
||||
|
||||
// Check default result with no parameters.
|
||||
$results = get_users_listing();
|
||||
$results = array_diff_key($results, $existingids);
|
||||
|
||||
// It should return all the results in order.
|
||||
$this->assertEquals($userids, array_keys($results));
|
||||
|
||||
// Results should have some general fields and name fields, check some samples.
|
||||
$this->assertEquals('user_a', $results[$userids[0]]->username);
|
||||
$this->assertEquals('[email protected]', $results[$userids[0]]->email);
|
||||
$this->assertEquals(1, $results[$userids[0]]->confirmed);
|
||||
$this->assertEquals('a_first', $results[$userids[0]]->firstname);
|
||||
$this->assertObjectHasAttribute('firstnamephonetic', $results[$userids[0]]);
|
||||
|
||||
// Should not have the custom field or department because no context specified.
|
||||
$this->assertObjectNotHasAttribute('department', $results[$userids[0]]);
|
||||
$this->assertObjectNotHasAttribute('profile_field_specialid', $results[$userids[0]]);
|
||||
|
||||
// Check sorting.
|
||||
$results = get_users_listing('username', 'DESC');
|
||||
$results = array_diff_key($results, $existingids);
|
||||
$this->assertEquals([$userids[3], $userids[2], $userids[1], $userids[0]], array_keys($results));
|
||||
|
||||
// Add the options to showuseridentity and check it returns those fields but only if you
|
||||
// specify a context AND have permissions.
|
||||
$results = get_users_listing('lastaccess', 'asc', 0, 0, '', '', '', '', null,
|
||||
\context_system::instance());
|
||||
$this->assertObjectNotHasAttribute('department', $results[$userids[0]]);
|
||||
$this->assertObjectNotHasAttribute('profile_field_specialid', $results[$userids[0]]);
|
||||
$this->setAdminUser();
|
||||
$results = get_users_listing('lastaccess', 'asc', 0, 0, '', '', '', '', null,
|
||||
\context_system::instance());
|
||||
$this->assertEquals('department_a', $results[$userids[0]]->department);
|
||||
$this->assertEquals('special_a', $results[$userids[0]]->profile_field_specialid);
|
||||
|
||||
// Check search (full name, email, username).
|
||||
$results = get_users_listing('lastaccess', 'asc', 0, 0, 'b_first last_b');
|
||||
$this->assertEquals([$userids[1]], array_keys($results));
|
||||
$results = get_users_listing('lastaccess', 'asc', 0, 0, 'c@example');
|
||||
$this->assertEquals([$userids[2]], array_keys($results));
|
||||
$results = get_users_listing('lastaccess', 'asc', 0, 0, 'user_d');
|
||||
$this->assertEquals([$userids[3]], array_keys($results));
|
||||
|
||||
// Check first and last initial restriction (all the test ones have same last initial).
|
||||
$results = get_users_listing('lastaccess', 'asc', 0, 0, '', 'C');
|
||||
$this->assertEquals([$userids[2]], array_keys($results));
|
||||
$results = get_users_listing('lastaccess', 'asc', 0, 0, '', '', 'L');
|
||||
$results = array_diff_key($results, $existingids);
|
||||
$this->assertEquals($userids, array_keys($results));
|
||||
|
||||
// Check the extra where clause, either with the 'u.' prefix or not.
|
||||
$results = get_users_listing('lastaccess', 'asc', 0, 0, '', '', '', 'id IN (:x,:y)',
|
||||
['x' => $userids[1], 'y' => $userids[3]]);
|
||||
$results = array_diff_key($results, $existingids);
|
||||
$this->assertEquals([$userids[1], $userids[3]], array_keys($results));
|
||||
$results = get_users_listing('lastaccess', 'asc', 0, 0, '', '', '', 'u.id IN (:x,:y)',
|
||||
['x' => $userids[1], 'y' => $userids[3]]);
|
||||
$results = array_diff_key($results, $existingids);
|
||||
$this->assertEquals([$userids[1], $userids[3]], array_keys($results));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1505,6 +1505,8 @@ class core_moodlelib_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test essential features implementation of {@link get_extra_user_fields()} as the admin user with all capabilities.
|
||||
*
|
||||
* @deprecated since Moodle 3.11 MDL-45242
|
||||
*/
|
||||
public function test_get_extra_user_fields_essentials() {
|
||||
global $CFG, $USER, $DB;
|
||||
@@ -1536,12 +1538,15 @@ class core_moodlelib_testcase extends advanced_testcase {
|
||||
// Two fields.
|
||||
$CFG->showuseridentity = 'frog,zombie';
|
||||
$this->assertEquals(array('zombie'), get_extra_user_fields($context, array('frog')));
|
||||
|
||||
$this->assertDebuggingCalledCount(6);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare environment for couple of tests related to permission checks in {@link get_extra_user_fields()}.
|
||||
*
|
||||
* @return stdClass
|
||||
* @deprecated since Moodle 3.11 MDL-45242
|
||||
*/
|
||||
protected function environment_for_get_extra_user_fields_tests() {
|
||||
global $CFG, $DB;
|
||||
@@ -1571,6 +1576,8 @@ class core_moodlelib_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* No identity fields shown to student user (no permission to view identity fields).
|
||||
*
|
||||
* @deprecated since Moodle 3.11 MDL-45242
|
||||
*/
|
||||
public function test_get_extra_user_fields_no_access() {
|
||||
|
||||
@@ -1580,10 +1587,14 @@ class core_moodlelib_testcase extends advanced_testcase {
|
||||
|
||||
$this->assertEquals(array(), get_extra_user_fields($env->coursecontext));
|
||||
$this->assertEquals(array(), get_extra_user_fields(context_system::instance()));
|
||||
|
||||
$this->assertDebuggingCalledCount(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Teacher can see students' identity fields only within the course.
|
||||
*
|
||||
* @deprecated since Moodle 3.11 MDL-45242
|
||||
*/
|
||||
public function test_get_extra_user_fields_course_only_access() {
|
||||
|
||||
@@ -1593,10 +1604,14 @@ class core_moodlelib_testcase extends advanced_testcase {
|
||||
|
||||
$this->assertEquals(array('idnumber', 'country', 'city'), get_extra_user_fields($env->coursecontext));
|
||||
$this->assertEquals(array(), get_extra_user_fields(context_system::instance()));
|
||||
|
||||
$this->assertDebuggingCalledCount(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Teacher can be prevented from seeing students' identity fields even within the course.
|
||||
*
|
||||
* @deprecated since Moodle 3.11 MDL-45242
|
||||
*/
|
||||
public function test_get_extra_user_fields_course_prevented_access() {
|
||||
|
||||
@@ -1606,10 +1621,14 @@ class core_moodlelib_testcase extends advanced_testcase {
|
||||
|
||||
assign_capability('moodle/course:viewhiddenuserfields', CAP_PREVENT, $env->teacherrole->id, $env->coursecontext->id);
|
||||
$this->assertEquals(array('idnumber'), get_extra_user_fields($env->coursecontext));
|
||||
|
||||
$this->assertDebuggingCalledCount(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manager can see students' identity fields anywhere.
|
||||
*
|
||||
* @deprecated since Moodle 3.11 MDL-45242
|
||||
*/
|
||||
public function test_get_extra_user_fields_anywhere_access() {
|
||||
|
||||
@@ -1619,10 +1638,14 @@ class core_moodlelib_testcase extends advanced_testcase {
|
||||
|
||||
$this->assertEquals(array('idnumber', 'country', 'city'), get_extra_user_fields($env->coursecontext));
|
||||
$this->assertEquals(array('idnumber', 'country', 'city'), get_extra_user_fields(context_system::instance()));
|
||||
|
||||
$this->assertDebuggingCalledCount(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Manager can be prevented from seeing hidden fields outside the course.
|
||||
*
|
||||
* @deprecated since Moodle 3.11 MDL-45242
|
||||
*/
|
||||
public function test_get_extra_user_fields_schismatic_access() {
|
||||
|
||||
@@ -1635,10 +1658,14 @@ class core_moodlelib_testcase extends advanced_testcase {
|
||||
// Note that inside the course, the manager can still see the hidden identifiers as this is currently
|
||||
// controlled by a separate capability for legacy reasons.
|
||||
$this->assertEquals(array('idnumber', 'country', 'city'), get_extra_user_fields($env->coursecontext));
|
||||
|
||||
$this->assertDebuggingCalledCount(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Two capabilities must be currently set to prevent manager from seeing hidden fields.
|
||||
*
|
||||
* @deprecated since Moodle 3.11 MDL-45242
|
||||
*/
|
||||
public function test_get_extra_user_fields_hard_to_prevent_access() {
|
||||
|
||||
@@ -1651,8 +1678,15 @@ class core_moodlelib_testcase extends advanced_testcase {
|
||||
|
||||
$this->assertEquals(array('idnumber'), get_extra_user_fields(context_system::instance()));
|
||||
$this->assertEquals(array('idnumber'), get_extra_user_fields($env->coursecontext));
|
||||
|
||||
$this->assertDebuggingCalledCount(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests get_extra_user_fields_sql.
|
||||
*
|
||||
* @deprecated since Moodle 3.11 MDL-45242
|
||||
*/
|
||||
public function test_get_extra_user_fields_sql() {
|
||||
global $CFG, $USER, $DB;
|
||||
$this->resetAfterTest();
|
||||
@@ -1686,6 +1720,8 @@ class core_moodlelib_testcase extends advanced_testcase {
|
||||
$CFG->showuseridentity = 'frog,zombie';
|
||||
$this->assertEquals(', u1.zombie AS u_zombie',
|
||||
get_extra_user_fields_sql($context, 'u1', 'u_', array('frog')));
|
||||
|
||||
$this->assertDebuggingCalledCount(6);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -3037,6 +3073,11 @@ class core_moodlelib_testcase extends advanced_testcase {
|
||||
$CFG->alternativefullnameformat = $originalcfg->alternativefullnameformat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the get_all_user_name_fields() deprecated function.
|
||||
*
|
||||
* @deprecated since Moodle 3.11 MDL-45242
|
||||
*/
|
||||
public function test_get_all_user_name_fields() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
@@ -3084,6 +3125,8 @@ class core_moodlelib_testcase extends advanced_testcase {
|
||||
// Returning a string.
|
||||
$teststring = 'firstname,lastname,firstnamephonetic,lastnamephonetic,middlename,alternatename';
|
||||
$this->assertEquals($teststring, get_all_user_name_fields(true, null, null, null, true));
|
||||
|
||||
$this->assertDebuggingCalledCount(7);
|
||||
}
|
||||
|
||||
public function test_order_in_string() {
|
||||
@@ -3745,7 +3788,7 @@ class core_moodlelib_testcase extends advanced_testcase {
|
||||
|
||||
// User information for showing a picture.
|
||||
$user = new stdClass();
|
||||
$additionalfields = explode(',', user_picture::fields());
|
||||
$additionalfields = explode(',', implode(',', \core\user_fields::get_picture_fields()));
|
||||
$user = username_load_fields_from_object($user, $userinfo, null, $additionalfields);
|
||||
$user->id = $userinfo->userid;
|
||||
$expectedarray = new stdClass();
|
||||
@@ -3774,7 +3817,7 @@ class core_moodlelib_testcase extends advanced_testcase {
|
||||
|
||||
// Return an object with user picture information.
|
||||
$user = new stdClass();
|
||||
$additionalfields = explode(',', user_picture::fields());
|
||||
$additionalfields = explode(',', implode(',', \core\user_fields::get_picture_fields()));
|
||||
$user = username_load_fields_from_object($user, $userinfo, 'author', $additionalfields);
|
||||
$user->id = $userinfo->userid;
|
||||
$expectedarray = new stdClass();
|
||||
|
||||
@@ -33,6 +33,11 @@ require_once($CFG->libdir . '/outputcomponents.php');
|
||||
*/
|
||||
class core_outputcomponents_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Tests user_picture::fields.
|
||||
*
|
||||
* @deprecated since Moodle 3.11 MDL-45242
|
||||
*/
|
||||
public function test_fields_aliasing() {
|
||||
$fields = user_picture::fields();
|
||||
$fields = array_map('trim', explode(',', $fields));
|
||||
@@ -60,10 +65,16 @@ class core_outputcomponents_testcase extends advanced_testcase {
|
||||
$this->assertContains($expected, $returned, "Expected pattern '$expected' not returned");
|
||||
}
|
||||
$this->assertContains("custom1 AS prefixcustom1", $returned, "Expected pattern 'custom1 AS prefixcustom1' not returned");
|
||||
|
||||
// Deprecation warnings for user_picture::fields.
|
||||
$this->assertDebuggingCalledCount(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests user_picture::unalias.
|
||||
*/
|
||||
public function test_fields_unaliasing() {
|
||||
$fields = user_picture::fields();
|
||||
$fields = implode(',', \core\user_fields::get_picture_fields());
|
||||
$fields = array_map('trim', explode(',', $fields));
|
||||
|
||||
$fakerecord = new stdClass();
|
||||
@@ -86,8 +97,11 @@ class core_outputcomponents_testcase extends advanced_testcase {
|
||||
$this->assertSame('Value of custom1', $returned->custom1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests user_picture::unalias with null values.
|
||||
*/
|
||||
public function test_fields_unaliasing_null() {
|
||||
$fields = user_picture::fields();
|
||||
$fields = implode(',', \core\user_fields::get_picture_fields());
|
||||
$fields = array_map('trim', explode(',', $fields));
|
||||
|
||||
$fakerecord = new stdClass();
|
||||
|
||||
@@ -0,0 +1,511 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
namespace core;
|
||||
|
||||
/**
|
||||
* Unit tests for \core\user_fields
|
||||
*
|
||||
* @package core
|
||||
* @copyright 2014 The Open University
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class user_fields_testcase extends \advanced_testcase {
|
||||
|
||||
/**
|
||||
* Tests getting the user picture fields.
|
||||
*/
|
||||
public function test_get_picture_fields() {
|
||||
$this->assertEquals(['id', 'picture', 'firstname', 'lastname', 'firstnamephonetic',
|
||||
'lastnamephonetic', 'middlename', 'alternatename', 'imagealt', 'email'],
|
||||
user_fields::get_picture_fields());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests getting the user name fields.
|
||||
*/
|
||||
public function test_get_name_fields() {
|
||||
$this->assertEquals(['firstnamephonetic', 'lastnamephonetic', 'middlename', 'alternatename',
|
||||
'firstname', 'lastname'],
|
||||
user_fields::get_name_fields());
|
||||
|
||||
$this->assertEquals(['firstname', 'lastname',
|
||||
'firstnamephonetic', 'lastnamephonetic', 'middlename', 'alternatename'],
|
||||
user_fields::get_name_fields(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests getting the identity fields.
|
||||
*/
|
||||
public function test_get_identity_fields() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Create two custom profile fields, one of which is private.
|
||||
$generator = self::getDataGenerator();
|
||||
$generator->create_custom_profile_field(['datatype' => 'text', 'shortname' => 'a', 'name' => 'A']);
|
||||
$generator->create_custom_profile_field(['datatype' => 'text', 'shortname' => 'b', 'name' => 'B',
|
||||
'visible' => PROFILE_VISIBLE_PRIVATE]);
|
||||
|
||||
// Set the extra user fields to include email, department, and both custom profile fields.
|
||||
set_config('showuseridentity', 'email,department,profile_field_a,profile_field_b');
|
||||
set_config('hiddenuserfields', 'email');
|
||||
|
||||
// Create a test course and a student in the course.
|
||||
$course = $generator->create_course();
|
||||
$coursecontext = \context_course::instance($course->id);
|
||||
$user = $generator->create_user();
|
||||
$anotheruser = $generator->create_user();
|
||||
$usercontext = \context_user::instance($anotheruser->id);
|
||||
$generator->enrol_user($user->id, $course->id, 'student');
|
||||
|
||||
// When no context is provided, it does no access checks and should return all specified.
|
||||
$this->assertEquals(['email', 'department', 'profile_field_a', 'profile_field_b'],
|
||||
user_fields::get_identity_fields(null));
|
||||
|
||||
// If you turn off custom profile fields, you don't get those.
|
||||
$this->assertEquals(['email', 'department'], user_fields::get_identity_fields(null, false));
|
||||
|
||||
// Request in context as an administator.
|
||||
$this->setAdminUser();
|
||||
$this->assertEquals(['email', 'department', 'profile_field_a', 'profile_field_b'],
|
||||
user_fields::get_identity_fields($coursecontext));
|
||||
$this->assertEquals(['email', 'department'],
|
||||
user_fields::get_identity_fields($coursecontext, false));
|
||||
|
||||
// Request in context as a student - they don't have any of the capabilities to see identity
|
||||
// fields or profile fields.
|
||||
$this->setUser($user);
|
||||
$this->assertEquals([], user_fields::get_identity_fields($coursecontext));
|
||||
|
||||
// Give the student the basic identity fields permission.
|
||||
$roleid = $DB->get_field('role', 'id', ['shortname' => 'student']);
|
||||
role_change_permission($roleid, $coursecontext, 'moodle/site:viewuseridentity', CAP_ALLOW);
|
||||
$this->assertEquals(['department', 'profile_field_a'],
|
||||
user_fields::get_identity_fields($coursecontext));
|
||||
$this->assertEquals(['department'],
|
||||
user_fields::get_identity_fields($coursecontext, false));
|
||||
|
||||
// Give them permission to view hidden user fields.
|
||||
role_change_permission($roleid, $coursecontext, 'moodle/course:viewhiddenuserfields', CAP_ALLOW);
|
||||
$this->assertEquals(['email', 'department', 'profile_field_a'],
|
||||
user_fields::get_identity_fields($coursecontext));
|
||||
$this->assertEquals(['email', 'department'],
|
||||
user_fields::get_identity_fields($coursecontext, false));
|
||||
|
||||
// Also give them permission to view all profile fields.
|
||||
role_change_permission($roleid, $coursecontext, 'moodle/user:viewalldetails', CAP_ALLOW);
|
||||
$this->assertEquals(['email', 'department', 'profile_field_a', 'profile_field_b'],
|
||||
user_fields::get_identity_fields($coursecontext));
|
||||
$this->assertEquals(['email', 'department'],
|
||||
user_fields::get_identity_fields($coursecontext, false));
|
||||
|
||||
// Even if we give them student role in the user context they can't view anything...
|
||||
$generator->role_assign($roleid, $user->id, $usercontext->id);
|
||||
$this->assertEquals([], user_fields::get_identity_fields($usercontext));
|
||||
|
||||
// Give them basic permission.
|
||||
role_change_permission($roleid, $usercontext, 'moodle/site:viewuseridentity', CAP_ALLOW);
|
||||
$this->assertEquals(['department', 'profile_field_a'],
|
||||
user_fields::get_identity_fields($usercontext));
|
||||
$this->assertEquals(['department'],
|
||||
user_fields::get_identity_fields($usercontext, false));
|
||||
|
||||
// Give them the hidden user fields permission (it's a different one).
|
||||
role_change_permission($roleid, $usercontext, 'moodle/user:viewhiddendetails', CAP_ALLOW);
|
||||
$this->assertEquals(['email', 'department', 'profile_field_a'],
|
||||
user_fields::get_identity_fields($usercontext));
|
||||
$this->assertEquals(['email', 'department'],
|
||||
user_fields::get_identity_fields($usercontext, false));
|
||||
|
||||
// Also give them permission to view all profile fields.
|
||||
role_change_permission($roleid, $usercontext, 'moodle/user:viewalldetails', CAP_ALLOW);
|
||||
$this->assertEquals(['email', 'department', 'profile_field_a', 'profile_field_b'],
|
||||
user_fields::get_identity_fields($usercontext));
|
||||
$this->assertEquals(['email', 'department'],
|
||||
user_fields::get_identity_fields($usercontext, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the get_required_fields function.
|
||||
*
|
||||
* This function composes the results of get_identity/name/picture_fields, so we are not going
|
||||
* to test the details of the identity permissions as that was already covered. Just how they
|
||||
* are included/combined.
|
||||
*/
|
||||
public function test_get_required_fields() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Set up some profile fields.
|
||||
$generator = self::getDataGenerator();
|
||||
$generator->create_custom_profile_field(['datatype' => 'text', 'shortname' => 'a', 'name' => 'A']);
|
||||
$generator->create_custom_profile_field(['datatype' => 'text', 'shortname' => 'b', 'name' => 'B']);
|
||||
set_config('showuseridentity', 'email,department,profile_field_a');
|
||||
|
||||
// What happens if you don't ask for anything?
|
||||
$fields = user_fields::empty();
|
||||
$this->assertEquals([], $fields->get_required_fields());
|
||||
|
||||
// Try each invidual purpose.
|
||||
$fields = user_fields::for_identity(null);
|
||||
$this->assertEquals(['email', 'department', 'profile_field_a'], $fields->get_required_fields());
|
||||
$fields = user_fields::for_userpic();
|
||||
$this->assertEquals(user_fields::get_picture_fields(), $fields->get_required_fields());
|
||||
$fields = user_fields::for_name();
|
||||
$this->assertEquals(user_fields::get_name_fields(), $fields->get_required_fields());
|
||||
|
||||
// Try combining them all. There should be no duplicates (e.g. email), and the 'id' field
|
||||
// should be moved to the start.
|
||||
$fields = user_fields::for_identity(null)->with_name()->with_userpic();
|
||||
$this->assertEquals(['id', 'email', 'department', 'profile_field_a', 'picture',
|
||||
'firstname', 'lastname', 'firstnamephonetic', 'lastnamephonetic', 'middlename',
|
||||
'alternatename', 'imagealt'], $fields->get_required_fields());
|
||||
|
||||
// Add some specified fields to a default result.
|
||||
$fields = user_fields::for_identity(null, true)->including('city', 'profile_field_b');
|
||||
$this->assertEquals(['email', 'department', 'profile_field_a', 'city', 'profile_field_b'],
|
||||
$fields->get_required_fields());
|
||||
|
||||
// Remove some fields, one of which actually is in the list.
|
||||
$fields = user_fields::for_identity(null, true)->excluding('email', 'city');
|
||||
$this->assertEquals(['department', 'profile_field_a'], $fields->get_required_fields());
|
||||
|
||||
// Add and remove fields.
|
||||
$fields = user_fields::for_identity(null, true)->including('city', 'profile_field_b')->excluding('city', 'department');
|
||||
$this->assertEquals(['email', 'profile_field_a', 'profile_field_b'],
|
||||
$fields->get_required_fields());
|
||||
|
||||
// Request the list without profile fields, check that still works with both sources.
|
||||
$fields = user_fields::for_identity(null, false)->including('city', 'profile_field_b')->excluding('city', 'department');
|
||||
$this->assertEquals(['email'], $fields->get_required_fields());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the get_required_fields function when you use the $limitpurposes parameter.
|
||||
*/
|
||||
public function test_get_required_fields_limitpurposes() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Set up some profile fields.
|
||||
$generator = self::getDataGenerator();
|
||||
$generator->create_custom_profile_field(['datatype' => 'text', 'shortname' => 'a', 'name' => 'A']);
|
||||
$generator->create_custom_profile_field(['datatype' => 'text', 'shortname' => 'b', 'name' => 'B']);
|
||||
set_config('showuseridentity', 'email,department,profile_field_a');
|
||||
|
||||
// Create a user_fields object with all three purposes, plus included and excluded fields.
|
||||
$fields = user_fields::for_identity(null, true)->with_name()->with_userpic()
|
||||
->including('city', 'profile_field_b')->excluding('firstnamephonetic', 'middlename', 'alternatename');
|
||||
|
||||
// Check the result with all purposes.
|
||||
$this->assertEquals(['id', 'email', 'department', 'profile_field_a', 'picture',
|
||||
'firstname', 'lastname', 'lastnamephonetic', 'imagealt', 'city',
|
||||
'profile_field_b'],
|
||||
$fields->get_required_fields([user_fields::PURPOSE_IDENTITY, user_fields::PURPOSE_NAME,
|
||||
user_fields::PURPOSE_USERPIC, user_fields::CUSTOM_INCLUDE]));
|
||||
|
||||
// Limit to identity and custom includes.
|
||||
$this->assertEquals(['email', 'department', 'profile_field_a', 'city', 'profile_field_b'],
|
||||
$fields->get_required_fields([user_fields::PURPOSE_IDENTITY, user_fields::CUSTOM_INCLUDE]));
|
||||
|
||||
// Limit to name fields.
|
||||
$this->assertEquals(['firstname', 'lastname', 'lastnamephonetic'],
|
||||
$fields->get_required_fields([user_fields::PURPOSE_NAME]));
|
||||
}
|
||||
|
||||
/**
|
||||
* There should be an exception if you try to 'limit' purposes to one that wasn't even included.
|
||||
*/
|
||||
public function test_get_required_fields_limitpurposes_not_in_constructor() {
|
||||
$fields = user_fields::for_identity(null);
|
||||
$this->expectExceptionMessage('$limitpurposes can only include purposes defined in object');
|
||||
$fields->get_required_fields([user_fields::PURPOSE_USERPIC]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up data and a user_fields object for all the get_sql tests.
|
||||
*
|
||||
* @return user_fields Constructed user_fields for testing
|
||||
*/
|
||||
protected function init_for_sql_tests(): user_fields {
|
||||
$generator = self::getDataGenerator();
|
||||
$generator->create_custom_profile_field(['datatype' => 'text', 'shortname' => 'a', 'name' => 'A']);
|
||||
$generator->create_custom_profile_field(['datatype' => 'text', 'shortname' => 'b', 'name' => 'B']);
|
||||
|
||||
// Create a couple of users. One doesn't have a profile field set, so we can test that.
|
||||
$generator->create_user(['profile_field_a' => 'A1', 'profile_field_b' => 'B1',
|
||||
'city' => 'C1', 'department' => 'D1', 'email' => '[email protected]',
|
||||
'idnumber' => 'XXX1', 'username' => 'u1']);
|
||||
$generator->create_user(['profile_field_a' => 'A2',
|
||||
'city' => 'C2', 'department' => 'D2', 'email' => '[email protected]',
|
||||
'idnumber' => 'XXX2', 'username' => 'u2']);
|
||||
|
||||
// It doesn't matter how we construct it (we already tested get_required_fields which is
|
||||
// where all those values are actually used) so let's just list the fields we want manually.
|
||||
return user_fields::empty()->including('department', 'city', 'profile_field_a', 'profile_field_b');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests getting SQL (and actually using it).
|
||||
*/
|
||||
public function test_get_sql_variations() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$fields = $this->init_for_sql_tests();
|
||||
user_fields::reset_unique_identifier();
|
||||
|
||||
// Basic SQL.
|
||||
['selects' => $selects, 'joins' => $joins, 'params' => $joinparams, 'mappings' => $mappings] =
|
||||
(array)$fields->get_sql();
|
||||
$sql = "SELECT idnumber
|
||||
$selects
|
||||
FROM {user}
|
||||
$joins
|
||||
WHERE idnumber LIKE ?
|
||||
ORDER BY idnumber";
|
||||
$records = $DB->get_records_sql($sql, array_merge($joinparams, ['X%']));
|
||||
$this->assertCount(2, $records);
|
||||
$expected1 = (object)['profile_field_a' => 'A1', 'profile_field_b' => 'B1',
|
||||
'city' => 'C1', 'department' => 'D1', 'idnumber' => 'XXX1'];
|
||||
$expected2 = (object)['profile_field_a' => 'A2', 'profile_field_b' => null,
|
||||
'city' => 'C2', 'department' => 'D2', 'idnumber' => 'XXX2'];
|
||||
$this->assertEquals($expected1, $records['XXX1']);
|
||||
$this->assertEquals($expected2, $records['XXX2']);
|
||||
|
||||
$this->assertEquals([
|
||||
'department' => '{user}.department',
|
||||
'city' => '{user}.city',
|
||||
'profile_field_a' => $DB->sql_compare_text('uf1d_1.data', 255),
|
||||
'profile_field_b' => $DB->sql_compare_text('uf1d_2.data', 255)], $mappings);
|
||||
|
||||
// SQL using named params.
|
||||
['selects' => $selects, 'joins' => $joins, 'params' => $joinparams] =
|
||||
(array)$fields->get_sql('', true);
|
||||
$sql = "SELECT idnumber
|
||||
$selects
|
||||
FROM {user}
|
||||
$joins
|
||||
WHERE idnumber LIKE :idnum
|
||||
ORDER BY idnumber";
|
||||
$records = $DB->get_records_sql($sql, array_merge($joinparams, ['idnum' => 'X%']));
|
||||
$this->assertCount(2, $records);
|
||||
$this->assertEquals($expected1, $records['XXX1']);
|
||||
$this->assertEquals($expected2, $records['XXX2']);
|
||||
|
||||
// SQL using alias for user table.
|
||||
['selects' => $selects, 'joins' => $joins, 'params' => $joinparams, 'mappings' => $mappings] =
|
||||
(array)$fields->get_sql('u');
|
||||
$sql = "SELECT idnumber
|
||||
$selects
|
||||
FROM {user} u
|
||||
$joins
|
||||
WHERE idnumber LIKE ?
|
||||
ORDER BY idnumber";
|
||||
$records = $DB->get_records_sql($sql, array_merge($joinparams, ['X%']));
|
||||
$this->assertCount(2, $records);
|
||||
$this->assertEquals($expected1, $records['XXX1']);
|
||||
$this->assertEquals($expected2, $records['XXX2']);
|
||||
|
||||
$this->assertEquals([
|
||||
'department' => 'u.department',
|
||||
'city' => 'u.city',
|
||||
'profile_field_a' => $DB->sql_compare_text('uf3d_1.data', 255),
|
||||
'profile_field_b' => $DB->sql_compare_text('uf3d_2.data', 255)], $mappings);
|
||||
|
||||
// Returning prefixed fields.
|
||||
['selects' => $selects, 'joins' => $joins, 'params' => $joinparams] =
|
||||
(array)$fields->get_sql('', false, 'u_');
|
||||
$sql = "SELECT idnumber
|
||||
$selects
|
||||
FROM {user}
|
||||
$joins
|
||||
WHERE idnumber LIKE ?
|
||||
ORDER BY idnumber";
|
||||
$records = $DB->get_records_sql($sql, array_merge($joinparams, ['X%']));
|
||||
$this->assertCount(2, $records);
|
||||
$expected1 = (object)['u_profile_field_a' => 'A1', 'u_profile_field_b' => 'B1',
|
||||
'u_city' => 'C1', 'u_department' => 'D1', 'idnumber' => 'XXX1'];
|
||||
$this->assertEquals($expected1, $records['XXX1']);
|
||||
|
||||
// Renaming the id field. We need to use a different set of fields so it actually has the
|
||||
// id field.
|
||||
$fields = user_fields::for_userpic();
|
||||
['selects' => $selects, 'joins' => $joins, 'params' => $joinparams] =
|
||||
(array)$fields->get_sql('', false, '', 'userid');
|
||||
$sql = "SELECT idnumber
|
||||
$selects
|
||||
FROM {user}
|
||||
$joins
|
||||
WHERE idnumber LIKE ?
|
||||
ORDER BY idnumber";
|
||||
$records = $DB->get_records_sql($sql, array_merge($joinparams, ['X%']));
|
||||
$this->assertCount(2, $records);
|
||||
|
||||
// User id was renamed.
|
||||
$this->assertObjectNotHasAttribute('id', $records['XXX1']);
|
||||
$this->assertObjectHasAttribute('userid', $records['XXX1']);
|
||||
|
||||
// Other fields are normal (just try a couple).
|
||||
$this->assertObjectHasAttribute('firstname', $records['XXX1']);
|
||||
$this->assertObjectHasAttribute('imagealt', $records['XXX1']);
|
||||
|
||||
// Check the user id is actually right.
|
||||
$this->assertEquals('XXX1',
|
||||
$DB->get_field('user', 'idnumber', ['id' => $records['XXX1']->userid]));
|
||||
|
||||
// Rename the id field and also use a prefix.
|
||||
['selects' => $selects, 'joins' => $joins, 'params' => $joinparams] =
|
||||
(array)$fields->get_sql('', false, 'u_', 'userid');
|
||||
$sql = "SELECT idnumber
|
||||
$selects
|
||||
FROM {user}
|
||||
$joins
|
||||
WHERE idnumber LIKE ?
|
||||
ORDER BY idnumber";
|
||||
$records = $DB->get_records_sql($sql, array_merge($joinparams, ['X%']));
|
||||
$this->assertCount(2, $records);
|
||||
|
||||
// User id was renamed.
|
||||
$this->assertObjectNotHasAttribute('id', $records['XXX1']);
|
||||
$this->assertObjectNotHasAttribute('u_id', $records['XXX1']);
|
||||
$this->assertObjectHasAttribute('userid', $records['XXX1']);
|
||||
|
||||
// Other fields are prefixed (just try a couple).
|
||||
$this->assertObjectHasAttribute('u_firstname', $records['XXX1']);
|
||||
$this->assertObjectHasAttribute('u_imagealt', $records['XXX1']);
|
||||
|
||||
// Without a leading comma.
|
||||
['selects' => $selects, 'joins' => $joins, 'params' => $joinparams] =
|
||||
(array)$fields->get_sql('', false, '', '', false);
|
||||
$sql = "SELECT $selects
|
||||
FROM {user}
|
||||
$joins
|
||||
WHERE idnumber LIKE ?
|
||||
ORDER BY idnumber";
|
||||
$records = $DB->get_records_sql($sql, array_merge($joinparams, ['X%']));
|
||||
$this->assertCount(2, $records);
|
||||
foreach ($records as $key => $record) {
|
||||
// ID should be the first field used by get_records_sql.
|
||||
$this->assertEquals($key, $record->id);
|
||||
// Check 2 other sample properties.
|
||||
$this->assertObjectHasAttribute('firstname', $record);
|
||||
$this->assertObjectHasAttribute('imagealt', $record);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests what happens if you use the SQL multiple times in a query (i.e. that it correctly
|
||||
* creates the different identifiers).
|
||||
*/
|
||||
public function test_get_sql_multiple() {
|
||||
global $DB;
|
||||
$this->resetAfterTest();
|
||||
|
||||
$fields = $this->init_for_sql_tests();
|
||||
|
||||
// Inner SQL.
|
||||
['selects' => $selects1, 'joins' => $joins1, 'params' => $joinparams1] =
|
||||
(array)$fields->get_sql('u1', true);
|
||||
// Outer SQL.
|
||||
$fields2 = user_fields::empty()->including('profile_field_a', 'email');
|
||||
['selects' => $selects2, 'joins' => $joins2, 'params' => $joinparams2] =
|
||||
(array)$fields2->get_sql('u2', true);
|
||||
|
||||
// Crazy combined query.
|
||||
$sql = "SELECT username, details.profile_field_b AS innerb, details.city AS innerc
|
||||
$selects2
|
||||
FROM {user} u2
|
||||
$joins2
|
||||
LEFT JOIN (
|
||||
SELECT u1.id
|
||||
$selects1
|
||||
FROM {user} u1
|
||||
$joins1
|
||||
WHERE idnumber LIKE :idnum
|
||||
) details ON details.id = u2.id
|
||||
ORDER BY username";
|
||||
$records = $DB->get_records_sql($sql, array_merge($joinparams1, $joinparams2, ['idnum' => 'X%']));
|
||||
// The left join won't match for admin.
|
||||
$this->assertNull($records['admin']->innerb);
|
||||
$this->assertNull($records['admin']->innerc);
|
||||
// It should match for one of the test users though.
|
||||
$expected1 = (object)['username' => 'u1', 'innerb' => 'B1', 'innerc' => 'C1',
|
||||
'profile_field_a' => 'A1', 'email' => '[email protected]'];
|
||||
$this->assertEquals($expected1, $records['u1']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the get_sql function when there are no fields to retrieve.
|
||||
*/
|
||||
public function test_get_sql_nothing() {
|
||||
$fields = user_fields::empty();
|
||||
['selects' => $selects, 'joins' => $joins, 'params' => $joinparams] = (array)$fields->get_sql();
|
||||
$this->assertEquals('', $selects);
|
||||
$this->assertEquals('', $joins);
|
||||
$this->assertEquals([], $joinparams);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests get_sql when there are no custom fields; in this scenario, the joins and joinparams
|
||||
* are always blank.
|
||||
*/
|
||||
public function test_get_sql_no_custom_fields() {
|
||||
$fields = user_fields::empty()->including('city', 'country');
|
||||
['selects' => $selects, 'joins' => $joins, 'params' => $joinparams, 'mappings' => $mappings] =
|
||||
(array)$fields->get_sql('u');
|
||||
$this->assertEquals(', u.city, u.country', $selects);
|
||||
$this->assertEquals('', $joins);
|
||||
$this->assertEquals([], $joinparams);
|
||||
$this->assertEquals(['city' => 'u.city', 'country' => 'u.country'], $mappings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the format of the $selects string, which is important particularly for backward
|
||||
* compatibility.
|
||||
*/
|
||||
public function test_get_sql_selects_format() {
|
||||
global $DB;
|
||||
|
||||
$this->resetAfterTest();
|
||||
user_fields::reset_unique_identifier();
|
||||
|
||||
$generator = self::getDataGenerator();
|
||||
$generator->create_custom_profile_field(['datatype' => 'text', 'shortname' => 'a', 'name' => 'A']);
|
||||
|
||||
// When we list fields that include custom profile fields...
|
||||
$fields = user_fields::empty()->including('id', 'profile_field_a');
|
||||
|
||||
// Supplying an alias: all fields have alias.
|
||||
$selects = $fields->get_sql('u')->selects;
|
||||
$this->assertEquals(', u.id, ' . $DB->sql_compare_text('uf1d_1.data', 255) . ' AS profile_field_a', $selects);
|
||||
|
||||
// No alias: all files have {user} because of the joins.
|
||||
$selects = $fields->get_sql()->selects;
|
||||
$this->assertEquals(', {user}.id, ' . $DB->sql_compare_text('uf2d_1.data', 255) . ' AS profile_field_a', $selects);
|
||||
|
||||
// When the list doesn't include custom profile fields...
|
||||
$fields = user_fields::empty()->including('id', 'city');
|
||||
|
||||
// Supplying an alias: all fields have alias.
|
||||
$selects = $fields->get_sql('u')->selects;
|
||||
$this->assertEquals(', u.id, u.city', $selects);
|
||||
|
||||
// No alias: fields do not have alias at all.
|
||||
$selects = $fields->get_sql()->selects;
|
||||
$this->assertEquals(', id, city', $selects);
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,12 @@ information provided here is intended especially for developers.
|
||||
- get_custom_rule_descriptions(): Returns an associative array with values containing the user-facing textual description
|
||||
of the custom completion rules (which serve as the keys to these values).
|
||||
e.g. ['completionsubmit' => 'Must submit']
|
||||
* Admin setting admin_setting_configmulticheckbox now supports lazy-loading the options list by
|
||||
supplying a callback function instead of an array of options.
|
||||
* A new core API class \core\user_fields provides ways to get lists of user fields, and SQL related to
|
||||
those fields. This replaces existing functions get_extra_user_fields(), get_extra_user_fields_sql(),
|
||||
get_user_field_name(), get_all_user_name_fields(), and user_picture::fields(), which have all been
|
||||
deprecated.
|
||||
|
||||
=== 3.10 ===
|
||||
* PHPUnit has been upgraded to 8.5. That comes with a few changes:
|
||||
|
||||
@@ -111,8 +111,9 @@ class api {
|
||||
global $DB;
|
||||
|
||||
// Get the user fields we want.
|
||||
$ufields = \user_picture::fields('u', array('lastaccess'), 'userfrom_id', 'userfrom_');
|
||||
$ufields2 = \user_picture::fields('u2', array('lastaccess'), 'userto_id', 'userto_');
|
||||
$userfieldsapi = \core\user_fields::for_userpic()->including('lastaccess');
|
||||
$ufields = $userfieldsapi->get_sql('u', false, 'userfrom_', '', false)->selects;
|
||||
$ufields2 = $userfieldsapi->get_sql('u2', false, 'userto_', '', false)->selects;
|
||||
// Add the uniqueid column to make each row unique and avoid SQL errors.
|
||||
$uniqueidsql = $DB->sql_concat('m.id', "'_'", 'm.useridfrom', "'_'", 'mcm.userid');
|
||||
|
||||
@@ -1022,7 +1023,8 @@ class api {
|
||||
debugging('\core_message\api::get_contacts_with_unread_message_count is deprecated and no longer used',
|
||||
DEBUG_DEVELOPER);
|
||||
|
||||
$userfields = \user_picture::fields('u', array('lastaccess'));
|
||||
$userfieldsapi = \core\user_fields::for_userpic()->including('lastaccess');
|
||||
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$unreadcountssql = "SELECT $userfields, count(m.id) as messagecount
|
||||
FROM {message_contacts} mc
|
||||
INNER JOIN {user} u
|
||||
@@ -1063,7 +1065,8 @@ class api {
|
||||
debugging('\core_message\api::get_non_contacts_with_unread_message_count is deprecated and no longer used',
|
||||
DEBUG_DEVELOPER);
|
||||
|
||||
$userfields = \user_picture::fields('u', array('lastaccess'));
|
||||
$userfieldsapi = \core\user_fields::for_userpic()->including('lastaccess');
|
||||
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$unreadcountssql = "SELECT $userfields, count(m.id) as messagecount
|
||||
FROM {user} u
|
||||
INNER JOIN {messages} m
|
||||
@@ -1885,7 +1888,8 @@ class api {
|
||||
public static function get_blocked_users($userid) {
|
||||
global $DB;
|
||||
|
||||
$userfields = \user_picture::fields('u', array('lastaccess'));
|
||||
$userfieldsapi = \core\user_fields::for_userpic()->including('lastaccess');
|
||||
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$blockeduserssql = "SELECT $userfields
|
||||
FROM {message_users_blocked} mub
|
||||
INNER JOIN {user} u
|
||||
|
||||
@@ -429,7 +429,8 @@ class helper {
|
||||
}
|
||||
|
||||
list($useridsql, $usersparams) = $DB->get_in_or_equal($userids);
|
||||
$userfields = \user_picture::fields('u', array('lastaccess'));
|
||||
$userfieldsapi = \core\user_fields::for_userpic()->including('lastaccess');
|
||||
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$userssql = "SELECT $userfields, u.deleted, mc.id AS contactid, mub.id AS blockedid
|
||||
FROM {user} u
|
||||
LEFT JOIN {message_contacts} mc
|
||||
|
||||
+5
-3
@@ -183,7 +183,8 @@ function message_search_users($courseids, $searchtext, $sort='', $exceptions='')
|
||||
}
|
||||
|
||||
$fullname = $DB->sql_fullname();
|
||||
$ufields = user_picture::fields('u');
|
||||
$userfieldsapi = \core\user_fields::for_userpic();
|
||||
$ufields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
|
||||
if (!empty($sort)) {
|
||||
$order = ' ORDER BY '. $sort;
|
||||
@@ -549,11 +550,12 @@ function message_get_messages($useridto, $useridfrom = 0, $notifications = -1, $
|
||||
global $DB;
|
||||
|
||||
// If the 'useridto' value is empty then we are going to retrieve messages sent by the useridfrom to any user.
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
if (empty($useridto)) {
|
||||
$userfields = get_all_user_name_fields(true, 'u', '', 'userto');
|
||||
$userfields = $userfieldsapi->get_sql('u', false, 'userto', '', false)->selects;
|
||||
$messageuseridtosql = 'u.id as useridto';
|
||||
} else {
|
||||
$userfields = get_all_user_name_fields(true, 'u', '', 'userfrom');
|
||||
$userfields = $userfieldsapi->get_sql('u', false, 'userfrom', '', false)->selects;
|
||||
$messageuseridtosql = "$useridto as useridto";
|
||||
}
|
||||
|
||||
|
||||
@@ -161,7 +161,8 @@ class send_email_task extends scheduled_task {
|
||||
protected function get_users_messages_for_conversation(int $conversationid, int $userid) : moodle_recordset {
|
||||
global $DB;
|
||||
|
||||
$usernamefields = \user_picture::fields('u');
|
||||
$userfieldsapi = \core\user_fields::for_userpic();
|
||||
$usernamefields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$sql = "SELECT $usernamefields, m.*
|
||||
FROM {messages} m
|
||||
JOIN {user} u
|
||||
|
||||
@@ -168,7 +168,8 @@ class grading_app implements templatable, renderable {
|
||||
$export->rarrow = $output->rarrow();
|
||||
$export->larrow = $output->larrow();
|
||||
// List of identity fields to display (the user info will not contain any fields the user cannot view anyway).
|
||||
$export->showuseridentity = $CFG->showuseridentity;
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$export->showuseridentity = implode(',', \core\user_fields::get_identity_fields(null, false));
|
||||
$export->currentuserid = $USER->id;
|
||||
$helpicon = new \help_icon('sendstudentnotifications', 'assign');
|
||||
$export->helpicon = $helpicon->export_for_template($output);
|
||||
|
||||
@@ -58,7 +58,8 @@ class mod_assign_extension_form extends moodleform {
|
||||
$usercount = 0;
|
||||
$usershtml = '';
|
||||
|
||||
$extrauserfields = get_extra_user_fields($assign->get_context());
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$extrauserfields = \core\user_fields::get_identity_fields($assign->get_context(), false);
|
||||
foreach ($userlist as $userid) {
|
||||
if ($usercount >= 5) {
|
||||
$usershtml .= get_string('moreusers', 'assign', count($userlist) - 5);
|
||||
|
||||
@@ -474,7 +474,8 @@ class assign_feedback_file extends assign_feedback_plugin {
|
||||
$this->assignment->get_course_context()),
|
||||
$this->assignment->is_blind_marking(),
|
||||
$this->assignment->get_uniqueid_for_user($user->id),
|
||||
get_extra_user_fields($this->assignment->get_context()));
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
\core\user_fields::get_identity_fields($this->assignment->get_context(), false));
|
||||
$usershtml .= $this->assignment->get_renderer()->render($usersummary);
|
||||
$usercount += 1;
|
||||
}
|
||||
|
||||
@@ -134,9 +134,11 @@ class assign_grading_table extends table_sql implements renderable {
|
||||
$params['assignmentid3'] = (int)$this->assignment->get_instance()->id;
|
||||
$params['newstatus'] = ASSIGN_SUBMISSION_STATUS_NEW;
|
||||
|
||||
$extrauserfields = get_extra_user_fields($this->assignment->get_context());
|
||||
|
||||
$fields = user_picture::fields('u', $extrauserfields) . ', ';
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$userfieldsapi = \core\user_fields::for_identity($this->assignment->get_context(), false)->with_userpic();
|
||||
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
$extrauserfields = $userfieldsapi->get_required_fields([\core\user_fields::PURPOSE_IDENTITY]);
|
||||
$fields = $userfields . ', ';
|
||||
$fields .= 'u.id as userid, ';
|
||||
$fields .= 's.status as status, ';
|
||||
$fields .= 's.id as submissionid, ';
|
||||
@@ -406,7 +408,7 @@ class assign_grading_table extends table_sql implements renderable {
|
||||
|
||||
foreach ($extrauserfields as $extrafield) {
|
||||
$columns[] = $extrafield;
|
||||
$headers[] = get_user_field_name($extrafield);
|
||||
$headers[] = \core\user_fields::get_display_name($extrafield);
|
||||
}
|
||||
} else {
|
||||
// Record ID.
|
||||
|
||||
+5
-3
@@ -589,7 +589,8 @@ function assign_print_recent_activity($course, $viewfullnames, $timestart) {
|
||||
// Do not use log table if possible, it may be huge.
|
||||
|
||||
$dbparams = array($timestart, $course->id, 'assign', ASSIGN_SUBMISSION_STATUS_SUBMITTED);
|
||||
$namefields = user_picture::fields('u', null, 'userid');
|
||||
$userfieldsapi = \core\user_fields::for_userpic();
|
||||
$namefields = $userfieldsapi->get_sql('u', false, '', 'userid', false)->selects;;
|
||||
if (!$submissions = $DB->get_records_sql("SELECT asb.id, asb.timemodified, cm.id AS cmid, um.id as recordid,
|
||||
$namefields
|
||||
FROM {assign_submission} asb
|
||||
@@ -746,7 +747,8 @@ function assign_get_recent_mod_activity(&$activities,
|
||||
$params['timestart'] = $timestart;
|
||||
$params['submitted'] = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
|
||||
|
||||
$userfields = user_picture::fields('u', null, 'userid');
|
||||
$userfieldsapi = \core\user_fields::for_userpic();
|
||||
$userfields = $userfieldsapi->get_sql('u', false, '', 'userid', false)->selects;
|
||||
|
||||
if (!$submissions = $DB->get_records_sql('SELECT asb.id, asb.timemodified, ' .
|
||||
$userfields .
|
||||
@@ -833,7 +835,7 @@ function assign_get_recent_mod_activity(&$activities,
|
||||
$activity->grade = $grades->items[0]->grades[$submission->userid]->str_long_grade;
|
||||
}
|
||||
|
||||
$userfields = explode(',', user_picture::fields());
|
||||
$userfields = explode(',', implode(',', \core\user_fields::get_picture_fields()));
|
||||
foreach ($userfields as $userfield) {
|
||||
if ($userfield == 'id') {
|
||||
// Aliased in SQL above.
|
||||
|
||||
@@ -2065,9 +2065,9 @@ class assign {
|
||||
*/
|
||||
private function get_grading_sort_sql() {
|
||||
$usersort = flexible_table::get_sort_for_table('mod_assign_grading');
|
||||
$extrauserfields = get_extra_user_fields($this->get_context());
|
||||
|
||||
$userfields = explode(',', user_picture::fields('', $extrauserfields));
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$userfieldsapi = \core\user_fields::for_identity($this->context, false)->with_userpic();
|
||||
$userfields = $userfieldsapi->get_required_fields();
|
||||
$orderfields = explode(',', $usersort);
|
||||
$validlist = [];
|
||||
|
||||
@@ -4148,7 +4148,8 @@ class assign {
|
||||
$viewfullnames,
|
||||
$this->is_blind_marking(),
|
||||
$this->get_uniqueid_for_user($user->id),
|
||||
get_extra_user_fields($this->get_context()),
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
\core\user_fields::get_identity_fields($this->get_context(), false),
|
||||
!$this->is_active_user($userid));
|
||||
$o .= $this->get_renderer()->render($usersummary);
|
||||
}
|
||||
@@ -4997,7 +4998,8 @@ class assign {
|
||||
$usershtml = '';
|
||||
|
||||
$usercount = 0;
|
||||
$extrauserfields = get_extra_user_fields($this->get_context());
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$extrauserfields = \core\user_fields::get_identity_fields($this->get_context(), false);
|
||||
$viewfullnames = has_capability('moodle/site:viewfullnames', $this->get_context());
|
||||
foreach ($userlist as $userid) {
|
||||
if ($usercount >= 5) {
|
||||
@@ -5061,7 +5063,8 @@ class assign {
|
||||
$usershtml = '';
|
||||
|
||||
$usercount = 0;
|
||||
$extrauserfields = get_extra_user_fields($this->get_context());
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$extrauserfields = \core\user_fields::get_identity_fields($this->get_context(), false);
|
||||
$viewfullnames = has_capability('moodle/site:viewfullnames', $this->get_context());
|
||||
foreach ($userlist as $userid) {
|
||||
if ($usercount >= 5) {
|
||||
|
||||
@@ -156,12 +156,13 @@ class assign_override_form extends moodleform {
|
||||
list($sort) = users_order_by_sql('u');
|
||||
|
||||
// Get the list of appropriate users, depending on whether and how groups are used.
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
if ($accessallgroups) {
|
||||
$users = get_enrolled_users($this->context, '', 0,
|
||||
'u.id, u.email, ' . get_all_user_name_fields(true, 'u'), $sort);
|
||||
'u.id, u.email, ' . $userfieldsapi->get_sql('u', false, '', '', false)->selects, $sort);
|
||||
} else if ($groups = groups_get_activity_allowed_groups($cm)) {
|
||||
$enrolledjoin = get_enrolled_join($this->context, 'u.id');
|
||||
$userfields = 'u.id, u.email, ' . get_all_user_name_fields(true, 'u');
|
||||
$userfields = 'u.id, u.email, ' . $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
list($ingroupsql, $ingroupparams) = $DB->get_in_or_equal(array_keys($groups), SQL_PARAMS_NAMED);
|
||||
$params = $enrolledjoin->params + $ingroupparams;
|
||||
$sql = "SELECT $userfields
|
||||
@@ -185,7 +186,8 @@ class assign_override_form extends moodleform {
|
||||
}
|
||||
|
||||
$userchoices = array();
|
||||
$canviewemail = in_array('email', get_extra_user_fields($this->context));
|
||||
// TODO Does not support custom user profile fields (MDL-70456).
|
||||
$canviewemail = in_array('email', \core\user_fields::get_identity_fields($this->context, false));
|
||||
foreach ($users as $id => $user) {
|
||||
if (empty($invalidusers[$id]) || (!empty($override) &&
|
||||
$id == $override->userid)) {
|
||||
|
||||
@@ -90,7 +90,8 @@ if ($override->groupid) {
|
||||
$group = $DB->get_record('groups', array('id' => $override->groupid), 'id, name');
|
||||
$confirmstr = get_string("overridedeletegroupsure", "assign", $group->name);
|
||||
} else {
|
||||
$namefields = get_all_user_name_fields(true);
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
$namefields = $userfieldsapi->get_sql('', false, '', '', false)->selects;
|
||||
$user = $DB->get_record('user', array('id' => $override->userid),
|
||||
'id, ' . $namefields);
|
||||
$confirmstr = get_string("overridedeleteusersure", "assign", fullname($user));
|
||||
|
||||
@@ -121,8 +121,9 @@ if ($groupmode) {
|
||||
list($sort, $params) = users_order_by_sql('u');
|
||||
$params['assignid'] = $assign->id;
|
||||
|
||||
$userfieldsapi = \core\user_fields::for_name();
|
||||
if ($accessallgroups) {
|
||||
$sql = 'SELECT o.*, ' . get_all_user_name_fields(true, 'u') . '
|
||||
$sql = 'SELECT o.*, ' . $userfieldsapi->get_sql('u', false, '', '', false)->selects . '
|
||||
FROM {assign_overrides} o
|
||||
JOIN {user} u ON o.userid = u.id
|
||||
WHERE o.assignid = :assignid
|
||||
@@ -133,7 +134,7 @@ if ($groupmode) {
|
||||
list($insql, $inparams) = $DB->get_in_or_equal(array_keys($groups), SQL_PARAMS_NAMED);
|
||||
$params += $inparams;
|
||||
|
||||
$sql = 'SELECT o.*, ' . get_all_user_name_fields(true, 'u') . '
|
||||
$sql = 'SELECT o.*, ' . $userfieldsapi->get_sql('u', false, '', '', false)->selects . '
|
||||
FROM {assign_overrides} o
|
||||
JOIN {user} u ON o.userid = u.id
|
||||
JOIN {groups_members} gm ON u.id = gm.userid
|
||||
|
||||
+7
-4
@@ -372,7 +372,8 @@ function chat_print_recent_activity($course, $viewfullnames, $timestart) {
|
||||
$groupselect = "";
|
||||
}
|
||||
|
||||
$userfields = user_picture::fields('u');
|
||||
$userfieldsapi = \core\user_fields::for_userpic();
|
||||
$userfields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
if (!$users = $DB->get_records_sql("SELECT $userfields
|
||||
FROM {course_modules} cm
|
||||
JOIN {chat} ch ON ch.id = cm.instance
|
||||
@@ -513,7 +514,8 @@ function chat_get_users($chatid, $groupid=0, $groupingid=0) {
|
||||
$groupingjoin = '';
|
||||
}
|
||||
|
||||
$ufields = user_picture::fields('u');
|
||||
$userfieldsapi = \core\user_fields::for_userpic();
|
||||
$ufields = $userfieldsapi->get_sql('u', false, '', '', false)->selects;
|
||||
return $DB->get_records_sql("SELECT DISTINCT $ufields, c.lastmessageping, c.firstping
|
||||
FROM {chat_users} c
|
||||
JOIN {user} u ON u.id = c.userid $groupingjoin
|
||||
@@ -905,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), user_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;
|
||||
@@ -936,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), user_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;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user