diff --git a/app/Http/Controllers/InventoryController.php b/app/Http/Controllers/InventoryController.php new file mode 100644 index 000000000000..1328b9c6250f --- /dev/null +++ b/app/Http/Controllers/InventoryController.php @@ -0,0 +1,50 @@ +validate($request, [ + 'device' => 'nullable|int', + 'descr' => 'nullable|string', + 'model' => 'nullable|string', + 'serial' => 'nullable|string', + ]); + + $device = \App\Models\Device::hasAccess($request->user()) + ->select(['device_id', 'hostname', 'ip', 'sysName', 'display']) + ->firstWhere('device_id', $request->get('device')); + + $model_filter = ['field' => 'model']; + $device_selected = ''; + if ($device) { + $device_selected = ['id' => $device->device_id, 'text' => $device->displayName()]; + $model_filter['device_id'] = $device->device_id; + } + + return view('inventory', [ + 'device_selected' => $device_selected, + 'filter' => [ + 'device' => $device?->device_id, + 'descr' => $request->get('descr'), + 'model' => $request->get('model'), + 'serial' => $request->get('serial'), + ], + 'model_filter' => $model_filter, + 'show_purge' => EntPhysical::whereDoesntHave('device')->exists(), + ]); + } + + public function purge() + { + EntPhysical::whereDoesntHave('device')->delete(); + + return redirect()->back(); + } +} diff --git a/app/Http/Controllers/Select/InventoryController.php b/app/Http/Controllers/Select/InventoryController.php new file mode 100644 index 000000000000..a349e8889398 --- /dev/null +++ b/app/Http/Controllers/Select/InventoryController.php @@ -0,0 +1,68 @@ +. + * + * @link https://www.librenms.org + * + * @copyright 2023 Tony Murray + * @author Tony Murray + */ + +namespace App\Http\Controllers\Select; + +use App\Models\EntPhysical; + +class InventoryController extends SelectController +{ + protected function rules() + { + return [ + 'field' => 'required|in:name,model,descr,class', + 'device' => 'nullable|int', + ]; + } + + protected function filterFields($request) + { + return [ + 'device_id' => 'device', + ]; + } + protected function searchFields($request) + { + return [$this->fieldToColumn($request->get('field'))]; + } + + protected function baseQuery($request) + { + $column = $this->fieldToColumn($request->get('field')); + return EntPhysical::hasAccess($request->user()) + ->select($column) + ->orderBy($column) + ->distinct(); + } + + private function fieldToColumn($field) { + return match ($field) { + 'name' => 'entPhysicalName', + 'model' => 'entPhysicalModelName', + 'descr' => 'entPhysicalDescr', + 'class' => 'entPhysicalClass', + }; + } +} diff --git a/app/Http/Controllers/Select/PortFieldController.php b/app/Http/Controllers/Select/PortFieldController.php index a55b7e25985c..e18bd62ae081 100644 --- a/app/Http/Controllers/Select/PortFieldController.php +++ b/app/Http/Controllers/Select/PortFieldController.php @@ -42,6 +42,18 @@ protected function rules() ]; } + /** + * Defines fields that can be used as filters + * @param $request + * @return string[] + */ + protected function filterFields($request) + { + return [ + 'device_id' => 'device', + ]; + } + /** * Defines search fields will be searched in order * @@ -61,14 +73,7 @@ protected function searchFields($request) */ protected function baseQuery($request) { - /** @var \Illuminate\Database\Eloquent\Builder $query */ - $query = Port::hasAccess($request->user()) + return Port::hasAccess($request->user()) ->select($request->get('field'))->distinct(); - - if ($device_id = $request->get('device')) { - $query->where('ports.device_id', $device_id); - } - - return $query; } } diff --git a/app/Http/Controllers/Select/SelectController.php b/app/Http/Controllers/Select/SelectController.php index d05f5baf204e..b051f1e8dc06 100644 --- a/app/Http/Controllers/Select/SelectController.php +++ b/app/Http/Controllers/Select/SelectController.php @@ -57,7 +57,8 @@ public function __invoke(Request $request) $query = $this->baseQuery($request)->when($request->has('id'), function ($query) { return $query->whereKey(request('id')); }); - $query = $this->search($request->get('term'), $query, $this->searchFields($request)); + $this->filter($request, $query, $this->filterFields($request)); + $this->search($request->get('term'), $query, $this->searchFields($request)); $this->sort($request, $query); $paginator = $query->simplePaginate($limit); diff --git a/app/Http/Controllers/Table/InventoryController.php b/app/Http/Controllers/Table/InventoryController.php new file mode 100644 index 000000000000..0a54f415ad50 --- /dev/null +++ b/app/Http/Controllers/Table/InventoryController.php @@ -0,0 +1,96 @@ +. + * + * @link https://www.librenms.org + * + * @copyright 2023 Tony Murray + * @author Tony Murray + */ + +namespace App\Http\Controllers\Table; + +use App\Models\EntPhysical; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Collection; +use LibreNMS\Util\Url; + +class InventoryController extends TableController +{ + public function rules() + { + return [ + 'device' => 'nullable|int', + 'descr' => 'nullable|string', + 'model'=> 'nullable|string', + 'serial' => 'nullable|string', + ]; + } + + protected function filterFields($request) + { + return [ + 'device_id' => 'device', + ]; + } + + protected function searchFields($request) + { + return ['entPhysicalDescr', 'entPhysicalModelName', 'entPhysicalSerialNum']; + } + + protected function sortFields($request) + { + return [ + 'device' => 'device_id', + 'name' => 'entPhysicalName', + 'descr' => 'entPhysicalDescr', + 'model' => 'entPhysicalModelName', + 'serial' => 'entPhysicalSerialNum', + ]; + } + + protected function baseQuery($request) + { + $query = EntPhysical::hasAccess($request->user()) + ->with('device') + ->select(['entPhysical_id', 'device_id', 'entPhysicalDescr', 'entPhysicalName', 'entPhysicalModelName', 'entPhysicalSerialNum']); + + // apply specific field filters + $this->search($request->get('descr'), $query, ['entPhysicalDescr']); + $this->search($request->get('model'), $query, ['entPhysicalModelName']); + $this->search($request->get('serial'), $query, ['entPhysicalSerialNum']); + + return $query; + } + + /** + * @param EntPhysical $entPhysical + * @return array|Model|Collection + */ + public function formatItem($entPhysical) + { + return [ + 'device' => Url::deviceLink($entPhysical?->device), + 'descr' => htmlspecialchars($entPhysical->entPhysicalDescr), + 'name' => htmlspecialchars($entPhysical->entPhysicalName), + 'model' => htmlspecialchars($entPhysical->entPhysicalModelName), + 'serial' => htmlspecialchars($entPhysical->entPhysicalSerialNum), + ]; + } +} diff --git a/app/Http/Controllers/Table/TableController.php b/app/Http/Controllers/Table/TableController.php index d2bd5552ad2e..dec505aa52dd 100644 --- a/app/Http/Controllers/Table/TableController.php +++ b/app/Http/Controllers/Table/TableController.php @@ -61,8 +61,8 @@ public function __invoke(Request $request) /** @var Builder $query */ $query = $this->baseQuery($request); - $this->search($request->get('searchPhrase'), $query, $this->searchFields($request)); $this->filter($request, $query, $this->filterFields($request)); + $this->search($request->get('searchPhrase'), $query, $this->searchFields($request)); $this->sort($request, $query); $limit = $request->get('rowCount', 25); diff --git a/html/css/app.css b/html/css/app.css index 646095b1ad28..c0832b768fe6 100644 --- a/html/css/app.css +++ b/html/css/app.css @@ -1,2 +1,2 @@ #flasher-container-top-right{position:fixed;right:12px;top:55px;z-index:999999}#flasher-container-top-right a{font-weight:700}#flasher-container-top-right>div{background-position:10px;background-repeat:no-repeat;min-height:50px;width:304px}.flasher-error{background-image:url("data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMzIgMzIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGNpcmNsZSBjeD0iMTYiIGN5PSIxNiIgcj0iMTUiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJNMTYgMEExNiAxNiAwIDAgMCAwIDE2YTE2IDE2IDAgMCAwIDE2IDE2IDE2IDE2IDAgMCAwIDE2LTE2QTE2IDE2IDAgMCAwIDE2IDB6bS02IDlhMSAxIDAgMCAxIC43MDcuMjkzTDE2IDE0LjU4Nmw1LjI5My01LjI5M2ExIDEgMCAwIDEgMS40MTQgMCAxIDEgMCAwIDEgMCAxLjQxNEwxNy40MTQgMTZsNS4yOTMgNS4yOTNhMSAxIDAgMCAxIDAgMS40MTQgMSAxIDAgMCAxLTEuNDE0IDBMMTYgMTcuNDE0bC01LjI5MyA1LjI5M2ExIDEgMCAwIDEtMS40MTQgMCAxIDEgMCAwIDEgMC0xLjQxNEwxNC41ODYgMTZsLTUuMjkzLTUuMjkzYTEgMSAwIDAgMSAwLTEuNDE0QTEgMSAwIDAgMSAxMCA5eiIgZmlsbD0iI2RjMjYyNiIvPjwvc3ZnPg==");background-size:32px}.flasher-info{background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiI+PGNpcmNsZSBjeD0iMTYiIGN5PSIxNiIgcj0iMTUiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJNMTYgMEExNiAxNiAwIDAgMCAwIDE2YTE2IDE2IDAgMCAwIDE2IDE2IDE2IDE2IDAgMCAwIDE2LTE2QTE2IDE2IDAgMCAwIDE2IDB6bTAgNmM1LjUxMSAwIDEwIDQuNDg5IDEwIDEwcy00LjQ4OSAxMC0xMCAxMFM2IDIxLjUxMSA2IDE2IDEwLjQ4OSA2IDE2IDZ6bTAgMmMtNC40MyAwLTggMy41Ny04IDhzMy41NyA4IDggOCA4LTMuNTcgOC04LTMuNTctOC04LTh6bTAgM2ExIDEgMCAwIDEgMSAxdjRhMSAxIDAgMCAxLTEgMSAxIDEgMCAwIDEtMS0xdi00YTEgMSAwIDAgMSAxLTF6bTAgOGguMDFhMSAxIDAgMCAxIDEgMSAxIDEgMCAwIDEtMSAxSDE2YTEgMSAwIDAgMS0xLTEgMSAxIDAgMCAxIDEtMXoiIGZpbGw9IiMyNTYzZWIiLz48L3N2Zz4=");background-size:32px}.flasher-success{background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiI+PGNpcmNsZSBjeD0iMTYiIGN5PSIxNiIgcj0iMTUiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJNMTYgMEExNiAxNiAwIDAgMCAwIDE2YTE2IDE2IDAgMCAwIDE2IDE2IDE2IDE2IDAgMCAwIDE2LTE2QTE2IDE2IDAgMCAwIDE2IDB6bTcgMTBhMSAxIDAgMCAxIC43MDcuMjkzIDEgMSAwIDAgMSAwIDEuNDE0bC0xMCAxMGExIDEgMCAwIDEtMS40MTQgMGwtNC00YTEgMSAwIDAgMSAwLTEuNDE0IDEgMSAwIDAgMSAxLjQxNCAwTDEzIDE5LjU4Nmw5LjI5My05LjI5M0ExIDEgMCAwIDEgMjMgMTB6IiBmaWxsPSIjMDU5NjY5Ii8+PC9zdmc+");background-size:32px}.flasher-warning{background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiI+PGNpcmNsZSBjeD0iMTYiIGN5PSIxNiIgcj0iMTUiIGZpbGw9IiNmZmYiLz48cGF0aCBkPSJNMTYgMEExNiAxNiAwIDAgMCAwIDE2YTE2IDE2IDAgMCAwIDE2IDE2IDE2IDE2IDAgMCAwIDE2LTE2QTE2IDE2IDAgMCAwIDE2IDB6bTAgNi4xNTZjMS4wMTYgMCAyLjAzMi40OSAyLjU5OCAxLjQ2OWw2LjkyNyAxMmMxLjEzMSAxLjk1OC0uMzM2IDQuNS0yLjU5NyA0LjVIOS4wNzJjLTIuMjYxIDAtMy43MjgtMi41NDItMi41OTctNC41bDYuOTI3LTEyYy41NjYtLjk3OSAxLjU4Mi0xLjQ2OSAyLjU5OC0xLjQ2OXptMCAxLjkzOGMtLjMzIDAtLjY2LjE3Ny0uODY1LjUzMWwtNi45MyAxMmMtLjQwOS43MDguMDQ5IDEuNS44NjcgMS41aDEzLjg1NmMuODE4IDAgMS4yNzYtLjc5Mi44NjctMS41bC02LjkzLTEyYy0uMjA0LS4zNTQtLjUzNC0uNTMxLS44NjUtLjUzMXptMCA0LjAzMWExIDEgMCAwIDEgMSAxdjJhMSAxIDAgMCAxLTEgMSAxIDEgMCAwIDEtMS0xdi0yYTEgMSAwIDAgMSAxLTF6bTAgNmguMDFhMSAxIDAgMCAxIDEgMSAxIDEgMCAwIDEtMSAxSDE2YTEgMSAwIDAgMS0xLTEgMSAxIDAgMCAxIDEtMXoiIGZpbGw9IiNkOTc3MDYiLz48L3N2Zz4=");background-size:32px} -/*! tailwindcss v3.0.15 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}html{-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-size:100%;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}*,:after,:before{--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246/0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.tw-z-50{z-index:50}.tw-float-right{float:right}.tw-mx-10{margin-left:2.5rem;margin-right:2.5rem}.tw-mt-2{margin-top:.5rem}.tw-mt-1{margin-top:.25rem}.tw-mr-0\.5{margin-right:.125rem}.tw-mr-0{margin-right:0}.tw-mt-5{margin-top:1.25rem}.tw-ml-3{margin-left:.75rem}.tw-mb-0{margin-bottom:0}.tw-block{display:block}.tw-inline-block{display:inline-block}.tw-flex{display:flex}.tw-inline-flex{display:inline-flex}.tw-table{display:table}.tw-grid{display:grid}.tw-hidden{display:none}.tw-h-1{height:.25rem}.tw-w-full{width:100%}.tw-cursor-pointer{cursor:pointer}.tw-flex-col{flex-direction:column}.tw-flex-wrap{flex-wrap:wrap}.tw-place-items-center{place-items:center}.tw-justify-between{justify-content:space-between}.tw-overflow-hidden{overflow:hidden}.tw-rounded-md{border-radius:.375rem}.tw-rounded{border-radius:.25rem}.tw-rounded-lg{border-radius:.5rem}.tw-rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.tw-border{border-width:1px}.tw-border-2{border-width:2px}.tw-border-l-8{border-left-width:8px}.tw-border-t-0\.5{border-top-width:.5px}.tw-border-r-0\.5{border-right-width:.5px}.tw-border-b-0\.5{border-bottom-width:.5px}.tw-border-t-0{border-top-width:0}.tw-border-r-0{border-right-width:0}.tw-border-b-0{border-bottom-width:0}.tw-border-b{border-bottom-width:1px}.tw-border-b-2{border-bottom-width:2px}.tw-border-solid{border-style:solid}.tw-border-current{border-color:currentColor}.tw-border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}.tw-border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity))}.tw-bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.tw-bg-current{background-color:currentColor}.tw-p-3{padding:.75rem}.tw-py-4{padding-bottom:1rem;padding-top:1rem}.tw-px-4{padding-left:1rem;padding-right:1rem}.tw-py-2{padding-bottom:.5rem;padding-top:.5rem}.tw-pl-20{padding-left:5rem}.tw-pr-2{padding-right:.5rem}.tw-pr-1{padding-right:.25rem}.tw-pl-2{padding-left:.5rem}.tw-text-left{text-align:left}.tw-text-xl{font-size:1.25rem;line-height:1.75rem}.tw-text-base{font-size:1rem;line-height:1.5rem}.tw-text-sm{font-size:.875rem;line-height:1.25rem}.tw-font-semibold{font-weight:600}.tw-font-bold{font-weight:700}.tw-font-normal{font-weight:400}.tw-capitalize{text-transform:capitalize}.tw-leading-7{line-height:1.75rem}.tw-leading-5{line-height:1.25rem}.tw-leading-normal{line-height:1.5}.tw-text-emerald-600{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity))}.tw-text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity))}.tw-text-amber-600{--tw-text-opacity:1;color:rgb(217 119 6/var(--tw-text-opacity))}.tw-text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity))}.tw-text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.tw-text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.tw-text-blue-900{--tw-text-opacity:1;color:rgb(30 58 138/var(--tw-text-opacity))}.tw-no-underline{-webkit-text-decoration-line:none;text-decoration-line:none}.tw-opacity-80{opacity:.8}.tw-opacity-90{opacity:.9}.tw-shadow-lg{--tw-shadow:0 10px 15px -3px rgb(0 0 0/0.1),0 4px 6px -4px rgb(0 0 0/0.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.visited\:tw-text-gray-400:visited{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.visited\:tw-text-red-600:visited{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity))}.visited\:tw-text-blue-900:visited{--tw-text-opacity:1;color:rgb(30 58 138/var(--tw-text-opacity))}.hover\:tw-bg-gray-500:hover{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity))}.hover\:tw-text-gray-100:hover{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}.hover\:tw-opacity-100:hover{opacity:1}.hover\:tw-shadow-xl:hover{--tw-shadow:0 20px 25px -5px rgb(0 0 0/0.1),0 8px 10px -6px rgb(0 0 0/0.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.tw-dark .dark\:tw-border-dark-gray-200{--tw-border-opacity:1;border-color:rgb(62 68 76/var(--tw-border-opacity))}.tw-dark .dark\:tw-bg-dark-gray-300{--tw-bg-opacity:1;background-color:rgb(53 58 65/var(--tw-bg-opacity))}.tw-dark .dark\:tw-text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.tw-dark .dark\:tw-text-dark-white-100,.tw-dark .dark\:visited\:tw-text-dark-white-100:visited{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}@media (min-width:576px){.sm\:tw-w-1\/2{width:50%}}@media (min-width:992px){.lg\:tw-w-1\/4{width:25%}} +/*! tailwindcss v3.0.15 | MIT License | https://tailwindcss.com*/*,:after,:before{border:0 solid #e5e7eb;box-sizing:border-box}:after,:before{--tw-content:""}html{-webkit-text-size-adjust:100%;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{line-height:inherit;margin:0}hr{border-top-width:1px;color:inherit;height:0}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-collapse:collapse;border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{color:inherit;font-family:inherit;font-size:100%;line-height:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}[hidden]{display:none}*,:after,:before{--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246/0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.tw-relative{position:relative}.tw-z-50{z-index:50}.tw-float-right{float:right}.tw-mx-10{margin-left:2.5rem;margin-right:2.5rem}.tw-mr-3{margin-right:.75rem}.tw-mt-2{margin-top:.5rem}.tw-mr-1{margin-right:.25rem}.tw-ml-2{margin-left:.5rem}.tw-mr-2{margin-right:.5rem}.tw-ml-auto{margin-left:auto}.tw-mt-1{margin-top:.25rem}.tw-mr-0\.5{margin-right:.125rem}.tw-mr-0{margin-right:0}.tw-ml-3{margin-left:.75rem}.tw-mb-0{margin-bottom:0}.tw-mb-2{margin-bottom:.5rem}.tw-mt-5{margin-top:1.25rem}.tw-block{display:block}.tw-inline-block{display:inline-block}.tw-flex{display:flex}.tw-inline-flex{display:inline-flex}.tw-grid{display:grid}.tw-hidden{display:none}.tw-h-1{height:.25rem}.tw-w-full{width:100%}.tw-cursor-pointer{cursor:pointer}.tw-flex-col{flex-direction:column}.tw-flex-wrap{flex-wrap:wrap}.tw-place-items-center{place-items:center}.tw-items-center{align-items:center}.tw-items-baseline{align-items:baseline}.tw-justify-between{justify-content:space-between}.tw-overflow-hidden{overflow:hidden}.tw-rounded-md{border-radius:.375rem}.tw-rounded-lg{border-radius:.5rem}.tw-rounded{border-radius:.25rem}.tw-rounded-t-lg{border-top-left-radius:.5rem;border-top-right-radius:.5rem}.tw-border-2{border-width:2px}.tw-border{border-width:1px}.tw-border-l-8{border-left-width:8px}.tw-border-t-0\.5{border-top-width:.5px}.tw-border-r-0\.5{border-right-width:.5px}.tw-border-b-0\.5{border-bottom-width:.5px}.tw-border-t-0{border-top-width:0}.tw-border-r-0{border-right-width:0}.tw-border-b-0{border-bottom-width:0}.tw-border-b-2{border-bottom-width:2px}.tw-border-b{border-bottom-width:1px}.tw-border-solid{border-style:solid}.tw-border-current{border-color:currentColor}.tw-border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity))}.tw-border-gray-500{--tw-border-opacity:1;border-color:rgb(107 114 128/var(--tw-border-opacity))}.tw-bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity))}.tw-bg-current{background-color:currentColor}.\!tw-p-0{padding:0!important}.tw-p-3{padding:.75rem}.tw-py-4{padding-bottom:1rem;padding-top:1rem}.tw-px-4{padding-left:1rem;padding-right:1rem}.tw-py-2{padding-bottom:.5rem;padding-top:.5rem}.tw-pl-20{padding-left:5rem}.tw-pr-2{padding-right:.5rem}.tw-pr-1{padding-right:.25rem}.tw-pl-2{padding-left:.5rem}.tw-text-left{text-align:left}.tw-text-xl{font-size:1.25rem;line-height:1.75rem}.tw-text-base{font-size:1rem;line-height:1.5rem}.tw-text-sm{font-size:.875rem;line-height:1.25rem}.tw-font-bold{font-weight:700}.tw-font-semibold{font-weight:600}.tw-font-normal{font-weight:400}.tw-capitalize{text-transform:capitalize}.tw-leading-7{line-height:1.75rem}.tw-leading-5{line-height:1.25rem}.tw-leading-normal{line-height:1.5}.tw-text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.tw-text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity))}.tw-text-blue-900{--tw-text-opacity:1;color:rgb(30 58 138/var(--tw-text-opacity))}.tw-text-emerald-600{--tw-text-opacity:1;color:rgb(5 150 105/var(--tw-text-opacity))}.tw-text-amber-600{--tw-text-opacity:1;color:rgb(217 119 6/var(--tw-text-opacity))}.tw-text-blue-600{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity))}.tw-text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity))}.tw-no-underline{-webkit-text-decoration-line:none;text-decoration-line:none}.tw-opacity-80{opacity:.8}.tw-opacity-90{opacity:.9}.tw-shadow-lg{--tw-shadow:0 10px 15px -3px rgb(0 0 0/0.1),0 4px 6px -4px rgb(0 0 0/0.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.visited\:tw-text-gray-400:visited{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity))}.visited\:tw-text-red-600:visited{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity))}.visited\:tw-text-blue-900:visited{--tw-text-opacity:1;color:rgb(30 58 138/var(--tw-text-opacity))}.hover\:tw-bg-gray-500:hover{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity))}.hover\:tw-text-gray-100:hover{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity))}.hover\:tw-opacity-100:hover{opacity:1}.hover\:tw-shadow-xl:hover{--tw-shadow:0 20px 25px -5px rgb(0 0 0/0.1),0 8px 10px -6px rgb(0 0 0/0.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.tw-dark .dark\:tw-border-dark-gray-200{--tw-border-opacity:1;border-color:rgb(62 68 76/var(--tw-border-opacity))}.tw-dark .dark\:tw-bg-dark-gray-300{--tw-bg-opacity:1;background-color:rgb(53 58 65/var(--tw-bg-opacity))}.tw-dark .dark\:tw-text-dark-white-100{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}.tw-dark .dark\:tw-text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity))}.tw-dark .dark\:visited\:tw-text-dark-white-100:visited{--tw-text-opacity:1;color:rgb(249 250 251/var(--tw-text-opacity))}@media (min-width:576px){.sm\:tw-w-1\/2{width:50%}}@media (min-width:992px){.lg\:tw-w-1\/4{width:25%}} diff --git a/html/mix-manifest.json b/html/mix-manifest.json index 82e2263402ac..f254d555b74b 100644 --- a/html/mix-manifest.json +++ b/html/mix-manifest.json @@ -2,15 +2,15 @@ "/js/app.js": "/js/app.js?id=5ddec7f7302f146a8dcc", "/js/manifest.js": "/js/manifest.js?id=2951ae529be231f05a93", "/css/vendor.css": "/css/vendor.css?id=2568831af31dbfc3128a", - "/css/app.css": "/css/app.css?id=bd093a6a2e2682bb59ef", + "/css/app.css": "/css/app.css?id=1cd88608bf4eaee000d8", "/js/vendor.js": "/js/vendor.js?id=c5fd3d75a63757080dbb", - "/js/lang/de.js": "/js/lang/de.js?id=613b5ca9cd06ca15e384", - "/js/lang/en.js": "/js/lang/en.js?id=efa23897934359283288", - "/js/lang/fr.js": "/js/lang/fr.js?id=4540d71a19d8ca7c824b", - "/js/lang/it.js": "/js/lang/it.js?id=71c68fae57a4a3647e43", + "/js/lang/de.js": "/js/lang/de.js?id=d74df23e729c5dabfee8", + "/js/lang/en.js": "/js/lang/en.js?id=20e52084af3a0a8f4724", + "/js/lang/fr.js": "/js/lang/fr.js?id=22902d30358443ef2877", + "/js/lang/it.js": "/js/lang/it.js?id=6220e138068a7e58387f", "/js/lang/ru.js": "/js/lang/ru.js?id=f6b7c078755312a0907c", "/js/lang/sr.js": "/js/lang/sr.js?id=388e38b41f63e3517506", - "/js/lang/uk.js": "/js/lang/uk.js?id=72f81fcbf77df09d0c82", - "/js/lang/zh-CN.js": "/js/lang/zh-CN.js?id=4e081fbac70d969894bf", - "/js/lang/zh-TW.js": "/js/lang/zh-TW.js?id=ed26425647721a42ee9d" + "/js/lang/uk.js": "/js/lang/uk.js?id=fdfb4cfa77a3340e50f8", + "/js/lang/zh-CN.js": "/js/lang/zh-CN.js?id=cc4309e63a32a671f107", + "/js/lang/zh-TW.js": "/js/lang/zh-TW.js?id=2248687ad44f27299377" } diff --git a/includes/html/pages/inventory.inc.php b/includes/html/pages/inventory.inc.php deleted file mode 100644 index 125d9df6f249..000000000000 --- a/includes/html/pages/inventory.inc.php +++ /dev/null @@ -1,100 +0,0 @@ - - -
-
- Inventory -
- - - - - - - - - - -
HostnameDescriptionNamePart NoSerial No
-
- - diff --git a/includes/html/table/inventory.inc.php b/includes/html/table/inventory.inc.php deleted file mode 100644 index 9655297ad353..000000000000 --- a/includes/html/table/inventory.inc.php +++ /dev/null @@ -1,86 +0,0 @@ -hasGlobalRead()) { - $device_ids = Permissions::devicesForUser()->toArray() ?: [0]; - $where .= ' AND `D`.`device_id` IN ' . dbGenPlaceholders(count($device_ids)); - $param = array_merge($param, $device_ids); -} - -$sql = " FROM entPhysical AS E, devices AS D WHERE $where AND D.device_id = E.device_id"; - -if (isset($searchPhrase) && ! empty($searchPhrase)) { - $sql .= ' AND (`D`.`hostname` LIKE ? OR `E`.`entPhysicalDescr` LIKE ? OR `E`.`entPhysicalModelName` LIKE ? OR `E`.`entPhysicalSerialNum` LIKE ?)'; - $param[] = "%$searchPhrase%"; - $param[] = "%$searchPhrase%"; - $param[] = "%$searchPhrase%"; - $param[] = "%$searchPhrase%"; -} - -if (isset($vars['string']) && strlen($vars['string'])) { - $sql .= ' AND E.entPhysicalDescr LIKE ?'; - $param[] = '%' . $vars['string'] . '%'; -} - -if (isset($vars['device_string']) && strlen($vars['device_string'])) { - $sql .= ' AND D.hostname LIKE ?'; - $param[] = '%' . $vars['device_string'] . '%'; -} - -if (isset($vars['part']) && strlen($vars['part'])) { - $sql .= ' AND E.entPhysicalModelName = ?'; - $param[] = $vars['part']; -} - -if (isset($vars['serial']) && strlen($vars['serial'])) { - $sql .= ' AND E.entPhysicalSerialNum LIKE ?'; - $param[] = '%' . $vars['serial'] . '%'; -} - -if (isset($vars['device']) && is_numeric($vars['device'])) { - $sql .= ' AND D.device_id = ?'; - $param[] = $vars['device']; -} - -$count_sql = "SELECT COUNT(`entPhysical_id`) $sql"; -$total = dbFetchCell($count_sql, $param); -if (empty($total)) { - $total = 0; -} - -if (! isset($sort) || empty($sort)) { - $sort = '`hostname` DESC'; -} - -$sql .= " ORDER BY $sort"; - -if (isset($current)) { - $limit_low = (($current * $rowCount) - $rowCount); - $limit_high = $rowCount; -} - -if ($rowCount != -1) { - $sql .= " LIMIT $limit_low,$limit_high"; -} - -$sql = "SELECT `D`.`device_id` AS `device_id`, `D`.`os` AS `os`, `D`.`hostname` AS `hostname`, `D`.`sysName` AS `sysName`,`entPhysicalDescr` AS `description`, `entPhysicalName` AS `name`, `entPhysicalModelName` AS `model`, `entPhysicalSerialNum` AS `serial` $sql"; - -foreach (dbFetchRows($sql, $param) as $invent) { - $response[] = [ - 'hostname' => generate_device_link($invent), - 'description' => $invent['description'], - 'name' => $invent['name'], - 'model' => $invent['model'], - 'serial' => $invent['serial'], - ]; -} - -$output = [ - 'current' => $current, - 'rowCount' => $rowCount, - 'rows' => $response, - 'total' => $total, -]; -echo json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE); diff --git a/lang/en/inventory.php b/lang/en/inventory.php new file mode 100644 index 000000000000..388420582c2e --- /dev/null +++ b/lang/en/inventory.php @@ -0,0 +1,12 @@ + 'Purge Non-Existent', + 'name' => 'Part Name', + 'model' => 'Part No', + 'serial' => 'Serial No', + 'part' => 'Part', + 'all_parts' => 'All Parts', +]; + + diff --git a/resources/views/inventory.blade.php b/resources/views/inventory.blade.php new file mode 100644 index 000000000000..3f8395156513 --- /dev/null +++ b/resources/views/inventory.blade.php @@ -0,0 +1,86 @@ +@extends('layouts.librenmsv1') + +@section('title', __('Inventory')) + +@section('content') +
+ + +

@lang('Inventory')

+ @if($show_purge) + + @endif +
+ + + + + + + + + + + +
@lang('Device')@lang('Description')@lang('inventory.name')@lang('inventory.model')@lang('inventory.serial')
+
+
+@endsection + +@push('scripts') + +@endpush + +@push('styles') + +@endpush + diff --git a/resources/views/layouts/menu.blade.php b/resources/views/layouts/menu.blade.php index 275966b28af9..4516d24ccba0 100644 --- a/resources/views/layouts/menu.blade.php +++ b/resources/views/layouts/menu.blade.php @@ -133,7 +133,7 @@ class="hidden-sm">{{ __('Overview') }} aria-hidden="true"> {{ __('Graylog') }} @endconfig -
  • {{ __('Inventory') }}
  • {{ __('Outages') }}
  • @@ -762,7 +762,7 @@ function toggleDashboardEditor() { @endif $(document).ready(function(){ - // Function to focus Global Search on Ctrl-F + // Function to focus Global Search on Ctrl-F window.addEventListener("keydown",function (e) { if (e.keyCode === 114 || (e.ctrlKey && e.keyCode === 70)){ if($('#gsearch').is(":focus")) { @@ -775,7 +775,7 @@ function toggleDashboardEditor() { } } }) - + }) diff --git a/routes/web.php b/routes/web.php index 0fba360e84bb..a360bbf18b25 100644 --- a/routes/web.php +++ b/routes/web.php @@ -39,6 +39,8 @@ // pages Route::post('alert/{alert}/ack', [AlertController::class, 'ack'])->name('alert.ack'); Route::resource('device-groups', 'DeviceGroupController'); + Route::any('inventory', \App\Http\Controllers\InventoryController::class)->name('inventory'); + Route::get('inventory/purge', [\App\Http\Controllers\InventoryController::class, 'purge'])->name('inventory.purge'); Route::resource('port', 'PortController')->only('update'); Route::prefix('poller')->group(function () { Route::get('', 'PollerController@pollerTab')->name('poller.index'); @@ -166,6 +168,7 @@ Route::get('graph', 'GraphController')->name('ajax.select.graph'); Route::get('graph-aggregate', 'GraphAggregateController')->name('ajax.select.graph-aggregate'); Route::get('graylog-streams', 'GraylogStreamsController')->name('ajax.select.graylog-streams'); + Route::get('inventory', 'InventoryController')->name('ajax.select.inventory'); Route::get('syslog', 'SyslogController')->name('ajax.select.syslog'); Route::get('location', 'LocationController')->name('ajax.select.location'); Route::get('munin', 'MuninPluginController')->name('ajax.select.munin'); @@ -185,6 +188,7 @@ Route::post('eventlog', 'EventlogController'); Route::post('fdb-tables', 'FdbTablesController'); Route::post('graylog', 'GraylogController'); + Route::post('inventory', 'InventoryController')->name('table.inventory'); Route::post('location', 'LocationController'); Route::post('mempools', 'MempoolsController'); Route::post('outages', 'OutagesController');