From ad8580d69450819bdf79f158c495a84d9057e7b3 Mon Sep 17 00:00:00 2001 From: Tony Murray Date: Fri, 9 Sep 2022 11:22:58 -0500 Subject: [PATCH] Cleanup and optimize the availability widget (#14329) * Cleanup and optimize the availability widget Default sort is display name Sort applies to services too (services always last) May need to refresh the page to get new css * style * We don't need request (lint fix) * Wrong service field name --- .../Widgets/AvailabilityMapController.php | 271 +++++++++++------- html/css/styles.css | 3 +- phpstan-baseline.neon | 10 - resources/views/layouts/librenmsv1.blade.php | 2 +- .../views/widgets/availability-map.blade.php | 26 +- .../settings/availability-map.blade.php | 4 +- 6 files changed, 174 insertions(+), 142 deletions(-) diff --git a/app/Http/Controllers/Widgets/AvailabilityMapController.php b/app/Http/Controllers/Widgets/AvailabilityMapController.php index 9c70bc9103..e57e50062a 100644 --- a/app/Http/Controllers/Widgets/AvailabilityMapController.php +++ b/app/Http/Controllers/Widgets/AvailabilityMapController.php @@ -25,11 +25,14 @@ namespace App\Http\Controllers\Widgets; +use App\Models\AlertSchedule; use App\Models\Device; use App\Models\DeviceGroup; use App\Models\Service; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Auth; use LibreNMS\Config; +use LibreNMS\Util\Url; class AvailabilityMapController extends WidgetController { @@ -44,7 +47,7 @@ class AvailabilityMapController extends WidgetController 'color_only_select' => 0, 'show_disabled_and_ignored' => 0, 'mode_select' => 0, - 'order_by' => Config::get('webui.availability_map_sort_status') ? 'status' : 'hostname', + 'order_by' => Config::get('webui.availability_map_sort_status') ? 'status' : 'display-name', 'device_group' => null, ]; } @@ -53,20 +56,8 @@ class AvailabilityMapController extends WidgetController { $data = $this->getSettings(); - $devices = []; - $device_totals = []; - $services = []; - $services_totals = []; - - $mode = $data['mode_select']; - if ($mode == 0 || $mode == 2) { - [$devices, $device_totals] = $this->getDevices($request); - } - if ($mode > 0) { - [$services, $services_totals] = $this->getServices($request); - } - - $data['device'] = Device::first(); + [$devices, $device_totals] = $this->getDevices(); + [$services, $services_totals] = $this->getServices(); $data['devices'] = $devices; $data['device_totals'] = $device_totals; @@ -81,142 +72,206 @@ class AvailabilityMapController extends WidgetController return view('widgets.settings.availability-map', $this->getSettings(true)); } - /** - * @param Request $request - * @return array - */ - private function getDevices(Request $request) + private function getDevices(): array { $settings = $this->getSettings(); + if ($settings['mode_select'] == 1) { // services only + return [[], []]; + } + // filter for by device group or show all if ($settings['device_group']) { - $device_query = DeviceGroup::find($settings['device_group'])->devices()->hasAccess($request->user()); + $device_query = DeviceGroup::find($settings['device_group'])->devices()->hasAccess(Auth::user()); } else { - $device_query = Device::hasAccess($request->user()); + $device_query = Device::hasAccess(Auth::user()); } if (! $settings['show_disabled_and_ignored']) { $device_query->isNotDisabled(); } - $devices = $device_query->select(['devices.device_id', 'hostname', 'sysName', 'display', 'status', 'uptime', 'last_polled', 'disabled', 'disable_notify', 'location_id'])->get(); + $devices = $device_query->select(['devices.device_id', 'hostname', 'sysName', 'display', 'status', 'uptime', 'last_polled', 'disabled', 'ignore'])->get(); // process status - $uptime_warn = Config::get('uptime_warning', 86400); + $uptime_warn = (int) Config::get('uptime_warning', 86400); + $check_maintenance = AlertSchedule::isActive()->exists(); // check if any maintenance schedule is active $totals = ['warn' => 0, 'up' => 0, 'down' => 0, 'maintenance' => 0, 'ignored' => 0, 'disabled' => 0]; $data = []; - // add another field for the selected device label - $label_type = $settings['color_only_select']; - foreach ($devices as $device) { - $row = ['device' => $device]; - if ($device->disabled) { - $totals['disabled']++; - $row['stateName'] = 'disabled'; - $row['labelClass'] = 'blackbg'; - } elseif ($device->disable_notify) { - $totals['ignored']++; - $row['stateName'] = 'alert-dis'; - $row['labelClass'] = 'label-default'; - } elseif ($device->status == 1) { - if (($device->uptime < $uptime_warn) && ($device->uptime != 0)) { - $totals['warn']++; - $row['stateName'] = 'warn'; - $row['labelClass'] = 'label-warning'; - } else { - $totals['up']++; - $row['stateName'] = 'up'; - $row['labelClass'] = 'label-success'; - } - } else { - $totals['down']++; - $row['stateName'] = 'down'; - $row['labelClass'] = 'label-danger'; - } + // parse state and count + [$state_name, $class] = $this->parseDeviceState($device, $uptime_warn); + $totals[$state_name]++; - if ($device->isUnderMaintenance()) { - $row['labelClass'] = 'label-default'; + if ($check_maintenance && $device->isUnderMaintenance()) { + $class = 'label-default'; $totals['maintenance']++; } - if ($label_type == 1) { - $row['label'] = null; - } elseif ($label_type == 4) { - $row['label'] = strtolower($device->shortDisplayName()); - } elseif ($label_type == 2) { - $row['label'] = strtolower($device->hostname); - } elseif ($label_type == 3) { - $row['label'] = strtolower($device->sysName); - } else { - $row['label'] = $device->status; + if ($settings['type'] == 1) { + $class = "availability-map-oldview-box-$state_name"; } - $data[] = $row; + $data[] = [ + 'status' => $device->status, + 'link' => Url::deviceUrl($device), + 'tooltip' => $this->getDeviceTooltip($device, $state_name), + 'label' => $this->getDeviceLabel($device, $state_name), // add another field for the selected label + 'labelClass' => $class, + ]; } - // now apply sorting, depending on the selected device label - $order_by = $settings['order_by']; - - if ($order_by == 'status') { - usort($data, function ($l, $r) { - $retval = $l['device']->status <=> $r['device']->status; - if ($retval == 0) { - $retval = $l['label'] <=> $r['label']; - } - - return $retval; - }); - } elseif ($order_by == 'label') { - usort($data, function ($l, $r) { - return $l['label'] <=> $r['label']; - }); - } + $this->sort($data); return [$data, $totals]; } - private function getServices($request) + private function getServices(): array { $settings = $this->getSettings(); - // filter for by device group or show all - if ($settings['device_group']) { - $services_query = DeviceGroup::find($settings['device_group'])->services()->hasAccess($request->user()); - } else { - $services_query = Service::hasAccess($request->user()); + if ($settings['mode_select'] == 0) { // devices only + return [[], []]; } - if ($settings['order_by'] == 'status') { - $services_query->orderBy('service_status', 'DESC')->orderBy('service_type'); - } elseif ($settings['order_by'] == 'hostname') { - $services_query->leftJoin('devices', 'services.device_id', 'devices.device_id')->orderBy('hostname')->orderBy('service_type'); + // filter for by device group or show all + if ($settings['device_group']) { + $services_query = DeviceGroup::find($settings['device_group'])->services()->hasAccess(Auth::user()); + } else { + $services_query = Service::hasAccess(Auth::user()); } - $services = $services_query->with(['device' => function ($query) { - $query->select(['devices.device_id', 'hostname', 'sysName']); - }])->select(['service_id', 'services.device_id', 'service_type', 'service_desc', 'service_status'])->get(); + + $services = $services_query->with([ + 'device' => function ($query) { + $query->select(['devices.device_id', 'hostname', 'sysName', 'display']); + }, + ])->select(['service_id', 'services.device_id', 'service_type', 'service_name', 'service_desc', 'service_status'])->get(); // process status $totals = ['warn' => 0, 'up' => 0, 'down' => 0]; $data = []; foreach ($services as $service) { - $row = ['service' => $service]; - if ($service->service_status == 0) { - $row['labelClass'] = 'label-success'; - $row['stateName'] = 'up'; - $totals['up']++; - } elseif ($service->service_status == 1) { - $row['labelClass'] = 'label-warning'; - $row['stateName'] = 'warn'; - $totals['warn']++; - } else { - $row['labelClass'] = 'label-danger'; - $row['stateName'] = 'down'; - $totals['down']++; + [$state_name, $class] = $this->parseServiceState($service); + $totals[$state_name]++; + + if ($settings['type'] == 1) { + $class = "availability-map-oldview-box-$state_name"; } - $data[] = $row; + + $data[] = [ + 'status' => $service->service_status, + 'link' => Url::deviceUrl($service->device), + 'tooltip' => $this->getServiceTooltip($service), + 'label' => $this->getServiceLabel($service, $state_name), + 'labelClass' => $class, + ]; } + $this->sort($data); + return [$data, $totals]; } + + private function sort(array &$data): void + { + switch ($this->getSettings()['order_by']) { + case 'status': + usort($data, function ($l, $r) { + return ($l['status'] <=> $r['status']) ?: strcasecmp($l['label'], $r['label']); + }); + break; + case 'label': + usort($data, function ($l, $r) { + return strcasecmp($l['label'], $r['label']); + }); + break; + default: // device display name (tooltip starts with the display name) + usort($data, function ($l, $r) { + return strcasecmp($l['tooltip'], $r['tooltip']) ?: strcasecmp($l['label'], $r['label']); + }); + } + } + + private function getDeviceLabel(Device $device, string $state_name): string + { + switch ($this->getSettings()['color_only_select']) { + case 1: + return ''; + case 4: + return $device->shortDisplayName(); + case 2: + return strtolower($device->hostname); + case 3: + return strtolower($device->sysName); + default: + return __($state_name); + } + } + + private function getServiceLabel(Service $service, string $state_name): string + { + if ($this->getSettings()['color_only_select'] == 1) { + return ''; + } + + return $service->service_type . ' - ' . __($state_name); + } + + private function getDeviceTooltip(Device $device, string $state_name): string + { + $tooltip = $device->displayName(); + $time = $device->formatDownUptime(true); + + if ($time) { + $tooltip .= ' - ' . ($state_name == 'down' ? 'downtime ' : '') . $time; + } + + return $tooltip; + } + + private function getServiceTooltip(Service $service): string + { + $tooltip = $service->device->displayName() . ' [' . $service->service_type . ']'; + + $description = $service->service_name ?: $service->service_desc; + if ($description) { + $tooltip .= ' ' . $description; + } + + return $tooltip; + } + + private function parseDeviceState(Device $device, int $uptime_warn): array + { + if ($device->disabled) { + return ['disabled', 'blackbg']; + } + + if ($device->ignore) { + return ['ignored', 'label-default']; + } + + if ($device->status == 1) { + if (($device->uptime < $uptime_warn) && ($device->uptime != 0)) { + return ['warn', 'label-warning']; + } + + return ['up', 'label-success']; + } + + return ['down', 'label-danger']; + } + + private function parseServiceState(Service $service): array + { + if ($service->service_status == 0) { + return ['up', 'label-success']; + } + + if ($service->service_status == 1) { + return ['warn', 'label-warning']; + } + + return ['down', 'label-danger']; + } } diff --git a/html/css/styles.css b/html/css/styles.css index ed70f4fec2..6f4bcbd008 100644 --- a/html/css/styles.css +++ b/html/css/styles.css @@ -2060,7 +2060,8 @@ label { margin:2px; } -.widget-availability-fixed { +.widget-availability:empty { + display: inline; min-width: 2.4em; min-height: 1.3em; max-height: 1.6em; diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index e92efd69c4..25db30a868 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -7630,16 +7630,6 @@ parameters: count: 1 path: app/Http/Controllers/UserPreferencesController.php - - - message: "#^Method App\\\\Http\\\\Controllers\\\\Widgets\\\\AvailabilityMapController\\:\\:getServices\\(\\) has no return type specified\\.$#" - count: 1 - path: app/Http/Controllers/Widgets/AvailabilityMapController.php - - - - message: "#^Method App\\\\Http\\\\Controllers\\\\Widgets\\\\AvailabilityMapController\\:\\:getServices\\(\\) has parameter \\$request with no type specified\\.$#" - count: 1 - path: app/Http/Controllers/Widgets/AvailabilityMapController.php - - message: "#^Method App\\\\Http\\\\Controllers\\\\Widgets\\\\DeviceSummaryController\\:\\:getData\\(\\) has no return type specified\\.$#" count: 1 diff --git a/resources/views/layouts/librenmsv1.blade.php b/resources/views/layouts/librenmsv1.blade.php index 2a45964807..6d824f9f44 100644 --- a/resources/views/layouts/librenmsv1.blade.php +++ b/resources/views/layouts/librenmsv1.blade.php @@ -42,7 +42,7 @@ - + @foreach(LibreNMS\Config::get('webui.custom_css', []) as $custom_css) diff --git a/resources/views/widgets/availability-map.blade.php b/resources/views/widgets/availability-map.blade.php index dbd8cbe053..89fab58bbc 100644 --- a/resources/views/widgets/availability-map.blade.php +++ b/resources/views/widgets/availability-map.blade.php @@ -26,35 +26,21 @@
@foreach($devices as $row) - + @if($type == 0) - @if($color_only_select == 1) - - @elseif($color_only_select == 2) - {{ $row['device']->hostname }} - @elseif($color_only_select == 3) - {{ $row['device']->sysName }} - @elseif($color_only_select == 4) - {{ $row['device']->shortDisplayName() }} - @else - {{ __($row['stateName']) }} - @endif + {{ $row['label'] }} @else -
+
@endif
@endforeach @foreach($services as $row) - + @if($type == 0) - @if($color_only_select) - - @else - {{ $row['service']->service_type }} - {{ $row['stateName'] }} - @endif + {{ $row['label'] }} @else -
+
@endif
@endforeach diff --git a/resources/views/widgets/settings/availability-map.blade.php b/resources/views/widgets/settings/availability-map.blade.php index fdb578e6ac..5ef3e4733b 100644 --- a/resources/views/widgets/settings/availability-map.blade.php +++ b/resources/views/widgets/settings/availability-map.blade.php @@ -21,7 +21,7 @@ - + @@ -54,6 +54,7 @@ @@ -72,7 +73,6 @@ init_select2('#device_group-{{ $id }}', 'device-group', {}); function toggle_availability_type(el, id) { - console.log(el.value); if (el.value === '0') { $('#tile_size-group-' + id).hide(); $('#color_only_select-group-' + id).show();