Add abillity for multi and range-selection to events.js
Add the abillity to select multiple rows, with a multi-selection using the CTRL-key or a range-selection using the shift-key. Also fix several issues in the Multi-Controller of the Backend. refs #5765
This commit is contained in:
parent
6973b04211
commit
6d303f1c42
|
@ -31,12 +31,12 @@ use \Icinga\Web\Form;
|
|||
use \Icinga\Web\Controller\ActionController;
|
||||
use \Icinga\Web\Widget\Tabextension\OutputFormat;
|
||||
use \Icinga\Module\Monitoring\Backend;
|
||||
use \Icinga\Module\Monitoring\Object\Host;
|
||||
use \Icinga\Module\Monitoring\Object\Service;
|
||||
use \Icinga\Data\BaseQuery;
|
||||
use \Icinga\Module\Monitoring\Backend\Ido\Query\StatusQuery;
|
||||
use \Icinga\Module\Monitoring\Form\Command\MultiCommandFlagForm;
|
||||
use \Icinga\Module\Monitoring\DataView\HostStatus as HostStatusView;
|
||||
use \Icinga\Module\Monitoring\DataView\HostStatus as HostStatusView;
|
||||
use \Icinga\Module\Monitoring\DataView\ServiceStatus as ServiceStatusView;
|
||||
use \Icinga\Module\Monitoring\DataView\Comment as CommentView;
|
||||
use \Icinga\Module\Monitoring\DataView\Comment as CommentView;
|
||||
|
||||
/**
|
||||
* Displays aggregations collections of multiple objects.
|
||||
|
@ -45,7 +45,7 @@ class Monitoring_MultiController extends ActionController
|
|||
{
|
||||
public function init()
|
||||
{
|
||||
$this->view->queries = $this->getDetailQueries();
|
||||
$this->view->queries = $this->getAllParamsAsArray();
|
||||
$this->backend = Backend::createBackend($this->_getParam('backend'));
|
||||
$this->createTabs();
|
||||
}
|
||||
|
@ -53,10 +53,10 @@ class Monitoring_MultiController extends ActionController
|
|||
public function hostAction()
|
||||
{
|
||||
$filters = $this->view->queries;
|
||||
$errors = array();
|
||||
$errors = array();
|
||||
|
||||
// Hosts
|
||||
$backendQuery = HostStatusView::fromRequest(
|
||||
// Fetch Hosts
|
||||
$hostQuery = HostStatusView::fromRequest(
|
||||
$this->_request,
|
||||
array(
|
||||
'host_name',
|
||||
|
@ -71,51 +71,69 @@ class Monitoring_MultiController extends ActionController
|
|||
)
|
||||
)->getQuery();
|
||||
if ($this->_getParam('host') !== '*') {
|
||||
$this->applyQueryFilter($backendQuery, $filters);
|
||||
$this->applyQueryFilter($hostQuery, $filters);
|
||||
}
|
||||
$hosts = $backendQuery->fetchAll();
|
||||
$hosts = $hostQuery->fetchAll();
|
||||
|
||||
// Comments
|
||||
$commentQuery = CommentView::fromRequest($this->_request)->getQuery();
|
||||
$this->applyQueryFilter($commentQuery, $filters);
|
||||
$comments = array_keys($this->getUniqueValues($commentQuery->fetchAll(), 'comment_id'));
|
||||
// Fetch comments
|
||||
$commentQuery = $this->applyQueryFilter(
|
||||
CommentView::fromRequest($this->_request)->getQuery(),
|
||||
$filters,
|
||||
'comment_host'
|
||||
);
|
||||
$comments = array_keys($this->getUniqueValues($commentQuery->fetchAll(), 'comment_internal_id'));
|
||||
|
||||
$this->view->objects = $this->view->hosts = $hosts;
|
||||
$this->view->problems = $this->getProblems($hosts);
|
||||
$this->view->comments = isset($comments) ? $comments : $this->getComments($hosts);
|
||||
// Populate view
|
||||
$this->view->objects = $this->view->hosts = $hosts;
|
||||
$this->view->problems = $this->getProblems($hosts);
|
||||
$this->view->comments = isset($comments) ? $comments : $this->getComments($hosts);
|
||||
$this->view->hostnames = $this->getProperties($hosts, 'host_name');
|
||||
$this->view->downtimes = $this->getDowntimes($hosts);
|
||||
$this->view->errors = $errors;
|
||||
$this->view->errors = $errors;
|
||||
|
||||
// Handle configuration changes
|
||||
$this->handleConfigurationForm(array(
|
||||
'host_passive_checks_enabled' => 'Passive Checks',
|
||||
'host_active_checks_enabled' => 'Active Checks',
|
||||
'host_obsessing' => 'Obsessing',
|
||||
'host_notifications_enabled' => 'Notifications',
|
||||
'host_event_handler_enabled' => 'Event Handler',
|
||||
'host_active_checks_enabled' => 'Active Checks',
|
||||
'host_obsessing' => 'Obsessing',
|
||||
'host_notifications_enabled' => 'Notifications',
|
||||
'host_event_handler_enabled' => 'Event Handler',
|
||||
'host_flap_detection_enabled' => 'Flap Detection'
|
||||
));
|
||||
$this->view->form->setAction('/icinga2-web/monitoring/multi/host');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $backendQuery BaseQuery The query to apply the filter to
|
||||
* @param $filter array Containing the filter expressions from the request
|
||||
* Apply the query-filter received
|
||||
*
|
||||
* @param $backendQuery BaseQuery The query to apply the filter to
|
||||
* @param $filter array Containing the queries of the current request, converted into an
|
||||
* array-structure.
|
||||
* @param $hostColumn string The name of the host-column in the BaseQuery, defaults to 'host_name'
|
||||
* @param $serviceColumn string The name of the service-column in the BaseQuery, defaults to 'service-description'
|
||||
*
|
||||
* @return BaseQuery The given BaseQuery
|
||||
*/
|
||||
private function applyQueryFilter($backendQuery, $filter)
|
||||
{
|
||||
private function applyQueryFilter(
|
||||
BaseQuery $backendQuery,
|
||||
array $filter,
|
||||
$hostColumn = 'host_name',
|
||||
$serviceColumn = 'service_description'
|
||||
) {
|
||||
// fetch specified hosts
|
||||
foreach ($filter as $index => $expr) {
|
||||
// Every query entry must define at least the host.
|
||||
if (!array_key_exists('host', $expr)) {
|
||||
$errors[] = 'Query ' . $index . ' misses property host.';
|
||||
continue;
|
||||
}
|
||||
// apply filter expressions from query
|
||||
$backendQuery->orWhere('host_name', $expr['host']);
|
||||
$backendQuery->orWhere($hostColumn, $expr['host']);
|
||||
if (array_key_exists('service', $expr)) {
|
||||
$backendQuery->andWhere('service_description', $expr['service']);
|
||||
$backendQuery->andWhere($serviceColumn, $expr['service']);
|
||||
}
|
||||
}
|
||||
return $backendQuery;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -134,23 +152,25 @@ class Monitoring_MultiController extends ActionController
|
|||
if (is_array($value)) {
|
||||
$unique[$value[$key]] = $value[$key];
|
||||
} else {
|
||||
$unique[$value->{$key}] = $value->{$key};
|
||||
$unique[$value->$key] = $value->$key;
|
||||
}
|
||||
}
|
||||
return $unique;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the numbers of problems in the given objects
|
||||
* Get the numbers of problems of the given objects
|
||||
*
|
||||
* @param $object array The hosts or services
|
||||
* @param $objects The objects containing the problems
|
||||
*
|
||||
* @return int The problem count
|
||||
*/
|
||||
private function getProblems($objects)
|
||||
{
|
||||
$problems = 0;
|
||||
foreach ($objects as $object) {
|
||||
if (property_exists($object, 'host_unhandled_service_count')) {
|
||||
$problems += $object->{'host_unhandled_service_count'};
|
||||
$problems += $object->host_unhandled_service_count;
|
||||
} else if (
|
||||
property_exists($object, 'service_handled') &&
|
||||
!$object->service_handled &&
|
||||
|
@ -175,7 +195,7 @@ class Monitoring_MultiController extends ActionController
|
|||
{
|
||||
$objectnames = array();
|
||||
foreach ($objects as $object) {
|
||||
$objectnames[] = $object->{$property};
|
||||
$objectnames[] = $object->$property;
|
||||
}
|
||||
return $objectnames;
|
||||
}
|
||||
|
@ -224,7 +244,7 @@ class Monitoring_MultiController extends ActionController
|
|||
// Comments
|
||||
$commentQuery = CommentView::fromRequest($this->_request)->getQuery();
|
||||
$this->applyQueryFilter($commentQuery, $filters);
|
||||
$comments = array_keys($this->getUniqueValues($commentQuery->fetchAll(), 'comment_id'));
|
||||
$comments = array_keys($this->getUniqueValues($commentQuery->fetchAll(), 'comment_internal_id'));
|
||||
|
||||
$this->view->objects = $this->view->services = $services;
|
||||
$this->view->problems = $this->getProblems($services);
|
||||
|
@ -236,9 +256,9 @@ class Monitoring_MultiController extends ActionController
|
|||
|
||||
$this->handleConfigurationForm(array(
|
||||
'service_passive_checks_enabled' => 'Passive Checks',
|
||||
'service_active_checks_enabled' => 'Active Checks',
|
||||
'service_notifications_enabled' => 'Notifications',
|
||||
'service_event_handler_enabled' => 'Event Handler',
|
||||
'service_active_checks_enabled' => 'Active Checks',
|
||||
'service_notifications_enabled' => 'Notifications',
|
||||
'service_event_handler_enabled' => 'Event Handler',
|
||||
'service_flap_detection_enabled' => 'Flap Detection'
|
||||
));
|
||||
$this->view->form->setAction('/icinga2-web/monitoring/multi/service');
|
||||
|
@ -263,28 +283,37 @@ class Monitoring_MultiController extends ActionController
|
|||
}
|
||||
|
||||
/**
|
||||
* Fetch all requests from the 'detail' parameter.
|
||||
* "Flips" the structure of the objects created by _getAllParams
|
||||
*
|
||||
* @return array An array of request that contain
|
||||
* the filter arguments as properties.
|
||||
* Regularly, _getAllParams would return queries like <b>host[0]=value1&service[0]=value2</b> as
|
||||
* two entirely separate arrays. Instead, we want it as one single array, containing one single object
|
||||
* for each index, containing all of its members as keys.
|
||||
*
|
||||
* @return array An array of all query parameters (See example above)
|
||||
* <b>
|
||||
* array( <br />
|
||||
* 0 => array(host => value1, service => value2), <br />
|
||||
* ... <br />
|
||||
* )
|
||||
* </b>
|
||||
*/
|
||||
private function getDetailQueries()
|
||||
private function getAllParamsAsArray()
|
||||
{
|
||||
$details = $this->_getAllParams();
|
||||
$objects = array();
|
||||
$queries = array();
|
||||
|
||||
foreach ($details as $property => $values) {
|
||||
if (!is_array($values)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($values as $index => $value) {
|
||||
if (!array_key_exists($index, $objects)) {
|
||||
$objects[$index] = array();
|
||||
if (!array_key_exists($index, $queries)) {
|
||||
$queries[$index] = array();
|
||||
}
|
||||
$objects[$index][$property] = $value;
|
||||
$queries[$index][$property] = $value;
|
||||
}
|
||||
}
|
||||
return $objects;
|
||||
return $queries;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -19,12 +19,17 @@ if ($hosts->count() === 0) {
|
|||
}
|
||||
?>
|
||||
|
||||
<table data-base-target="_next" class="action multiselect">
|
||||
<table
|
||||
data-base-target="_next"
|
||||
class="action multiselect"
|
||||
data-icinga-multiselect-url="<?= $this->href("/monitoring/multi/host") ?>"
|
||||
data-icinga-multiselect-data="host"
|
||||
>
|
||||
<tbody>
|
||||
<?php foreach($hosts as $host):
|
||||
|
||||
$hostStateName = strtolower($this->util()->getHostStateName($host->host_state));
|
||||
$hostLink = $this->href('monitoring/show/host', array('host' => $host->host_name));
|
||||
$hostLink = $this->href('/monitoring/show/host', array('host' => $host->host_name));
|
||||
|
||||
$icons = array();
|
||||
if (!$host->host_handled && $host->host_state > 0){
|
||||
|
|
|
@ -13,6 +13,12 @@
|
|||
|
||||
Icinga.Events.prototype = {
|
||||
|
||||
keyboard: {
|
||||
ctrlKey: false,
|
||||
altKey: false,
|
||||
shiftKey: false
|
||||
},
|
||||
|
||||
/**
|
||||
* Icinga will call our initialize() function once it's ready
|
||||
*/
|
||||
|
@ -65,7 +71,7 @@
|
|||
type: 'pie',
|
||||
sliceColors: ['#44bb77', '#ffaa44', '#ff5566', '#dcd'],
|
||||
width: '2em',
|
||||
height: '2em',
|
||||
height: '2em'
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -90,7 +96,10 @@
|
|||
$(document).on('click', 'a', { self: this }, this.linkClicked);
|
||||
|
||||
// We treat tr's with a href attribute like links
|
||||
$(document).on('click', 'tr[href]', { self: this }, this.linkClicked);
|
||||
$(document).on('click', ':not(table) tr[href]', { self: this }, this.linkClicked);
|
||||
|
||||
// When tables have the class 'multiselect', multiple selection is possible.
|
||||
$(document).on('click', 'table tr[href]', { self: this }, this.rowSelected);
|
||||
|
||||
$(document).on('click', 'button', { self: this }, this.submitForm);
|
||||
|
||||
|
@ -277,6 +286,151 @@
|
|||
return false;
|
||||
},
|
||||
|
||||
handleExternalTarget: function($node) {
|
||||
var linkTarget = $node.attr('target');
|
||||
|
||||
// TODO: Let remote links pass through. Right now they only work
|
||||
// combined with target="_blank" or target="_self"
|
||||
// window.open is used as return true; didn't work reliable
|
||||
if (linkTarget === '_blank' || linkTarget === '_self') {
|
||||
window.open(href, linkTarget);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle table selection.
|
||||
*/
|
||||
rowSelected: function(event) {
|
||||
var self = event.data.self;
|
||||
var icinga = self.icinga;
|
||||
var $tr = $(this);
|
||||
var $table = $tr.closest('table.multiselect');
|
||||
var data = $table.data('icinga-multiselect-data').split(',');
|
||||
var multisel = $table.hasClass('multiselect');
|
||||
var url = $table.data('icinga-multiselect-url');
|
||||
var $trs, $target;
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
if (icinga.events.handleExternalTarget($tr)) {
|
||||
// link handled externally
|
||||
return false;
|
||||
}
|
||||
if (!data) {
|
||||
icinga.logger.error('A table with multiselection must define the attribute "data-icinga-multiselect-data"');
|
||||
return;
|
||||
}
|
||||
if (!url) {
|
||||
icinga.logger.error('A table with multiselection must define the attribute "data-icinga-multiselect-url"');
|
||||
return;
|
||||
}
|
||||
|
||||
// Update selection
|
||||
if (event.ctrlKey && multisel) {
|
||||
// multi selection
|
||||
if ($tr.hasClass('active')) {
|
||||
$tr.removeClass('active');
|
||||
} else {
|
||||
$tr.addClass('active');
|
||||
}
|
||||
} else if (event.shiftKey && multisel) {
|
||||
// range selection
|
||||
|
||||
var $rows = $table.find('tr[href]'),
|
||||
from, to;
|
||||
var selected = this;
|
||||
|
||||
// TODO: find a better place for this
|
||||
$rows.find('td').attr('unselectable', 'on')
|
||||
.css('user-select', 'none')
|
||||
.css('-webkit-user-select', 'none')
|
||||
.css('-moz-user-select', 'none')
|
||||
.css('-ms-user-select', 'none');
|
||||
|
||||
$rows.each(function(i, el) {
|
||||
if ($(el).hasClass('active') || el === selected) {
|
||||
if (!from) {
|
||||
from = el;
|
||||
}
|
||||
to = el;
|
||||
}
|
||||
});
|
||||
var inRange = false;
|
||||
$rows.each(function(i, el){
|
||||
if (el === from) {
|
||||
inRange = true;
|
||||
}
|
||||
if (inRange) {
|
||||
$(el).addClass('active');
|
||||
}
|
||||
if (el === to) {
|
||||
inRange = false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// single selection
|
||||
if ($tr.hasClass('active')) {
|
||||
return false;
|
||||
}
|
||||
$table.find('tr[href].active').removeClass('active');
|
||||
$tr.addClass('active');
|
||||
}
|
||||
$trs = $table.find('tr[href].active');
|
||||
|
||||
// Update url
|
||||
$target = self.getLinkTargetFor($tr);
|
||||
if ($trs.length > 1) {
|
||||
// display multiple rows
|
||||
var query = icinga.events.selectionToQuery($trs, data, icinga);
|
||||
icinga.loader.loadUrl(url + '?' + query, $target);
|
||||
} else if ($trs.length === 1) {
|
||||
// display a single row
|
||||
icinga.loader.loadUrl($tr.attr('href'), $target);
|
||||
} else {
|
||||
// display nothing
|
||||
icinga.loader.loadUrl('#');
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
selectionToQuery: function ($selection, data, icinga) {
|
||||
var selections = [], queries = [];
|
||||
if ($selection.length === 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
// read all current selections
|
||||
$selection.each(function(ind, selected) {
|
||||
var url = $(selected).attr('href');
|
||||
var params = icinga.utils.parseUrl(url).params;
|
||||
var tuple = {};
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
var key = data[i];
|
||||
if (params[key]) {
|
||||
tuple[key] = params[key];
|
||||
}
|
||||
}
|
||||
selections.push(tuple);
|
||||
});
|
||||
|
||||
// create new url
|
||||
if (selections.length < 2) {
|
||||
// single-selection
|
||||
$.each(selections[0], function(key, value){
|
||||
queries.push(key + '=' + encodeURIComponent(value));
|
||||
});
|
||||
} else {
|
||||
// multi-selection
|
||||
$.each(selections, function(i, el){
|
||||
$.each(el, function(key, value) {
|
||||
queries.push(key + '[' + i + ']=' + encodeURIComponent(value));
|
||||
});
|
||||
});
|
||||
}
|
||||
return queries.join('&');
|
||||
},
|
||||
|
||||
/**
|
||||
* Someone clicked a link or tr[href]
|
||||
*/
|
||||
|
|
|
@ -99,6 +99,7 @@
|
|||
}
|
||||
|
||||
var self = this;
|
||||
console.log("$.ajax({ url = " + url + " })");
|
||||
var req = $.ajax({
|
||||
type : method,
|
||||
url : url,
|
||||
|
|
|
@ -104,7 +104,7 @@
|
|||
path : a.pathname.replace(/^([^\/])/,'/$1'),
|
||||
relative: (a.href.match(/tps?:\/\/[^\/]+(.+)/) || [,''])[1],
|
||||
segments: a.pathname.replace(/^\//,'').split('/'),
|
||||
params : this.parseParams(a),
|
||||
params : this.parseParams(a)
|
||||
};
|
||||
a = null;
|
||||
|
||||
|
|
Loading…
Reference in New Issue