Skip to content

Commit 63bdc76

Browse files
committed
patch
1 parent 16730a2 commit 63bdc76

4 files changed

Lines changed: 75 additions & 175 deletions

File tree

www/controllers/Layout/Chart/vars/hosts-count-chart.vars.inc.php

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@
99
$options = [];
1010
$totalNotUptodate = 0;
1111
$totalUptodate = 0;
12-
$totalCompliant = 0;
13-
$totalNotCompliant = 0;
1412

1513
// Getting hosts list
1614
$hosts = $hostListingController->get();
@@ -53,17 +51,14 @@
5351
} else {
5452
$totalNotUptodate++;
5553
}
56-
57-
// Calculate % compliance
58-
$totalCompliant = round(($totalUptodate / ($totalUptodate + $totalNotUptodate)) * 100, 2);
5954
}
6055

6156
$labels[] = 'Compliant';
6257
$labels[] = 'Not compliant';
6358
$datasets[0]['data'][] = $totalUptodate;
6459
$datasets[0]['data'][] = $totalNotUptodate;
6560
$datasets[0]['colors'] = ['#24d794', '#F32F63'];
66-
$options['title']['text'] = $totalCompliant . '% compliant';
61+
$options['title']['text'] = round(($totalUptodate / ($totalUptodate + $totalNotUptodate)) * 100, 2) . '% compliant';
6762
$options['title']['align'] = 'left';
6863
$options['title']['fontSize'] = 12;
6964

www/controllers/Utils/Convert.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ public static function toBool(string $string) : bool|null
1616
return filter_var($string, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
1717
}
1818

19+
/**
20+
* Converts a boolean to a string
21+
*/
22+
public static function toString(bool $bool): string
23+
{
24+
return $bool ? 'true' : 'false';
25+
}
26+
1927
/**
2028
* Converts a microtime to a time format
2129
*/

www/public/resources/js/classes/Host/HostSearch.js

Lines changed: 47 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -6,185 +6,83 @@ class HostSearch
66
*/
77
static search()
88
{
9-
var div;
10-
var filter_hostname;
11-
var filter_os;
12-
var filter_os_version;
13-
var filter_os_family;
14-
var filter_type;
15-
var filter_kernel;
16-
var filter_arch;
17-
var filter_profile;
18-
var filter_env;
19-
var filter_agent_version;
20-
var filter_reboot_required;
21-
var line;
22-
23-
const filters = ['HOSTNAME', 'OS', 'OS-VERSION', 'OS-FAMILY', 'TYPE', 'KERNEL', 'ARCH', 'PROFILE', 'ENV', 'AGENT-VERSION', 'REBOOT-REQUIRED'];
24-
25-
// Quit if the input is empty, quit
9+
// Quit if the input is empty
2610
if (!$('#search-host-input').val()) {
2711
HostSearch.showAll();
2812
return;
2913
}
3014

31-
// Show search loading spinner
3215
HostSearch.showSearchSpinner();
3316

3417
setTimeout(function () {
35-
/**
36-
* Retrieve the search term from the input
37-
* Convert the search term to uppercase to ignore case when searching
38-
*/
39-
var search = $('#search-host-input').val().toUpperCase();
18+
let search = $('#search-host-input').val().toUpperCase();
19+
const filterValues = {};
20+
21+
// Attribute name mapping for HTML attributes
22+
const attributeMap = {
23+
'HOSTNAME': 'hostname',
24+
'OS': 'os',
25+
'OS-VERSION': 'os_version',
26+
'OS-FAMILY': 'os_family',
27+
'TYPE': 'type',
28+
'KERNEL': 'kernel',
29+
'ARCH': 'arch',
30+
'PROFILE': 'profile',
31+
'ENV': 'env',
32+
'AGENT-VERSION': 'agent-version',
33+
'REBOOT-REQUIRED': 'reboot-required',
34+
'COMPLIANT': 'compliant'
35+
};
4036

4137
// Show all group containers (in case they were hidden during a previous search)
4238
$('.hosts-group-container').show();
43-
44-
// Hide all host lines, only those corresponding to the search will be re-displayed
4539
$('.host-line').removeClass('flex').hide();
4640

4741
/**
48-
* Check if the user has entered a filter in his search, different filters are possible:
49-
* os, os_version, os_family, type, kernel, arch, profile, env, agent_version, reboot_required
50-
*
51-
* e.g:
52-
* os=ubuntu 192.168
53-
* os="Linux Mint" os_version="21" 192.168
54-
*
55-
* As the input retrieved has been converted to uppercase, we search for the presence of a filter in uppercase
42+
* Parse filter parameters from search input.
43+
* Supports quoted values with spaces: os="Linux Mint" type=ubuntu
5644
*/
57-
58-
filters.forEach(function (filter) {
59-
// Match filter value: if quoted, allow spaces; if not, stop at first space
60-
// e.g. os="Linux Mint" or os=Ubuntu
61-
var regex = new RegExp(filter + '=(?:"([^"]+)"|([^" ]+))');
62-
var match = search.match(regex);
45+
Object.keys(attributeMap).forEach(function (filterKey) {
46+
const regex = new RegExp(filterKey + '=(?:"([^"]+)"|([^" ]+))');
47+
const match = search.match(regex);
6348
if (match) {
64-
var filterValue = match[1] !== undefined ? match[1] : match[2];
65-
66-
// Remove the filter from the global search (preserve spaces outside quotes)
49+
const filterValue = match[1] !== undefined ? match[1] : match[2];
50+
filterValues[filterKey] = filterValue.toUpperCase();
51+
// Remove the filter from the global search
6752
search = search.replace(regex, '').replace(/\s{2,}/g, ' ').trim();
68-
69-
switch (filter) {
70-
case 'HOSTNAME':
71-
filter_hostname = filterValue.toUpperCase();
72-
break;
73-
case 'OS':
74-
filter_os = filterValue.toUpperCase();
75-
break;
76-
case 'OS-VERSION':
77-
filter_os_version = filterValue.toUpperCase();
78-
break;
79-
case 'OS-FAMILY':
80-
filter_os_family = filterValue.toUpperCase();
81-
break;
82-
case 'TYPE':
83-
filter_type = filterValue.toUpperCase();
84-
break;
85-
case 'KERNEL':
86-
filter_kernel = filterValue.toUpperCase();
87-
break;
88-
case 'ARCH':
89-
filter_arch = filterValue.toUpperCase();
90-
break;
91-
case 'PROFILE':
92-
filter_profile = filterValue.toUpperCase();
93-
break;
94-
case 'ENV':
95-
filter_env = filterValue.toUpperCase();
96-
break;
97-
case 'AGENT-VERSION':
98-
filter_agent_version = filterValue.toUpperCase();
99-
break;
100-
case 'REBOOT-REQUIRED':
101-
filter_reboot_required = filterValue.toUpperCase();
102-
break;
103-
default:
104-
console.warn('Unknown filter:', filter);
105-
break;
106-
}
10753
}
10854
});
10955

110-
// Trim the search term again after removing filters
111-
search = search.trim();
56+
// Start with all hosts
57+
let hosts = $('.host-line');
11258

113-
// Get all host lines
114-
var hosts = $('.host-line');
59+
/**
60+
* Apply attribute-based filters
61+
*/
62+
Object.keys(filterValues).forEach(function (filterKey) {
63+
const attrName = attributeMap[filterKey];
64+
const filterValue = filterValues[filterKey];
11565

116-
if (!empty(filter_hostname)) {
117-
hosts = hosts.filter(function () {
118-
return $(this).attr('hostname').toUpperCase().indexOf(filter_hostname) > -1;
119-
});
120-
}
121-
if (!empty(filter_os)) {
122-
hosts = hosts.filter(function () {
123-
return $(this).attr('os').toUpperCase().indexOf(filter_os) > -1;
124-
});
125-
}
126-
if (!empty(filter_os_version)) {
127-
hosts = hosts.filter(function () {
128-
return $(this).attr('os_version').toUpperCase().indexOf(filter_os_version) > -1;
129-
});
130-
}
131-
if (!empty(filter_os_family)) {
132-
hosts = hosts.filter(function () {
133-
return $(this).attr('os_family').toUpperCase().indexOf(filter_os_family) > -1;
134-
});
135-
}
136-
if (!empty(filter_type)) {
137-
hosts = hosts.filter(function () {
138-
return $(this).attr('type').toUpperCase().indexOf(filter_type) > -1;
139-
});
140-
}
141-
if (!empty(filter_kernel)) {
142-
hosts = hosts.filter(function () {
143-
return $(this).attr('kernel').toUpperCase().indexOf(filter_kernel) > -1;
144-
});
145-
}
146-
if (!empty(filter_arch)) {
147-
hosts = hosts.filter(function () {
148-
return $(this).attr('arch').toUpperCase().indexOf(filter_arch) > -1;
149-
});
150-
}
151-
if (!empty(filter_profile)) {
152-
hosts = hosts.filter(function () {
153-
return $(this).attr('profile').toUpperCase().indexOf(filter_profile) > -1;
154-
});
155-
}
156-
if (!empty(filter_env)) {
157-
hosts = hosts.filter(function () {
158-
return $(this).attr('env').toUpperCase().indexOf(filter_env) > -1;
159-
});
160-
}
161-
if (!empty(filter_agent_version)) {
162-
hosts = hosts.filter(function () {
163-
return $(this).attr('agent_version').toUpperCase().indexOf(filter_agent_version) > -1;
164-
});
165-
}
166-
if (!empty(filter_reboot_required)) {
16766
hosts = hosts.filter(function () {
168-
return $(this).attr('reboot_required').toUpperCase().indexOf(filter_reboot_required) > -1;
67+
const attrValue = $(this).attr(attrName);
68+
return attrValue && attrValue.toUpperCase().indexOf(filterValue) > -1;
16969
});
170-
}
171-
172-
// Process each host line to check if it matches the search term
173-
$.each(hosts, function () {
174-
div = $(this).find('div')[0];
70+
});
17571

176-
if (div) {
177-
var txtValue = div.textContent || div.innerText;
178-
if (txtValue.toUpperCase().indexOf(search) > -1) {
179-
$(this).addClass('flex').show();
180-
}
72+
/**
73+
* Process each host line to check if it matches the free text search term
74+
*/
75+
hosts.each(function () {
76+
const textContent = $(this).text().toUpperCase();
77+
if (textContent.indexOf(search) > -1) {
78+
$(this).addClass('flex').show();
18179
}
18280
});
18381

184-
// Finally, hide the search spinner and show the hosts container
82+
// Hide the search spinner and show the hosts container
18583
HostSearch.hideSearchSpinner();
18684

187-
// Hide group divs whose all divs have been hidden
85+
// Hide group divs whose all hosts have been hidden
18886
HostSearch.hideGroupDiv();
18987
}, 500);
19088
}

www/views/includes/containers/hosts/list.inc.php

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22
use \Controllers\User\Permission\Host as HostPermission;
33
use \Controllers\Utils\Generate\Html\Label;
44
use \Controllers\Utils\Generate\Html\Icon;
5-
use \Controllers\Host\Package\Package;
6-
use \Controllers\Host\Package\Event as HostPackageEvent; ?>
5+
use \Controllers\Utils\Convert;
6+
use \Controllers\Host\Package\Event as HostPackageEvent;
7+
use \Controllers\Host\Package\Package; ?>
78

89
<section class="section-main reloadable-container" container="hosts/list">
910
<div class="flex justify-space-between margin-top-50 margin-bottom-40">
@@ -244,9 +245,24 @@
244245

245246
unset($hostPackageController, $hostPackageEventController);
246247

248+
// Determine host compliance before rendering attributes used by JS filters
249+
$class = '';
250+
if ($packagesAvailableTotal >= $complianceThresholdCount) {
251+
$class = 'bkg-red';
252+
$compliant = false;
253+
$compliantIcon = 'warning-red.svg';
254+
$compliantTitle = 'Host is not compliant: pending updates (' . $packagesAvailableTotal . ') >= threshold (' . $complianceThresholdCount . ')';
255+
}
256+
257+
if ($latestUpdate and strtotime($latestUpdate) < strtotime('-' . $complianceThresholdDays . ' days')) {
258+
$compliant = false;
259+
$compliantIcon = 'warning-red.svg';
260+
$compliantTitle = 'Host is not compliant: latest update (' . DateTime::createFromFormat('Y-m-d', $latestUpdate)->format('d-m-Y') . ') is older than threshold (' . $complianceThresholdDays . ' day' . ($complianceThresholdDays > 1 ? 's' : '') . ')';
261+
}
262+
247263
// Print the host informations
248264
// Here the <div> will contain all the host informations in order to be able to search on it (input 'search a host') ?>
249-
<div class="host-line flex flex-direction-column div-generic-blue bck-blue-alt margin-bottom-10" hostid="<?= $id ?>" hostname="<?= $hostname ?>" os="<?= $os ?>" os_version="<?= $osVersion ?>" os_family="<?= $osFamily ?>" type="<?= $type ?>" kernel="<?= $kernel ?>" arch="<?= $arch ?>" profile="<?= $profile ?>" env="<?= $env ?>" agent_version="<?= $agentVersion ?>" reboot_required="<?= $rebootRequired ?>">
265+
<div class="host-line flex flex-direction-column div-generic-blue bck-blue-alt margin-bottom-10" hostid="<?= $id ?>" hostname="<?= $hostname ?>" os="<?= $os ?>" os_version="<?= $osVersion ?>" os_family="<?= $osFamily ?>" type="<?= $type ?>" kernel="<?= $kernel ?>" arch="<?= $arch ?>" profile="<?= $profile ?>" env="<?= $env ?>" agent-status="<?= $agentStatus ?>" agent-version="<?= $agentVersion ?>" reboot_required="<?= $rebootRequired ?>" compliant="<?= Convert::toString($compliant) ?>">
250266
<div class="flex column-gap-20">
251267
<div class="align-self-center">
252268
<?php
@@ -290,23 +306,6 @@
290306
<div class="flex flex-direction-column row-gap-5">
291307
<a href="/host/<?= $id ?>" target="_blank" rel="noopener noreferrer">
292308
<div class="label-icon-tr max-width-fit">
293-
<?php
294-
$class = '';
295-
// The host is not compliant if the available updates count is >= threshold
296-
if ($packagesAvailableTotal >= $complianceThresholdCount) {
297-
$class = 'bkg-red';
298-
$compliant = false;
299-
$compliantIcon = 'warning-red.svg';
300-
$compliantTitle = 'Host is not compliant: pending updates (' . $packagesAvailableTotal . ') >= threshold (' . $complianceThresholdCount . ')';
301-
}
302-
303-
// The host is not compliant if the latest update is older than the threshold in days
304-
if ($latestUpdate and strtotime($latestUpdate) < strtotime('-' . $complianceThresholdDays . ' days')) {
305-
$compliant = false;
306-
$compliantIcon = 'warning-red.svg';
307-
$compliantTitle = 'Host is not compliant: latest update (' . DateTime::createFromFormat('Y-m-d', $latestUpdate)->format('d-m-Y') . ') is older than threshold (' . $complianceThresholdDays . ' day' . ($complianceThresholdDays > 1 ? 's' : '') . ')';
308-
} ?>
309-
310309
<img src="/assets/icons/<?= $compliantIcon ?>" class="icon-np" title="<?= $compliantTitle ?>"/>
311310
<div class="flex align-item-center column-gap-10">
312311
<p class="font-size-13" title="<?= $packagesInstalledTotal . ' package(s) installed on this host' ?>"><?= $packagesInstalledTotal ?></p>

0 commit comments

Comments
 (0)