Skip to content

Commit

Permalink
MDL-75283 admin: Update admin user fetching to use sort helper
Browse files Browse the repository at this point in the history
  • Loading branch information
stevandoMoodle authored and junpataleta committed Sep 7, 2022
1 parent 786a495 commit c4df60b
Show file tree
Hide file tree
Showing 3 changed files with 231 additions and 7 deletions.
20 changes: 13 additions & 7 deletions lib/datalib.php
Expand Up @@ -506,21 +506,27 @@ function get_users_listing($sort='lastaccess', $dir='ASC', $page=0, $recordsperp
$params = $params + (array)$extraparams;
}

if ($sort) {
$sort = " ORDER BY $sort $dir";
}

// If a context is specified, get extra user fields that the current user
// is supposed to see.
$extrafields = '';
$includedfields = ['id', 'username', 'email', 'firstname', 'lastname', 'city', 'country',
'lastaccess', 'confirmed', 'mnethostid', 'suspended'];
if ($extracontext) {
$extrafields = get_extra_user_fields_sql($extracontext, '', '',
array('id', 'username', 'email', 'firstname', 'lastname', 'city', 'country',
'lastaccess', 'confirmed', 'mnethostid'));
$extrafields = get_extra_user_fields_sql($extracontext, '', '', $includedfields);
}
$namefields = get_all_user_name_fields(true);
$extrafields = "$extrafields, $namefields";

if ($sort) {
$orderbymap = trim($extrafields, ', ');
$orderbymap = array_merge(explode(',', $orderbymap), $includedfields);
$neworderbymap = ['default' => 'lastaccess'];
foreach($orderbymap as $value) {
$neworderbymap[$value] = $value;
}
$sort = get_safe_orderby($neworderbymap, $sort, $dir);
}

// warning: will return UNCONFIRMED USERS
return $DB->get_records_sql("SELECT id, username, email, city, country, lastaccess, confirmed, mnethostid, suspended $extrafields
FROM {user}
Expand Down
116 changes: 116 additions & 0 deletions lib/testing/generator/data_generator.php
Expand Up @@ -1307,4 +1307,120 @@ protected function get_default_plugin_generator(string $component, ?string $clas
"data generators yet. Class {$classname} not found.");
}

/**
* 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;
}
}
102 changes: 102 additions & 0 deletions lib/tests/datalib_test.php
Expand Up @@ -684,6 +684,108 @@ public function test_get_all_instances_in_course() {
}
}

/**
* 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');

// 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,
'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('user_a@example.com', $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 department because no context specified.
$this->assertObjectNotHasAttribute('department', $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));

// Check default fallback sort field works as expected.
$results = get_users_listing('blah2', 'ASC');
$results = array_diff_key($results, $existingids);
$this->assertEquals([$userids[0], $userids[1], $userids[2], $userids[3]], array_keys($results));

// Check default fallback sort direction works as expected.
$results = get_users_listing('lastaccess', 'blah2');
$results = array_diff_key($results, $existingids);
$this->assertEquals([$userids[0], $userids[1], $userids[2], $userids[3]], 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->setAdminUser();
$results = get_users_listing('lastaccess', 'asc', 0, 0, '', '', '', '', null,
\context_system::instance());
$this->assertEquals('department_a', $results[$userids[0]]->department);

// 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, '', '', '', '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));
}

/**
* Data provider for test_get_safe_orderby().
*
Expand Down

0 comments on commit c4df60b

Please sign in to comment.