Firewall - Rules [new]: Add all rules option to interface selectpicker and select it by default

This commit is contained in:
Monviech 2026-02-03 10:21:10 +01:00
parent c3dd6d56f1
commit e6e59fa2c1
2 changed files with 47 additions and 13 deletions

View file

@ -81,18 +81,21 @@ class FilterController extends FilterBaseController
{
$categories = $this->request->get('category');
$show_all = !empty($this->request->get('show_all'));
if (!empty($this->request->get('interface'))) {
$interfaces = explode(',', $this->request->get('interface'));
if ($show_all) {
/* add groups which contain the selected interface when looking at full impact*/
if (!$this->request->has('interface')) {
// ALL rules
$interfaces = null;
} else {
// interface param may be empty
$interfaces = array_filter(explode(',', (string)$this->request->get('interface')), 'strlen');
if ($show_all && !empty($interfaces)) {
/* add groups which contain the selected interface when looking at full impact */
foreach ((new Group())->ifgroupentry->iterateItems() as $groupItem) {
if (array_intersect($interfaces, $groupItem->members->getValues())) {
$interfaces[] = $groupItem->ifname->getValue();
}
}
}
} else {
$interfaces = [];
}
/* filter logic for mvc rules */
@ -100,6 +103,11 @@ class FilterController extends FilterBaseController
$is_cat = empty($categories) || array_intersect(explode(',', $record->categories), $categories);
$rule_interfaces = $record->interface->getValues();
// ALL rules, skip interface logic entirely
if ($interfaces === null) {
return $is_cat;
}
if (!$record->interfacenot->isEmpty()) {
$if_intersects = !array_intersect($interfaces, $rule_interfaces); /* All but interface */
} else {
@ -150,12 +158,17 @@ class FilterController extends FilterBaseController
}
$is_cat = empty($categories) || array_intersect($r_categories, $categories);
if (!empty($record['interfacenot'])) {
$is_if = !array_intersect(explode(',', $record['interface'] ?? ''), $interfaces);
if ($interfaces === null) {
// ALL interfaces, interface always matches
$is_if = true;
} else {
$is_if = array_intersect(explode(',', $record['interface'] ?? ''), $interfaces);
if (!empty($record['interfacenot'])) {
$is_if = !array_intersect(explode(',', $record['interface'] ?? ''), $interfaces);
} else {
$is_if = array_intersect(explode(',', $record['interface'] ?? ''), $interfaces);
}
$is_if = $is_if || empty($record['interface']);
}
$is_if = $is_if || empty($record['interface']);
if ($is_cat && $is_if) {
/* translate/convert legacy fields before returning, similar to mvc handling */
@ -383,6 +396,11 @@ class FilterController extends FilterBaseController
'label' => gettext('Interfaces'),
'icon' => 'fa fa-ethernet text-info',
'items' => []
],
'any' => [
'label' => gettext('Any'),
'icon' => 'fa fa-globe-europe text-muted',
'items' => []
]
];
@ -399,6 +417,8 @@ class FilterController extends FilterBaseController
$ruleCounts[$interfaces[0]] = ($ruleCounts[$interfaces[0]] ?? 0) + 1;
}
}
// ALL rules
$totalRules = array_sum($ruleCounts);
// Helper to build item with label and count
$makeItem = fn($value, $label, $count, $type) => [
@ -426,6 +446,9 @@ class FilterController extends FilterBaseController
}
}
// ALL rules
$result['any']['items'][] = $makeItem('*', gettext('All rules'), $totalRules, 'any');
// Sort items by count and alphabetically
foreach ($result as &$section) {
usort($section['items'], fn($a, $b) =>

View file

@ -163,10 +163,16 @@
}
// Add interface selectpicker, or fall back to hash for the first load
let selectedInterface = $('#interface_select').val();
if ((!selectedInterface || selectedInterface.length === 0) && pendingUrlInterface) {
if (
(selectedInterface === null || selectedInterface === undefined || selectedInterface === '') &&
pendingUrlInterface
) {
request['interface'] = pendingUrlInterface;
pendingUrlInterface = null; // consume the hash so it is not used again
} else if (selectedInterface && selectedInterface.length > 0) {
// ALL interfaces
} else if (selectedInterface === '*') {
// do not send interface parameter!
} else if (selectedInterface !== null && selectedInterface !== undefined) {
request['interface'] = selectedInterface;
}
if (inspectEnabled) {
@ -728,7 +734,8 @@
const bgClassMap = {
floating: 'bg-primary',
group: 'bg-warning',
interface: 'bg-info'
interface: 'bg-info',
any: 'text-muted'
};
const badgeClass = bgClassMap[item.type] || 'bg-info';
@ -758,6 +765,10 @@
$('#interface_select').val(ifaceFromHash).selectpicker('refresh');
}
}
// Default to ALL interfaces when nothing is selected and no hash applied
if (!match && ($('#interface_select').val() === null || $('#interface_select').val() === '')) {
$('#interface_select').val('*').selectpicker('refresh');
}
interfaceInitialized = true;
},