mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
New Map Menu (#15969)
* Map Menu New top level Map menu. Ability to group custom maps. Ajax Select controller improvements * Fix style
This commit is contained in:
@@ -47,6 +47,7 @@ class CustomMapController extends Controller
|
||||
return view('map.custom-manage', [
|
||||
'maps' => CustomMap::orderBy('name')->get(['custom_map_id', 'name']),
|
||||
'name' => 'New Map',
|
||||
'menu_group' => null,
|
||||
'node_align' => 10,
|
||||
'edge_separation' => 10,
|
||||
'reverse_arrows' => 0,
|
||||
@@ -101,6 +102,7 @@ class CustomMapController extends Controller
|
||||
'edit' => false,
|
||||
'map_id' => $map->custom_map_id,
|
||||
'name' => $map->name,
|
||||
'menu_group' => $map->menu_group,
|
||||
'reverse_arrows' => $map->reverse_arrows,
|
||||
'legend' => $this->legendConfig($map),
|
||||
'background' => (bool) $map->background_suffix,
|
||||
@@ -123,6 +125,7 @@ class CustomMapController extends Controller
|
||||
$data = [
|
||||
'map_id' => $map->custom_map_id,
|
||||
'name' => $map->name,
|
||||
'menu_group' => $map->menu_group,
|
||||
'node_align' => $map->node_align,
|
||||
'edge_separation' => $map->edge_separation,
|
||||
'reverse_arrows' => $map->reverse_arrows,
|
||||
@@ -163,6 +166,7 @@ class CustomMapController extends Controller
|
||||
return response()->json([
|
||||
'id' => $map->custom_map_id,
|
||||
'name' => $map->name,
|
||||
'menu_group' => $map->menu_group,
|
||||
'width' => $map->width,
|
||||
'height' => $map->height,
|
||||
'reverse_arrows' => $map->reverse_arrows,
|
||||
|
44
app/Http/Controllers/Select/CustomMapMenuGroupController.php
Normal file
44
app/Http/Controllers/Select/CustomMapMenuGroupController.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
/*
|
||||
* CustomMapMenuGroupController.php
|
||||
*
|
||||
* -Description-
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @package LibreNMS
|
||||
* @link http://librenms.org
|
||||
* @copyright 2024 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace App\Http\Controllers\Select;
|
||||
|
||||
use App\Models\CustomMap;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class CustomMapMenuGroupController extends SelectController
|
||||
{
|
||||
protected function searchFields($request): array
|
||||
{
|
||||
return ['menu_group'];
|
||||
}
|
||||
|
||||
protected function baseQuery(Request $request): Builder
|
||||
{
|
||||
return CustomMap::query()->hasAccess($request->user())
|
||||
->whereNotNull('menu_group')->select('menu_group')->groupBy('menu_group');
|
||||
}
|
||||
}
|
@@ -62,19 +62,18 @@ class DashboardController extends SelectController
|
||||
];
|
||||
}
|
||||
|
||||
public function formatResponse($paginator)
|
||||
protected function prependItem(): array
|
||||
{
|
||||
if (! request()->has('term')) {
|
||||
$paginator->prepend((object) ['dashboard_id' => 0]);
|
||||
}
|
||||
|
||||
return parent::formatResponse($paginator);
|
||||
return [
|
||||
'id' => 0,
|
||||
'text' => __('No Default Dashboard'),
|
||||
];
|
||||
}
|
||||
|
||||
private function describe($dashboard): string
|
||||
{
|
||||
if ($dashboard->dashboard_id == 0) {
|
||||
return 'No Default Dashboard';
|
||||
return $this->prependItem()['text'];
|
||||
}
|
||||
|
||||
return "{$dashboard->username}: {$dashboard->dashboard_name} ("
|
||||
|
@@ -39,16 +39,11 @@ class PollerGroupController extends SelectController
|
||||
return PollerGroup::query()->select(['id', 'group_name']);
|
||||
}
|
||||
|
||||
protected function formatResponse($paginator)
|
||||
protected function prependItem(): array
|
||||
{
|
||||
// prepend the default group, unless filtered out
|
||||
if ($this->includeGeneral()) {
|
||||
$general = new PollerGroup;
|
||||
$general->id = 0;
|
||||
$general->group_name = 'General';
|
||||
$paginator->prepend($general);
|
||||
}
|
||||
|
||||
return parent::formatResponse($paginator);
|
||||
return [
|
||||
'id' => 0,
|
||||
'text' => __('General'),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@@ -32,7 +32,6 @@ use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Query\Builder;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
abstract class SelectController extends PaginatedAjaxController
|
||||
{
|
||||
@@ -78,8 +77,15 @@ abstract class SelectController extends PaginatedAjaxController
|
||||
*/
|
||||
protected function formatResponse($paginator)
|
||||
{
|
||||
$results = collect($paginator->items())->map([$this, 'formatItem']);
|
||||
|
||||
// prepend the initial item, unless filtered out
|
||||
if ($this->canPrependFirstItem(request())) {
|
||||
$results->prepend($this->prependItem());
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'results' => collect($paginator->items())->map([$this, 'formatItem']),
|
||||
'results' => $results,
|
||||
'pagination' => ['more' => $paginator->hasMorePages()],
|
||||
]);
|
||||
}
|
||||
@@ -111,11 +117,28 @@ abstract class SelectController extends PaginatedAjaxController
|
||||
];
|
||||
}
|
||||
|
||||
protected function includeGeneral(): bool
|
||||
protected function prependItem(): ?array
|
||||
{
|
||||
if (request()->has('id') && request('id') !== 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function canPrependFirstItem(Request $request): bool
|
||||
{
|
||||
$item = $this->prependItem();
|
||||
|
||||
if (empty($item)) {
|
||||
return false;
|
||||
} elseif (request()->has('term') && ! Str::contains('general', strtolower(request('term')))) {
|
||||
}
|
||||
|
||||
if ($request->page > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($request->has('id') && $request->id != $item['id']) { // purposely loose comparison
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($request->has('term') && ! str_contains(strtolower($item['text']), strtolower($request->term))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@@ -23,7 +23,8 @@ class CustomMapSettingsRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string',
|
||||
'name' => 'required|string|max:100',
|
||||
'menu_group' => 'nullable|string|max:100',
|
||||
'node_align' => 'integer',
|
||||
'reverse_arrows' => 'boolean',
|
||||
'edge_separation' => 'integer',
|
||||
|
@@ -78,12 +78,6 @@ class MenuComposer
|
||||
//Dashboards
|
||||
$vars['dashboards'] = Dashboard::select('dashboard_id', 'dashboard_name')->allAvailable($user)->orderBy('dashboard_name')->get();
|
||||
|
||||
//Maps
|
||||
$vars['links'] = Link::exists();
|
||||
$vars['device_dependencies'] = \DB::table('device_relationships')->exists();
|
||||
$vars['device_group_dependencies'] = \DB::table('device_group_device')->exists();
|
||||
$vars['custommaps'] = CustomMap::select('custom_map_id', 'name')->hasAccess($user)->orderBy('name')->get();
|
||||
|
||||
// Device menu
|
||||
$vars['device_groups'] = DeviceGroup::hasAccess($user)->orderBy('name')->get(['device_groups.id', 'name', 'desc']);
|
||||
$vars['package_count'] = Package::hasAccess($user)->count();
|
||||
@@ -95,6 +89,12 @@ class MenuComposer
|
||||
new Collection();
|
||||
$vars['show_vmwinfo'] = Vminfo::hasAccess($user)->exists();
|
||||
|
||||
//Maps
|
||||
$vars['links'] = Link::exists();
|
||||
$vars['device_dependencies'] = \DB::table('device_relationships')->exists();
|
||||
$vars['device_group_dependencies'] = $vars['device_groups']->isNotEmpty() && \DB::table('device_group_device')->exists();
|
||||
$vars['custommaps'] = CustomMap::select(['custom_map_id', 'name', 'menu_group'])->hasAccess($user)->orderBy('name')->get()->groupBy('menu_group')->sortKeys();
|
||||
|
||||
// Service menu
|
||||
if (Config::get('show_services')) {
|
||||
$vars['service_counts'] = ObjectCache::serviceCounts(['warning', 'critical']);
|
||||
|
@@ -41,6 +41,7 @@ class CustomMap extends BaseModel
|
||||
];
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'menu_group',
|
||||
'width',
|
||||
'height',
|
||||
'node_align',
|
||||
|
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('custom_maps', function (Blueprint $table) {
|
||||
$table->string('menu_group', 100)->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('custom_maps', function (Blueprint $table) {
|
||||
$table->dropColumn('menu_group');
|
||||
});
|
||||
}
|
||||
};
|
@@ -467,7 +467,6 @@ function init_select2(selector, type, data, selected, placeholder, config) {
|
||||
$select.select2(init);
|
||||
|
||||
if (selected) {
|
||||
console.log(selected);
|
||||
if (typeof selected !== 'object') {
|
||||
selected = {id: selected, text: selected};
|
||||
}
|
||||
|
@@ -33,6 +33,8 @@ return [
|
||||
'map' => [
|
||||
'settings_title' => 'Map Settings',
|
||||
'name' => 'Name',
|
||||
'menu_group' => 'Menu Group',
|
||||
'no_group' => 'No Group',
|
||||
'width' => 'Width',
|
||||
'height' => 'Height',
|
||||
'alignment' => 'Node Alignment',
|
||||
|
@@ -537,6 +537,7 @@ custom_maps:
|
||||
- { Field: newedgeconfig, Type: longtext, 'Null': false, Extra: '' }
|
||||
- { Field: created_at, Type: timestamp, 'Null': true, Extra: '' }
|
||||
- { Field: updated_at, Type: timestamp, 'Null': true, Extra: '' }
|
||||
- { Field: menu_group, Type: varchar(100), 'Null': true, Extra: '' }
|
||||
Indexes:
|
||||
PRIMARY: { Name: PRIMARY, Columns: [custom_map_id], Unique: true, Type: BTREE }
|
||||
custom_map_backgrounds:
|
||||
|
@@ -41,67 +41,6 @@
|
||||
</ul>
|
||||
</li>
|
||||
<li role="presentation" class="divider"></li>
|
||||
<li class="dropdown-submenu">
|
||||
<a><i class="fa fa-map fa-fw fa-lg"
|
||||
aria-hidden="true"></i> {{ __('Maps') }}</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{{ url('availability-map') }}"><i class="fa fa-arrow-circle-up fa-fw fa-lg"
|
||||
aria-hidden="true"></i> {{ __('Availability') }}
|
||||
</a></li>
|
||||
@if($device_dependencies)
|
||||
<li><a href="{{ url('maps/devicedependency') }}"><i class="fa fa-chain fa-fw fa-lg"
|
||||
aria-hidden="true"></i> {{ __('Device Dependency') }}</a></li>
|
||||
@endif
|
||||
@if($device_groups->isNotEmpty() && $device_group_dependencies)
|
||||
<li class="dropdown-submenu"><a><i class="fa fa-chain fa-fw fa-lg"
|
||||
aria-hidden="true"></i> {{ __('Device Groups Dependencies') }}
|
||||
</a>
|
||||
<ul class="dropdown-menu scrollable-menu">
|
||||
@foreach($device_groups as $group)
|
||||
<li><a href="{{ url("maps/devicedependency?group=$group->id") }}" title="{{ $group->desc }}"><i class="fa fa-chain fa-fw fa-lg" aria-hidden="true"></i>
|
||||
{{ ucfirst($group->name) }}
|
||||
</a></li>
|
||||
@endforeach
|
||||
</ul></li>
|
||||
@endif
|
||||
@if($links)
|
||||
<li><a href="{{ url('map') }}"><i class="fa fa-sitemap fa-fw fa-lg"
|
||||
aria-hidden="true"></i> {{ __('Network') }}</a></li>
|
||||
@endif
|
||||
@if($device_groups->isNotEmpty())
|
||||
<li class="dropdown-submenu"><a><i class="fa fa-th fa-fw fa-lg"
|
||||
aria-hidden="true"></i> {{ __('Device Groups Maps') }}
|
||||
</a>
|
||||
<ul class="dropdown-menu scrollable-menu">
|
||||
@foreach($device_groups as $group)
|
||||
<li><a href="{{ url("map/group=$group->id") }}" title="{{ $group->desc }}"><i class="fa fa-th fa-fw fa-lg" aria-hidden="true"></i>
|
||||
{{ ucfirst($group->name) }}
|
||||
</a></li>
|
||||
@endforeach
|
||||
</ul></li>
|
||||
@endif
|
||||
@admin
|
||||
<li><a href="{{ route('maps.custom.index') }}"><i class="fa fa-map-marked fa-fw fa-lg"
|
||||
aria-hidden="true"></i> {{ __('Custom Map Editor') }}
|
||||
</a></li>
|
||||
@endadmin
|
||||
@if($custommaps->isNotEmpty())
|
||||
<li class="dropdown-submenu"><a><i class="fa fa-map-marked fa-fw fa-lg"
|
||||
aria-hidden="true"></i> {{ __('Custom Maps') }}
|
||||
</a>
|
||||
<ul class="dropdown-menu scrollable-menu">
|
||||
@foreach($custommaps as $map)
|
||||
<li><a href="{{ route('maps.custom.show', ['map' => $map->custom_map_id]) }}" title="{{ $map->name }}"><i class="fa fa-map-marked fa-fw fa-lg" aria-hidden="true"></i>
|
||||
{{ ucfirst($map->name) }}
|
||||
</a></li>
|
||||
@endforeach
|
||||
</ul></li>
|
||||
@endif
|
||||
<li><a href="{{ url('fullscreenmap') }}"><i class="fa fa-expand fa-fw fa-lg"
|
||||
aria-hidden="true"></i> {{ __('Geographical') }}
|
||||
</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
@if(auth()->user()->isAdmin() || $has_v1_plugins || $has_v2_plugins)
|
||||
<li class="dropdown-submenu">
|
||||
<a><i class="fa fa-plug fa-fw fa-lg" aria-hidden="true"></i> {{ __('Plugins') }}</a>
|
||||
@@ -244,6 +183,79 @@
|
||||
aria-hidden="true"></i> {{ __('Delete Device') }}</a></li>
|
||||
@endadmin
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
{{-- Maps --}}
|
||||
<li class="dropdown">
|
||||
<a href="{{ url('services') }}" class="dropdown-toggle" data-hover="dropdown"
|
||||
data-toggle="dropdown"><i class="fa fa-map fa-fw fa-lg fa-nav-icons hidden-md"
|
||||
aria-hidden="true"></i> <span
|
||||
class="hidden-sm">{{ __('Maps') }}</span></a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="{{ url('availability-map') }}"><i class="fa fa-arrow-circle-up fa-fw fa-lg"
|
||||
aria-hidden="true"></i> {{ __('Availability') }}
|
||||
</a></li>
|
||||
@if($device_dependencies)
|
||||
<li><a href="{{ url('maps/devicedependency') }}"><i class="fa fa-chain fa-fw fa-lg"
|
||||
aria-hidden="true"></i> {{ __('Device Dependency') }}</a></li>
|
||||
@endif
|
||||
@if($device_group_dependencies)
|
||||
<li class="dropdown-submenu"><a><i class="fa fa-chain fa-fw fa-lg"
|
||||
aria-hidden="true"></i> {{ __('Device Groups Dependencies') }}
|
||||
</a>
|
||||
<ul class="dropdown-menu scrollable-menu">
|
||||
@foreach($device_groups as $group)
|
||||
<li><a href="{{ url("maps/devicedependency?group=$group->id") }}" title="{{ $group->desc }}"><i class="fa fa-chain fa-fw fa-lg" aria-hidden="true"></i>
|
||||
{{ ucfirst($group->name) }}
|
||||
</a></li>
|
||||
@endforeach
|
||||
</ul></li>
|
||||
@endif
|
||||
@if($links)
|
||||
<li><a href="{{ url('map') }}"><i class="fa fa-sitemap fa-fw fa-lg"
|
||||
aria-hidden="true"></i> {{ __('Network') }}</a></li>
|
||||
@endif
|
||||
<li><a href="{{ url('fullscreenmap') }}"><i class="fa fa-expand fa-fw fa-lg"
|
||||
aria-hidden="true"></i> {{ __('Geographical') }}
|
||||
</a></li>
|
||||
@if($device_groups->isNotEmpty())
|
||||
<li class="dropdown-submenu"><a><i class="fa fa-th fa-fw fa-lg"
|
||||
aria-hidden="true"></i> {{ __('Device Groups Maps') }}
|
||||
</a>
|
||||
<ul class="dropdown-menu scrollable-menu">
|
||||
@foreach($device_groups as $group)
|
||||
<li><a href="{{ url("map/group=$group->id") }}" title="{{ $group->desc }}"><i class="fa fa-th fa-fw fa-lg" aria-hidden="true"></i>
|
||||
{{ ucfirst($group->name) }}
|
||||
</a></li>
|
||||
@endforeach
|
||||
</ul></li>
|
||||
@endif
|
||||
|
||||
@if($custommaps->isNotEmpty())
|
||||
<li role="presentation" class="divider"></li>
|
||||
@foreach($custommaps as $map_group => $group_maps)
|
||||
@if($map_group)
|
||||
<li class="dropdown-submenu">
|
||||
<a><i class="fa fa-map-marked fa-fw fa-lg"aria-hidden="true"></i> {{ $map_group }}
|
||||
</a>
|
||||
<ul class="dropdown-menu scrollable-menu">
|
||||
@endif
|
||||
@foreach($group_maps as $map)
|
||||
<li><a href="{{ route('maps.custom.show', ['map' => $map->custom_map_id]) }}"><i class="fa fa-map-marked fa-fw fa-lg" aria-hidden="true"></i>
|
||||
{{ ucfirst($map->name) }}
|
||||
</a></li>
|
||||
@endforeach
|
||||
@if($map_group)</ul>@endif
|
||||
@endforeach
|
||||
</li>
|
||||
@endif
|
||||
@admin
|
||||
<li role="presentation" class="divider"></li>
|
||||
<li><a href="{{ route('maps.custom.index') }}"><i class="fa fa-map-marked fa-fw fa-lg"
|
||||
aria-hidden="true"></i> {{ __('Custom Map Editor') }}
|
||||
</a></li>
|
||||
@endadmin
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
{{-- Services --}}
|
||||
|
@@ -1,4 +1,4 @@
|
||||
<div class="modal fade" id="mapModal" tabindex="-1" role="dialog" aria-labelledby="mapModalLabel" aria-hidden="true">
|
||||
<div class="modal fade" id="mapModal" role="dialog" aria-labelledby="mapModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
@@ -15,6 +15,12 @@
|
||||
<input type="text" id="mapname" name="mapname" class="form-control input-sm" value="{{ $name ?? '' }}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="mapmenugroup" class="col-sm-3 control-label">{{ __('map.custom.edit.map.menu_group') }}</label>
|
||||
<div class="col-sm-9">
|
||||
<select id="mapmenugroup" name="mapmenugroup" class="form-control input-sm"></select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="mapwidth" class="col-sm-3 control-label">{{ __('map.custom.edit.map.width') }}</label>
|
||||
<div class="col-sm-9">
|
||||
@@ -106,6 +112,7 @@
|
||||
$("#savemap-alert").attr("class", "col-sm-12 alert alert-info");
|
||||
|
||||
var name = $("#mapname").val();
|
||||
var group = $("#mapmenugroup").val();
|
||||
var width = $("#mapwidth").val();
|
||||
var height = $("#mapheight").val();
|
||||
var node_align = $("#mapnodealign").val();
|
||||
@@ -157,6 +164,7 @@
|
||||
url: url,
|
||||
data: {
|
||||
name: name,
|
||||
menu_group: group,
|
||||
width: width,
|
||||
height: height,
|
||||
node_align: node_align,
|
||||
@@ -205,5 +213,20 @@
|
||||
$("#maplegend").bootstrapSwitch('state', (legend.x >= 0 && legend.y >= 0));
|
||||
$("#maplegendhideinvalid").bootstrapSwitch('state', Boolean(legend.hide_invalid));
|
||||
$("#maplegendhideoverspeed").bootstrapSwitch('state', Boolean(legend.hide_overspeed));
|
||||
init_select2("#mapmenugroup", "custom-map-menu-group", {}, @json($menu_group ?? null), "{{ __('map.custom.edit.map.no_group') }}", {
|
||||
tags: true,
|
||||
createTag: function (params) {
|
||||
var term = $.trim(params.term);
|
||||
|
||||
if (term === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
id: term,
|
||||
text: term
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
@@ -171,6 +171,7 @@ Route::middleware(['auth'])->group(function () {
|
||||
Route::prefix('select')->namespace('Select')->group(function () {
|
||||
Route::get('application', 'ApplicationController')->name('ajax.select.application');
|
||||
Route::get('bill', 'BillController')->name('ajax.select.bill');
|
||||
Route::get('custom-map-menu-group', 'CustomMapMenuGroupController')->name('ajax.select.custom-map-menu-group');
|
||||
Route::get('dashboard', 'DashboardController')->name('ajax.select.dashboard');
|
||||
Route::get('device', 'DeviceController')->name('ajax.select.device');
|
||||
Route::get('device-field', 'DeviceFieldController')->name('ajax.select.device-field');
|
||||
|
Reference in New Issue
Block a user