Commit cf31e142 authored by Son Nguyen's avatar Son Nguyen Committed by Aaron Wells
Browse files

Add a filter for users with duplicate emails (Bug #1166499)



1. When the option "Filter by duplicate email addresses" is checked,
 the result table will list the accounts with duplicate emails.
 The primary email and additional emails will be shown in the column
'Emails'
 The column 'Emails' is still sortable based on the primary email

2. Also add the column 'Authentication' in search result table

3. Reset offset param when changing the filters

Change-Id: Id59f2de1d9a95c0bb67e8da40eebfeb5a5def60f
Signed-off-by: default avatarSon Nguyen <son.nguyen@catalyst.net.nz>
parent e59a4270
......@@ -37,14 +37,15 @@ if ($action == 'search') {
require_once('searchlib.php');
$params = new StdClass;
$params->query = trim(param_variable('query', ''));
$params->institution = param_alphanum('institution', null);
$params->f = param_alpha('f', null);
$params->l = param_alpha('l', null);
$params->sortby = param_alpha('sortby', 'firstname');
$params->sortdir = param_alpha('sortdir', 'asc');
$params->loggedin = param_alpha('loggedin', 'any');
$params->loggedindate= param_variable('loggedindate', null);
$params->query = trim(param_variable('query', ''));
$params->institution = param_alphanum('institution', null);
$params->f = param_alpha('f', null);
$params->l = param_alpha('l', null);
$params->sortby = param_alpha('sortby', 'firstname');
$params->sortdir = param_alpha('sortdir', 'asc');
$params->loggedin = param_alpha('loggedin', 'any');
$params->loggedindate = param_variable('loggedindate', null);
$params->duplicateemail = param_boolean('duplicateemail', false);
$offset = param_integer('offset', 0);
$limit = param_integer('limit', 10);
......
......@@ -36,13 +36,14 @@ define('SECTION_PAGE', 'usersearch');
require_once('searchlib.php');
$search = (object) array(
'query' => trim(param_variable('query', '')),
'f' => param_alpha('f', null), // first initial
'l' => param_alpha('l', null), // last initial
'sortby' => param_alpha('sortby', 'firstname'),
'sortdir' => param_alpha('sortdir', 'asc'),
'loggedin' => param_alpha('loggedin', 'any'),
'loggedindate'=> param_variable('loggedindate', strftime(get_string('strftimedatetimeshort'))),
'query' => trim(param_variable('query', '')),
'f' => param_alpha('f', null), // first initial
'l' => param_alpha('l', null), // last initial
'sortby' => param_alpha('sortby', 'firstname'),
'sortdir' => param_alpha('sortdir', 'asc'),
'loggedin' => param_alpha('loggedin', 'any'),
'loggedindate' => param_variable('loggedindate', strftime(get_string('strftimedatetimeshort'))),
'duplicateemail' => param_boolean('duplicateemail', false),
);
$offset = param_integer('offset', 0);
......
......@@ -34,6 +34,7 @@ function UserSearch() {
self.selectusers = {};
self.rewriteCheckboxes();
self.rewriteLoggedInFilter();
self.rewriteDuplicateEmailFilter();
self.params = {'loggedindate' : $('loggedinform_loggedindate').value};
};
......@@ -77,19 +78,19 @@ function UserSearch() {
else {
delete self.params[initialtype];
}
self.params.offset = 0;
self.doSearch();
e.stop();
};
this.searchByChildLink = function (element) {
var children = getElementsByTagAndClassName('a', null, element);
if (children.length == 1) {
var href = getNodeAttribute(children[0], 'href');
self.params = parseQueryString(href.substring(href.indexOf('?')+1, href.length));
// Assume this is only changing the page or the order of results,
// so pass true here to avoid clearing the selected users.
self.doSearch(true);
}
// First <a> element should be the link of the column header
var href = getNodeAttribute(children[0], 'href');
self.params = parseQueryString(href.substring(href.indexOf('?')+1, href.length));
// Assume this is only changing the page or the order of results,
// so pass true here to avoid clearing the selected users.
self.doSearch(true);
};
this.changePage = function(e) {
......@@ -200,6 +201,7 @@ function UserSearch() {
connect($('loggedin'), 'onchange', function(e) {
e.stop();
var type = this.value;
self.params.offset = 0;
self.params.loggedin = type;
if (type === 'since' || type === 'notsince') {
removeElementClass($('loggedindate_container'), 'js-hidden');
......@@ -211,11 +213,20 @@ function UserSearch() {
});
$('loggedinform_loggedindate').onchange = function(e) {
// Set handler directly so that calendar works
self.params.offset = 0;
self.params.loggedindate = this.value;
self.doSearch();
};
};
this.rewriteDuplicateEmailFilter = function() {
$('duplicateemail').onclick = function(e) {
self.params.offset = 0;
self.params.duplicateemail = this.checked;
self.doSearch();
};
};
addLoadEvent(self.init);
}
......
......@@ -1038,6 +1038,9 @@ $string['usershaveneverloggedin'] = 'Users have never logged in';
$string['usershaveloggedinsince'] = 'Users have logged in since';
$string['usershavenotloggedinsince'] = 'Users have not logged in since';
// Admin user search duplicate email filter
$string['duplicateemailfilter'] = 'Filter by duplicate email addresses:';
$string['lastlogin'] = 'Last login';
// Masquerading reasons and notification
......
<h3>Emails</h3>
<p>When the option "Filter by duplicate email addresses" is used, each user will have all their email addresses displayed, i.e. the primary email address and any additional email addresses entered on the "Contact information" page in the profile. If a user has more than one email address, those that are duplicates are marked with an asterisk (*).</p>
......@@ -406,6 +406,7 @@ $string['language'] = 'Language';
$string['itemdeleted'] = 'Item deleted';
$string['itemupdated'] = 'Item updated';
$string['approvalrequired'] = 'Approval required';
$string['authentication'] = 'Authentication';
// Forgot password
$string['cantchangepassword'] = 'Sorry, you are unable to change your password through this interface - please use your institution\'s interface instead.';
......@@ -543,6 +544,7 @@ $string['sitecontentnotfound'] = '%s text not available';
// Contact us form
$string['name'] = 'Name';
$string['email'] = 'Email';
$string['emails'] = 'Emails';
$string['subject'] = 'Subject';
$string['message'] = 'Message';
$string['messagesent'] = 'Your message has been sent';
......
......@@ -247,6 +247,30 @@ function get_admin_user_search_results($search, $offset, $limit) {
}
// Filter by duplicate emails
if ($search->duplicateemail) {
$duplicateemailartefacts = get_column_sql('
SELECT id
FROM {artefact}
WHERE
artefacttype = \'email\'
AND LOWER(title) IN (
SELECT LOWER(title)
FROM artefact
WHERE artefacttype = \'email\'
GROUP BY LOWER(title)
HAVING count(id) > 1
)');
if ($duplicateemailartefacts === false || !is_array($duplicateemailartefacts)) {
$duplicateemailartefacts = array();
}
$constraints[] = array(
'field' => 'duplicateemail',
'type' => 'in',
'string' => $duplicateemailartefacts
);
}
// Filter by viewable institutions:
global $USER;
if (!$USER->get('admin') && !$USER->get('staff')) {
......@@ -288,6 +312,35 @@ function get_admin_user_search_results($search, $offset, $limit) {
if (!empty($result['institutions'])) {
$result['institutions'] = array_combine($result['institutions'],$result['institutions']);
}
// Show all user's emails if searching for duplicate emails
if ($search->duplicateemail) {
$emails = get_records_sql_array('
SELECT title,
(CASE WHEN id IN (' . join(',', array_map('db_quote', $duplicateemailartefacts)) . ') THEN 1 ELSE 0 END) AS duplicated
FROM {artefact} a
WHERE a.artefacttype = ?
AND a.owner = ?',
array('email', $result['id']));
if (is_array($emails)) {
for ($i = 0; $i < count($emails); $i++) {
// Move primary email to the beginning of $emails
if ($emails[0]->title == $result['email']) {
break;
}
if ($emails[$i]->title == $result['email']) {
$e = $emails[0];
$emails[0] = $emails[$i];
$emails[$i] = $e;
break;
}
}
}
else {
throw new EmailException('An User must have at least one email!');
}
$result['email'] = $emails;
}
if ($isadmin) {
continue;
}
......@@ -310,7 +363,7 @@ function get_admin_user_search_results($search, $offset, $limit) {
function build_admin_user_search_results($search, $offset, $limit) {
global $USER, $THEME;
$wantedparams = array('query', 'f', 'l', 'sortby', 'sortdir', 'loggedin', 'loggedindate');
$wantedparams = array('query', 'f', 'l', 'sortby', 'sortdir', 'loggedin', 'loggedindate', 'duplicateemail');
$params = array();
foreach ($search as $k => $v) {
if (!in_array($k, $wantedparams)) {
......@@ -367,6 +420,16 @@ function build_admin_user_search_results($search, $offset, $limit) {
),
);
if ($search->duplicateemail) {
$cols['email'] = array(
'name' => get_string('emails'),
'sort' => true,
'help' => true,
'helplink' => get_help_icon('core', 'admin', 'usersearch', 'email'),
'template' => 'admin/users/searchemailcolumn.tpl',
);
}
$institutions = get_records_assoc('institution', '', '', '', 'name,displayname');
if (count($institutions) > 1) {
$cols['institution'] = array(
......@@ -376,6 +439,11 @@ function build_admin_user_search_results($search, $offset, $limit) {
);
}
$cols['authname'] = array(
'name' => get_string('authentication'),
'sort' => true,
);
$cols['lastlogin'] = array(
'name' => get_string('lastlogin', 'admin'),
'sort' => true,
......
......@@ -371,6 +371,33 @@ class PluginSearchInternal extends PluginSearch {
}
/**
* Returns a list of search results for the admin user search interface.
*
* The constraints parameter takes an array of arrays, like so:
* $params = array(
* array(
* 'field' => 'institution'
* 'string' => 'mahara'
* 'type' => 'equals'
* ),
* ...
* )
*
* Each constraint should has these three keys:
* field: Should be a column in the usr table, or the special field "duplicateemails" (which indicates only users with a non-unique email).
* also, for the field "institution", a string value of "mahara" indicates users with no institution
* string: The value to compare the contents of that field against
* type: The operation by which to compare "field" to "string". This can be any of the operations in PluginSearchInternal::match_expression
* (starts, equals, notequals, greaterthan, greaterthanequal, lessthan, lessthanequal, contains, or in)
*
* @param string $query_string The string to search for
* @param array $constraints A list of constraints on the search results (see above for format)
* @param int $offset
* @param int $limit
* @param string $sortfield Which of the output columns to sort by
* @param string $sortdir DESC or ASC
*/
public static function admin_search_user($query_string, $constraints, $offset, $limit,
$sortfield, $sortdir) {
$sort = 'TRUE';
......@@ -421,7 +448,22 @@ class PluginSearchInternal extends PluginSearch {
. PluginSearchInternal::match_expression($f['type'], $f['string'], $values, $ilike) . '
)';
}
} else {
}
else if ($f['field'] == 'duplicateemail') {
if (!empty($f['string'])) {
$where .= '
AND u.id IN (
SELECT owner
FROM {artefact}
WHERE id IN (' . join(',', array_map('db_quote', $f['string'])) . ')
)';
}
else {
// No duplicate email is found, return empty list
$where .= 'AND FALSE';
}
}
else {
$where .= ' AND u.' . $f['field']
. PluginSearchInternal::match_expression($f['type'], $f['string'], $values, $ilike);
}
......@@ -434,8 +476,8 @@ class PluginSearchInternal extends PluginSearch {
$data = get_records_sql_assoc('
SELECT
u.id, u.firstname, u.lastname, u.preferredname, u.username, u.email, u.staff, u.profileicon,
u.lastlogin, u.active, NOT u.suspendedcusr IS NULL as suspended
FROM {usr} u ' . $where . '
u.lastlogin, u.active, NOT u.suspendedcusr IS NULL as suspended, au.instancename AS authname
FROM {usr} u INNER JOIN {auth_instance} au ON u.authinstance = au.id ' . $where . '
ORDER BY ' . $sort . ', u.id',
$values,
$offset,
......
......@@ -66,6 +66,10 @@
{$loggedindate|safe}
</span>
</div>
<div class="duplicateemail-filter">
<label for="duplicateemail">{str tag="duplicateemailfilter" section="admin"}</label>
<input type="checkbox" name="duplicateemail" id="duplicateemail" value="1"{if $search->duplicateemail} checked{/if}>
</div>
<div class="searchform-gap nojs-hidden-block"></div>
<div class="searchform">
<label>{str tag='Search' section='admin'}:</label>
......
{foreach from=$r.email item=e}
{if (count($r.email) > 1 && $e->duplicated)}
<div>{$e->title} *</div>
{else}
<div>{$e->title}</div>
{/if}
{/foreach}
......@@ -21,6 +21,9 @@
{else}
{$c.name}
{/if}
{if $c.help}
{$c.helplink|safe}
{/if}
{if $c.headhtml}<div style="font-weight: normal;">{$c.headhtml|safe}</div>{/if}
</th>
{/foreach}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment