mirror of
https://github.com/EGroupware/egroupware.git
synced 2025-01-03 04:29:28 +01:00
Limit number of accounts (& other select/taglist search results) sent to client
This commit is contained in:
parent
7bb4332961
commit
f8e56edc20
@ -33,17 +33,14 @@ export class Et2SelectAccount extends Et2Select
|
|||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
|
||||||
// currently only account_selection "Primary group and search" needs the search,
|
// all types can search the server. If there are a lot of accounts, local list will
|
||||||
// all other types have the accounts fully local
|
// not be complete
|
||||||
if (this.egw().preference('account_selection', 'common') === 'primary_group')
|
if(this.egw().preference('account_selection', 'common') !== 'none')
|
||||||
{
|
{
|
||||||
this.searchUrl = "EGroupware\\Api\\Etemplate\\Widget\\Taglist::ajax_search";
|
this.searchUrl = "EGroupware\\Api\\Etemplate\\Widget\\Taglist::ajax_search";
|
||||||
}
|
}
|
||||||
else // always allow local search
|
|
||||||
{
|
this.searchOptions = {type: 'account', account_type: 'accounts'};
|
||||||
this.search = true;
|
|
||||||
}
|
|
||||||
this.searchOptions = { type: 'account', account_type: 'accounts' };
|
|
||||||
this.__accountType = 'accounts';
|
this.__accountType = 'accounts';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,8 +230,29 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
protected validators : Validator[];
|
protected validators : Validator[];
|
||||||
|
|
||||||
private _searchTimeout : number;
|
private _searchTimeout : number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When user is typing, we wait this long for them to be finished before we start the search
|
||||||
|
* @type {number}
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
protected static SEARCH_TIMEOUT = 500;
|
protected static SEARCH_TIMEOUT = 500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We need at least this many characters before we start the search
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
protected static MIN_CHARS = 2;
|
protected static MIN_CHARS = 2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Limit server searches to 100 results, matches Link::DEFAULT_NUM_ROWS
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
static RESULT_LIMIT : number = 100;
|
||||||
|
|
||||||
|
|
||||||
// Hold the original option data from earlier search results, since we discard on subsequent search
|
// Hold the original option data from earlier search results, since we discard on subsequent search
|
||||||
private _selected_remote = <SelectOption[]>[];
|
private _selected_remote = <SelectOption[]>[];
|
||||||
|
|
||||||
@ -1019,8 +1040,13 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
*/
|
*/
|
||||||
protected remoteQuery(search : string, options : object)
|
protected remoteQuery(search : string, options : object)
|
||||||
{
|
{
|
||||||
|
// Include a limit, even if options don't, to avoid massive lists breaking the UI
|
||||||
|
let sendOptions = {
|
||||||
|
num_rows: Et2WidgetWithSearch.RESULT_LIMIT,
|
||||||
|
...options
|
||||||
|
}
|
||||||
return this.egw().request(this.egw().link(this.egw().ajaxUrl(this.egw().decodePath(this.searchUrl)),
|
return this.egw().request(this.egw().link(this.egw().ajaxUrl(this.egw().decodePath(this.searchUrl)),
|
||||||
{query: search, ...options}), [search, options]).then((result) =>
|
{query: search, ...sendOptions}), [search, sendOptions]).then((result) =>
|
||||||
{
|
{
|
||||||
this.processRemoteResults(result);
|
this.processRemoteResults(result);
|
||||||
});
|
});
|
||||||
@ -1033,8 +1059,16 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
*/
|
*/
|
||||||
protected processRemoteResults(results)
|
protected processRemoteResults(results)
|
||||||
{
|
{
|
||||||
|
// If results have a total included, pull it out.
|
||||||
|
// It will cause errors if left in the results
|
||||||
|
let total = null;
|
||||||
|
if(typeof results.total !== "undefined")
|
||||||
|
{
|
||||||
|
total = results.total;
|
||||||
|
delete results.total;
|
||||||
|
}
|
||||||
let entries = cleanSelectOptions(results);
|
let entries = cleanSelectOptions(results);
|
||||||
|
let resultCount = entries.length;
|
||||||
|
|
||||||
if(entries.length == 0)
|
if(entries.length == 0)
|
||||||
{
|
{
|
||||||
@ -1073,12 +1107,23 @@ export const Et2WithSearchMixin = <T extends Constructor<LitElement>>(superclass
|
|||||||
temp_target.querySelectorAll(":scope > *").forEach((item) =>
|
temp_target.querySelectorAll(":scope > *").forEach((item) =>
|
||||||
{
|
{
|
||||||
// Avoid duplicate error
|
// Avoid duplicate error
|
||||||
if(!target.querySelector("[value='" + item.value.replace(/'/g, "\\\'") + "']"))
|
if(!target.querySelector("[value='" + ('' + item.value).replace(/'/g, "\\\'") + "']"))
|
||||||
{
|
{
|
||||||
target.appendChild(item);
|
target.appendChild(item);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.handleMenuSlotChange();
|
this.handleMenuSlotChange();
|
||||||
|
})
|
||||||
|
.then(() =>
|
||||||
|
{
|
||||||
|
if(total && total > resultCount)
|
||||||
|
{
|
||||||
|
// More results available that were not sent
|
||||||
|
let count = document.createElement("span")
|
||||||
|
count.classList.add("remote");
|
||||||
|
count.textContent = this.egw().lang("%1 more...", total - resultCount);
|
||||||
|
target.appendChild(count);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -427,11 +427,12 @@ class Accounts
|
|||||||
}
|
}
|
||||||
$accounts = array();
|
$accounts = array();
|
||||||
foreach(self::getInstance()->search(array(
|
foreach(self::getInstance()->search(array(
|
||||||
'type' => $options['filter']['group'] < 0 ? $options['filter']['group'] : $type,
|
'type' => $options['filter']['group'] < 0 ? $options['filter']['group'] : $type,
|
||||||
'query' => $pattern,
|
'query' => $pattern,
|
||||||
'query_type' => 'all',
|
'query_type' => 'all',
|
||||||
'order' => $order,
|
'order' => $order,
|
||||||
)) as $account)
|
'offset' => $options['num_rows']
|
||||||
|
)) as $account)
|
||||||
{
|
{
|
||||||
$displayName = self::format_username($account['account_lid'],
|
$displayName = self::format_username($account['account_lid'],
|
||||||
$account['account_firstname'],$account['account_lastname'],$account['account_id']);
|
$account['account_firstname'],$account['account_lastname'],$account['account_id']);
|
||||||
@ -452,6 +453,11 @@ class Accounts
|
|||||||
$accounts[$account['account_id']] = $displayName;
|
$accounts[$account['account_id']] = $displayName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// If limited rows were requested, send the total number of rows
|
||||||
|
if(array_key_exists('num_rows', $options))
|
||||||
|
{
|
||||||
|
$options['total'] = self::getInstance()->total;
|
||||||
|
}
|
||||||
return $accounts;
|
return $accounts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,13 +87,20 @@ class Taglist extends Etemplate\Widget
|
|||||||
$results[] = ['value' => $id, 'label' => $name];
|
$results[] = ['value' => $id, 'label' => $name];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
usort($results, static function ($a, $b) use ($query) {
|
usort($results, static function ($a, $b) use ($query)
|
||||||
similar_text($query, $a["label"], $percent_a);
|
{
|
||||||
similar_text($query, $b["label"], $percent_b);
|
similar_text($query, $a["label"], $percent_a);
|
||||||
return $percent_a === $percent_b ? 0 : ($percent_a > $percent_b ? -1 : 1);
|
similar_text($query, $b["label"], $percent_b);
|
||||||
|
return $percent_a === $percent_b ? 0 : ($percent_a > $percent_b ? -1 : 1);
|
||||||
});
|
});
|
||||||
|
|
||||||
// switch regular JSON response handling off
|
// If we have a total, include it too so client knows if results were limited
|
||||||
|
if(array_key_exists('total', $options))
|
||||||
|
{
|
||||||
|
$results['total'] = intval($options['total']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// switch regular JSON response handling off
|
||||||
Api\Json\Request::isJSONRequest(false);
|
Api\Json\Request::isJSONRequest(false);
|
||||||
|
|
||||||
header('Content-Type: application/json; charset=utf-8');
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
@ -1662,7 +1662,8 @@ abstract class Framework extends Framework\Extra
|
|||||||
// close session now, to not block other user actions
|
// close session now, to not block other user actions
|
||||||
$GLOBALS['egw']->session->commit_session();
|
$GLOBALS['egw']->session->commit_session();
|
||||||
|
|
||||||
$list = array('accounts' => array(),'groups' => array(), 'owngroups' => array());
|
$list = array('accounts' => array('num_rows' => Link::DEFAULT_NUM_ROWS), 'groups' => array(),
|
||||||
|
'owngroups' => array());
|
||||||
if($GLOBALS['egw_info']['user']['preferences']['common']['account_selection'] == 'primary_group')
|
if($GLOBALS['egw_info']['user']['preferences']['common']['account_selection'] == 'primary_group')
|
||||||
{
|
{
|
||||||
$list['accounts']['filter']['group'] = $GLOBALS['egw_info']['user']['account_primary_group'];
|
$list['accounts']['filter']['group'] = $GLOBALS['egw_info']['user']['account_primary_group'];
|
||||||
|
Loading…
Reference in New Issue
Block a user