fixes #1214 select only filtered users + fixes #999 use up-to-date datatable API

* use datatables.ajax instead of datatables.ajaxsource. The request parameters have changed, the backend has been updated accordingly

* the "select all" users is replaced by "select page" + "select whole set". The selection only applies to filtered list. If you filter on a group, for example, the "select whole set" will only select users of this group.
This commit is contained in:
Louis
2020-10-28 17:57:31 +01:00
committed by plegall
parent 5b956f255e
commit 44f18a09d1
3 changed files with 169 additions and 112 deletions
+88 -46
View File
@@ -27,7 +27,9 @@ var missingConfirm = "{'You need to confirm deletion'|translate|escape:javascrip
var missingUsername = "{'Please, enter a login'|translate|escape:javascript}";
let title_msg = '{'Are you sure you want to delete the user "%s"?'|@translate|escape:'javascript'}';
var allUsers = [{$all_users}];
var filteredUsers = [];
var nb_all_users = 0;
//var allUsers = [{$all_users}];
var selection = [];
var pwg_token = "{$PWG_TOKEN}";
@@ -110,7 +112,8 @@ jQuery(document).ready(function() {
jQuery("#addUserForm input[type=text], #addUserForm input[type=password]").val("");
var new_user = data.result.users[0];
allUsers.push(parseInt(new_user.id));
nb_all_users += 1;
//allUsers.push(parseInt(new_user.id));
jQuery("#showAddUser .infos").html(sprintf(newUser_pattern, new_user.username)).show();
checkSelection();
@@ -199,7 +202,6 @@ jQuery(document).ready(function() {
/* Formating function for row details */
function fnFormatDetails(oTable, nTr) {
var userId = oTable.api().row(nTr).data()[0];
console.log("userId = "+userId);
var sOut = null;
jQuery.ajax({
@@ -296,7 +298,7 @@ jQuery(document).ready(function() {
var template = _.template(
jQuery("script.userDetails").html()
);
);
jQuery("#user"+userId).html(template(user));
@@ -632,7 +634,13 @@ jQuery(document).on('click', '.close-user-details', function(e) {
processing: true,
serverSide: true,
serverMethod: "POST",
ajaxSource: "admin/user_list_backend.php",
ajax: {
url: "admin/user_list_backend.php",
type: "POST",
data: {
pwg_token: pwg_token
}
},
pagingType: "simple",
{/literal}{if (isset($filter_group))}{literal}
"oSearch": {"sSearch": "group:{/literal}{$filter_group}{literal}"},
@@ -654,10 +662,12 @@ jQuery(document).on('click', '.close-user-details', function(e) {
}
},
"drawCallback": function( oSettings ) {
nb_all_users = parseInt(oTable.api().ajax.json().iTotalRecords);
jQuery("#userList input[type=checkbox]").each(function() {
var user_id = jQuery(this).data("user_id");
jQuery(this).prop('checked', (selection.indexOf(user_id) != -1));
});
checkSelection();
},
columns: aoColumns
});
@@ -668,7 +678,7 @@ jQuery(document).on('click', '.close-user-details', function(e) {
function checkSelection() {
if (selection.length > 0) {
jQuery("#forbidAction").hide();
jQuery("#permitAction").show();
jQuery("#permitActionUserList").show();
jQuery("#applyOnDetails").text(
sprintf(
@@ -677,11 +687,11 @@ jQuery(document).on('click', '.close-user-details', function(e) {
)
);
if (selection.length == allUsers.length) {
if (selection.length == nb_all_users) {
jQuery("#selectedMessage").text(
sprintf(
selectedMessage_all,
allUsers.length
nb_all_users
)
);
}
@@ -690,19 +700,19 @@ jQuery(document).on('click', '.close-user-details', function(e) {
sprintf(
selectedMessage_pattern,
selection.length,
allUsers.length
nb_all_users
)
);
}
}
else {
jQuery("#forbidAction").show();
jQuery("#permitAction").hide();
jQuery("#permitActionUserList").hide();
jQuery("#selectedMessage").text(
sprintf(
selectedMessage_none,
allUsers.length
nb_all_users
)
);
}
@@ -721,9 +731,50 @@ jQuery(document).on('click', '.close-user-details', function(e) {
checkSelection();
});
jQuery("#selectSet").click(function () {
let api = oTable.api();
let info = api.page.info();
let search_term = api.search();
jQuery.ajax({
url: "admin/user_list_backend.php",
type:"POST",
data: {
search: {
value: search_term,
regex: false
},
start: info.start,
length: info.length,
get_set_uids: true
},
beforeSend: function() {
$("#checkActions .loading").show();
},
success:function(data) {
data = jQuery.parseJSON(data);
filtered_users = data.filtered_uids.map(x=>+x);
selection = filtered_users;
checkSelection();
$("#checkActions .loading").hide();
},
error:function(XMLHttpRequest, textStatus, errorThrows) {
$("#checkActions .loading").hide();
}
});
jQuery("#userList input[type=checkbox]").prop('checked', true);
checkSelection();
return false;
});
jQuery("#selectAll").click(function () {
selection = allUsers;
jQuery("#selectAllPage").click(function () {
let tab = []
let sel = $("tr td").find("label").each(function() {
let val = $(this).find("input").attr('data-user_id');
tab.push(parseInt(val));
});
selection = tab;
jQuery("#userList input[type=checkbox]").prop('checked', true);
checkSelection();
return false;
@@ -737,20 +788,17 @@ jQuery(document).on('click', '.close-user-details', function(e) {
});
jQuery("#selectInvert").click(function () {
var newSelection = [];
for(var i in allUsers)
{
if (selection.indexOf(allUsers[i]) == -1) {
newSelection.push(allUsers[i]);
let tab = []
let sel = $("tr td").find("label").each(function() {
let val = parseInt($(this).find("input").attr('data-user_id'));
$(this).find("input[type=checkbox]").prop('checked', false);
if (selection.includes(val) == false) {
tab.push(val);
$(this).find("input[type=checkbox]").prop('checked', true);
}
}
selection = newSelection;
jQuery("#userList input[type=checkbox]").each(function() {
var user_id = jQuery(this).data("user_id");
jQuery(this).prop('checked', (selection.indexOf(user_id) != -1));
});
selection = tab;
//jQuery("#userList input[type=checkbox]").prop('checked', true);
checkSelection();
return false;
});
@@ -775,7 +823,7 @@ jQuery(document).on('click', '.close-user-details', function(e) {
}
});
jQuery("#permitAction input, #permitAction select").click(function() {
jQuery("#permitActionUserList input, #permitActionUserList select").click(function() {
jQuery("#applyActionBlock .infos").hide();
});
@@ -851,15 +899,7 @@ jQuery(document).on('click', '.close-user-details', function(e) {
jQuery("#applyActionBlock .infos").show();
if (action == 'delete') {
var allUsers_new = [];
for(var i in allUsers)
{
if (selection.indexOf(allUsers[i]) == -1) {
allUsers_new.push(allUsers[i]);
}
}
allUsers = allUsers_new;
console.log('allUsers_new.length = '+allUsers_new.length);
nb_all_users -= 1;
selection = [];
checkSelection();
}
@@ -871,7 +911,6 @@ jQuery(document).on('click', '.close-user-details', function(e) {
return false;
});
});
{/literal}{/footer_script}
@@ -903,7 +942,7 @@ span.infos, span.errors {background-image:none; padding:2px 5px; margin:0;border
.recent_period_infos {margin-left:10px;}
.nb_image_page, .recent_period {width:340px;margin-top:5px;}
#action_recent_period .recent_period {display:inline-block;}
.checkActions {padding:0 1em;}
#checkActions {padding:0 1em;}
{/literal}{/html_style}
<div class="titrePage">
@@ -973,20 +1012,23 @@ span.infos, span.errors {background-image:none; padding:2px 5px; margin:0;border
<div style="clear:right"></div>
<p class="checkActions">
{'Select:'|@translate}
<a href="#" id="selectAll">{'All'|@translate}</a>,
<a href="#" id="selectNone">{'None'|@translate}</a>,
<a href="#" id="selectInvert">{'Invert'|@translate}</a>
<div style='margin: 1em; padding: 1em;' class="ActionUserList">
<legend></span>{'Selection'|@translate}</legend>
<p id="checkActions">
<a href="#" id="selectAllPage">{'The whole page'|@translate}</a>
<a href="#" id="selectSet">{'The whole set'|@translate}</a><span class="loading" style="display:none"><img src="themes/default/images/ajax-loader-small.gif"></span>
<a href="#" id="selectNone">{'None'|@translate}</a>
<a href="#" id="selectInvert">{'Invert'|@translate}</a>
<span id="selectedMessage"></span>
</p>
<span id="selectedMessage"></span>
</p>
</div>
<fieldset id="action">
<legend>{'Action'|@translate}</legend>
<div id="forbidAction">{'No users selected, no actions possible.'|@translate}</div>
<div id="permitAction" style="display:none">
<div id="permitActionUserList" style="display:block">
<select name="selectAction">
<option value="-1">{'Choose an action'|@translate}</option>
@@ -1095,7 +1137,7 @@ span.infos, span.errors {background-image:none; padding:2px 5px; margin:0;border
<span class="infos" style="display:none">&#x2714; {'Users modified'|translate}</span>
</p>
</div> {* #permitAction *}
</div> {* #permitActionUserList *}
</fieldset>
</form>
+2
View File
@@ -1921,6 +1921,8 @@ h2:lang(en) { text-transform:capitalize; }
.infos .submit {margin-left:30px;}
.checkActions {text-align:left;padding:0;margin:0;}
#checkActions {text-align:left; margin:0 0 20px 0;}
.ActionUserList #checkActions {margin: 20px 0 20px 0;}
.pluginBoxes {
border: none;
+79 -66
View File
@@ -13,10 +13,8 @@ include_once(PHPWG_ROOT_PATH.'include/common.inc.php');
check_status(ACCESS_ADMINISTRATOR);
check_input_parameter('iDisplayStart', $_REQUEST, false, PATTERN_ID);
check_input_parameter('iDisplayLength', $_REQUEST, false, PATTERN_ID);
check_input_parameter('start', $_REQUEST, false, PATTERN_ID);
check_input_parameter('length', $_REQUEST, false, PATTERN_ID);
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Easy set variables
@@ -52,38 +50,32 @@ $sTable = USERS_TABLE.' INNER JOIN '.USER_INFOS_TABLE.' AS ui ON '.$conf['user_f
* Paging
*/
$sLimit = "";
if ( isset( $_REQUEST['iDisplayStart'] ) && $_REQUEST['iDisplayLength'] != '-1' )
if ( isset( $_REQUEST['start'] ) && $_REQUEST['length'] != '-1' )
{
$sLimit = "LIMIT ".$_REQUEST['iDisplayStart'].", ".$_REQUEST['iDisplayLength'];
$sLimit = "LIMIT ".$_REQUEST['start'].", ".$_REQUEST['length'];
}
$sOrder = "";
/*
* Ordering
*/
if ( isset( $_REQUEST['iSortCol_0'] ) )
if ( isset( $_REQUEST["order"][0]["column"] ) )
{
$sOrder = "ORDER BY ";
for ( $i=0 ; $i<intval( $_REQUEST['iSortingCols'] ) ; $i++ )
$i = 0;
$col = $_REQUEST["order"][0]["column"];
if ( $_REQUEST['columns'][$col]["searchable"] == "true" )
{
check_input_parameter('iSortCol_'.$i, $_REQUEST, false, PATTERN_ID);
if ( $_REQUEST[ 'bSortable_'.$_REQUEST['iSortCol_'.$i] ] == "true" )
{
check_input_parameter('sSortDir_'.$i, $_REQUEST, false, '/^(asc|desc)$/');
$sOrder .= $aColumns[ $_REQUEST['iSortCol_'.$i] ].' '.$_REQUEST['sSortDir_'.$i].', ';
}
$sOrder .= $aColumns[ $col ].' '.$_REQUEST["order"][0]["dir"].', ';
}
$sOrder = substr_replace( $sOrder, "", -2 );
if ( $sOrder == "ORDER BY" )
{
$sOrder = "";
}
}
/*
* Filtering
* NOTE this does not match the built-in DataTables filtering which does it
@@ -91,11 +83,11 @@ if ( isset( $_REQUEST['iSortCol_0'] ) )
* on very large tables, and MySQL's regex functionality is very limited
*/
$sWhere = "";
if ( $_REQUEST['sSearch'] != "" )
if ( isSet( $_REQUEST['search']["value"]) && $_REQUEST['search']["value"] != "" )
{
$user_ids = null;
if (preg_match('/group:(\d+)/', $_REQUEST['sSearch'], $matches))
if (preg_match('/group:(\d+)/', $_REQUEST['search']["value"], $matches))
{
$group_id = $matches[1];
@@ -108,7 +100,7 @@ SELECT
$user_ids = query2array($query, null, 'user_id');
$user_ids[] = -1;
$_REQUEST['sSearch'] = preg_replace('/group:(\d+)/', '', $_REQUEST['sSearch']);
$_REQUEST['search']["value"] = preg_replace('/group:(\d+)/', '', $_REQUEST['search']["value"]);
}
$sWhere = "WHERE (";
@@ -118,11 +110,11 @@ SELECT
$sWhere.= '`user_id` IN ('.implode(',', $user_ids).') OR ';
}
if ($_REQUEST['sSearch'] != "")
if ($_REQUEST['search']["value"] != "")
{
for ( $i=0 ; $i<count($aColumns) ; $i++ )
{
$sWhere .= $aColumns[$i]." LIKE '%".pwg_db_real_escape_string( $_REQUEST['sSearch'] )."%' OR ";
$sWhere .= $aColumns[$i]." LIKE '%".pwg_db_real_escape_string( $_REQUEST['search']["value"] )."%' OR ";
}
}
@@ -133,8 +125,8 @@ SELECT
/* Individual column filtering */
for ( $i=0 ; $i<count($aColumns) ; $i++ )
{
if (isset($_REQUEST['bSearchable_'.$i]) && isset($_REQUEST['sSearch_'.$i])
&&$_REQUEST['bSearchable_'.$i] == "true" && $_REQUEST['sSearch_'.$i] != ''
if (isset($_REQUEST['columns'][$i]["searchable"]) && isset($_REQUEST['columns'][$i]['search']['value'])
&& $_REQUEST['columns'][$i]["searchable"] == "true" && $_REQUEST['columns'][$i]['search']['value'] != ''
)
{
if ( $sWhere == "" )
@@ -145,7 +137,7 @@ for ( $i=0 ; $i<count($aColumns) ; $i++ )
{
$sWhere .= " AND ";
}
$sWhere .= $aColumns[$i]." LIKE '%".pwg_db_real_escape_string($_REQUEST['sSearch_'.$i])."%' ";
$sWhere .= $aColumns[$i]." LIKE '%".pwg_db_real_escape_string($_REQUEST['columns'][$i]['search']['value'])."%' ";
}
}
@@ -154,19 +146,29 @@ for ( $i=0 ; $i<count($aColumns) ; $i++ )
* SQL queries
* Get data to display
*/
$sQuery = "
if (isSet($_REQUEST['get_set_uids'])) {
$sQuery = "
SELECT SQL_CALC_FOUND_ROWS ".str_replace(" , ", " ", implode(", ", $aColumns))."
FROM $sTable
$sWhere
$sOrder ;
";
} else {
$sQuery = "
SELECT SQL_CALC_FOUND_ROWS ".str_replace(" , ", " ", implode(", ", $aColumns))."
FROM $sTable
$sWhere
$sOrder
$sLimit
";
";
}
$rResult = pwg_query($sQuery);
/* Data set length after filtering */
$rResultFilterTotal = pwg_query('SELECT FOUND_ROWS();');
list($iFilteredTotal) = pwg_db_fetch_row($rResultFilterTotal);
/* Total data set length */
$sQuery = "
SELECT COUNT(".$sIndexColumn.")
@@ -175,54 +177,65 @@ $sQuery = "
$rResultTotal = pwg_query($sQuery);
$aResultTotal = pwg_db_fetch_array($rResultTotal);
$iTotal = $aResultTotal[0];
$sEcho = isSet($_REQUEST['sEcho']) ? intval($_REQUEST['sEcho']) : 0;
/*
* Output
*/
$output = array(
"sEcho" => intval($_REQUEST['sEcho']),
"sEcho" => $sEcho,
"iTotalRecords" => $iTotal,
"iTotalDisplayRecords" => $iFilteredTotal,
"aaData" => array()
"aaData" => array(),
"filtered_uids" => array()
);
$user_ids = array();
$filtered_uids = array();
while ( $aRow = pwg_db_fetch_array( $rResult ) )
{
$user_ids[] = $aRow[ $conf['user_fields']['id'] ];
$row = array();
for ( $i=0 ; $i<count($aColumns) ; $i++ )
if (isSet($_REQUEST['get_set_uids'])) {
while ( $aRow = pwg_db_fetch_array( $rResult ) )
{
if ( $aColumns[$i] == "status" )
{
$row[] = l10n('user_status_'.$aRow[ $aColumns[$i] ]);
}
else if ( $aColumns[$i] == "level" )
{
$row[] = $aRow[ $aColumns[$i] ] == 0 ? '' : l10n(sprintf('Level %d', $aRow[ $aColumns[$i] ]));
}
else if ( $aColumns[$i] != ' ' )
{
/* General output */
$colname = $aColumns[$i];
foreach ($conf['user_fields'] as $real_name => $alias)
{
if ($aColumns[$i] == $real_name)
{
$colname = $alias;
}
}
$row[] = $aRow[$colname];
}
$filtered_uids[] = $aRow[ $conf['user_fields']['id'] ];
}
} else {
while ( $aRow = pwg_db_fetch_array( $rResult ) )
{
$user_ids[] = $aRow[ $conf['user_fields']['id'] ];
$row = array();
for ( $i=0 ; $i<count($aColumns) ; $i++ )
{
if ( $aColumns[$i] == "status" )
{
$row[] = l10n('user_status_'.$aRow[ $aColumns[$i] ]);
}
else if ( $aColumns[$i] == "level" )
{
$row[] = $aRow[ $aColumns[$i] ] == 0 ? '' : l10n(sprintf('Level %d', $aRow[ $aColumns[$i] ]));
}
else if ( $aColumns[$i] != ' ' )
{
/* General output */
$colname = $aColumns[$i];
foreach ($conf['user_fields'] as $real_name => $alias)
{
if ($aColumns[$i] == $real_name)
{
$colname = $alias;
}
}
$row[] = $aRow[$colname];
}
}
$output['aaData'][] = $row;
}
$output['aaData'][] = $row;
}
$output["filtered_uids"] = $filtered_uids;
// replace "recent_period" by the list of groups
if (count($user_ids) > 0)
{
@@ -254,6 +267,6 @@ SELECT
}
$output = trigger_change('after_render_user_list', $output);
echo json_encode( $output );
?>