Fix #37902 Preserve search_show_childproducts checkbox state (#38516)

The "show variants" checkbox in the product list lost its state on
every filter, sort or pagination submit. An HTML checkbox does not
post anything when unchecked, so reading GETPOST() with no
companion field made the value default to '' (falsy) as soon as the
form was resubmitted, and the variants were hidden again even when
the user wanted them. The URL-side propagation also stored the raw
"on" string from a checked submit, which is brittle once the value
ever comes back as an integer 0/1.

Emit a hidden "search_show_childproducts=0" companion before the
checkbox so the form always carries the flag, use
GETPOSTISSET()/GETPOSTINT() to read it as a strict 0/1, reset it to
0 (not the empty string) when clearing filters, and stop guarding
the URL append with a value that has just been confirmed truthy.

Same code on 21.0, 22.0, 23.0 and develop; PR targets 21.0.

Reported and root cause pinpointed by @daxitsolutions.

Signed-off-by: Dolicraft <contact@dolicraft.com>
Co-authored-by: Dolicraft <contact@dolicraft.com>
This commit is contained in:
w3houde 2026-05-30 21:54:11 +07:00 committed by GitHub
parent b1d906d6d3
commit b6aaa71a3b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -126,7 +126,14 @@ $type = GETPOST("type", 'alpha');
// Show/hide child product variants
$show_childproducts = 0;
if (isModEnabled('variants')) {
$show_childproducts = GETPOST('search_show_childproducts');
// An HTML checkbox does not submit anything when unchecked, so a hidden
// "search_show_childproducts=0" companion field is emitted next to the
// checkbox (see below). GETPOSTISSET() then lets us distinguish a
// resubmission with the checkbox off from a request that does not carry
// the filter at all, and the value is read as a strict 0/1 flag.
if (GETPOSTISSET('search_show_childproducts')) {
$show_childproducts = GETPOSTINT('search_show_childproducts');
}
}
$diroutputmassaction = $conf->product->dir_output.'/temp/massgeneration/'.$user->id;
@ -383,7 +390,7 @@ if (empty($reshook)) {
$search_finished = '';
//$search_type=''; // There is 2 types of list: a list of product and a list of services. No list with both. So when we clear search criteria, we must keep the filter on type.
$show_childproducts = '';
$show_childproducts = 0;
$search_import_key = '';
$search_accountancy_code_sell = '';
$search_accountancy_code_sell_intra = '';
@ -829,7 +836,7 @@ if ($fourn_id > 0) {
$param .= "&fourn_id=".urlencode((string) ($fourn_id));
}
if ($show_childproducts) {
$param .= ($show_childproducts ? "&search_show_childproducts=".urlencode($show_childproducts) : "");
$param .= "&search_show_childproducts=".urlencode((string) $show_childproducts);
}
if ($type != '') {
$param .= '&type='.urlencode((string) ($type));
@ -979,7 +986,11 @@ if (isModEnabled('category') && $user->hasRight('categorie', 'read')) {
// Show/hide child variant products
if (isModEnabled('variants')) {
$moreforfilter .= '<div class="divsearchfield">';
$moreforfilter .= '<input type="checkbox" id="search_show_childproducts" name="search_show_childproducts"'.($show_childproducts ? 'checked="checked"' : '').'>';
// Companion hidden field ensures search_show_childproducts is always
// posted, even when the checkbox below is unchecked, so the unchecked
// state survives the filter/pagination submit and is parsed as 0/1.
$moreforfilter .= '<input type="hidden" name="search_show_childproducts" value="0">';
$moreforfilter .= '<input type="checkbox" id="search_show_childproducts" name="search_show_childproducts" value="1"'.($show_childproducts ? ' checked="checked"' : '').'>';
$moreforfilter .= ' <label for="search_show_childproducts">'.$langs->trans('ShowChildProducts').'</label>';
$moreforfilter .= '</div>';
}