Merge branch 'bugfix/service-grid-grouping-postgres-9333'

Conflicts:
	modules/monitoring/application/controllers/ListController.php
	modules/monitoring/library/Monitoring/Backend/Ido/Query/IdoQuery.php

fixes #9333
fixes #9538
This commit is contained in:
Eric Lippmann 2015-08-27 12:58:49 +02:00
commit c294283636
14 changed files with 514 additions and 448 deletions

View File

@ -4,23 +4,23 @@
namespace Icinga\Application\Modules;
use Exception;
use Zend_Controller_Router_Route;
use Zend_Controller_Router_Route_Abstract;
use Zend_Controller_Router_Route as Route;
use Zend_Controller_Router_Route_Regex as RegexRoute;
use Zend_Controller_Router_Route_Regex;
use Icinga\Application\ApplicationBootstrap;
use Icinga\Application\Config;
use Icinga\Application\Icinga;
use Icinga\Application\Logger;
use Icinga\Data\ConfigObject;
use Icinga\Exception\IcingaException;
use Icinga\Exception\ProgrammingError;
use Icinga\Module\Setup\SetupWizard;
use Icinga\Util\File;
use Icinga\Util\Translator;
use Icinga\Web\Hook;
use Icinga\Web\Menu;
use Icinga\Web\Widget;
use Icinga\Web\Widget\Dashboard\Pane;
use Icinga\Module\Setup\SetupWizard;
use Icinga\Util\File;
use Icinga\Exception\ProgrammingError;
use Icinga\Exception\IcingaException;
/**
* Module handling
@ -188,7 +188,7 @@ class Module
/**
* A set of menu elements
*
* @var array
* @var Menu[]
*/
protected $menuItems = array();
@ -770,6 +770,7 @@ class Module
{
$this->launchConfigScript();
$tabs = Widget::create('tabs');
/** @var \Icinga\Web\Widget\Tabs $tabs */
$tabs->add('info', array(
'url' => 'config/module',
'urlParams' => array('name' => $this->getName()),
@ -1039,12 +1040,13 @@ class Module
protected function registerRoutes()
{
$router = $this->app->getFrontController()->getRouter();
/** @var \Zend_Controller_Router_Rewrite $router */
foreach ($this->routes as $name => $route) {
$router->addRoute($name, $route);
}
$router->addRoute(
$this->name . '_jsprovider',
new Route(
new Zend_Controller_Router_Route(
'js/' . $this->name . '/:file',
array(
'controller' => 'static',
@ -1055,7 +1057,7 @@ class Module
);
$router->addRoute(
$this->name . '_img',
new RegexRoute(
new Zend_Controller_Router_Route_Regex(
'img/' . $this->name . '/(.+)',
array(
'controller' => 'static',

View File

@ -4,12 +4,11 @@
namespace Icinga\Data;
use Icinga\Data\Filter\Filter;
use Icinga\Data\SimpleQuery;
use Icinga\Application\Icinga;
use Icinga\Web\Paginator\Adapter\QueryAdapter;
use Zend_Paginator;
class PivotTable
class PivotTable implements Sortable
{
/**
* The query to fetch as pivot table
@ -19,53 +18,74 @@ class PivotTable
protected $baseQuery;
/**
* The query to fetch the x axis labels
*
* @var SimpleQuery
*/
protected $xAxisQuery;
/**
* The query to fetch the y axis labels
*
* @var SimpleQuery
*/
protected $yAxisQuery;
/**
* The column that contains the labels for the x axis
* X-axis pivot column
*
* @var string
*/
protected $xAxisColumn;
/**
* The column that contains the labels for the y axis
* Y-axis pivot column
*
* @var string
*/
protected $yAxisColumn;
/**
* The filter being applied on the query for the x axis
* Column for sorting the result set
*
* @var array
*/
protected $order = array();
/**
* The filter being applied on the query for the x-axis
*
* @var Filter
*/
protected $xAxisFilter;
/**
* The filter being applied on the query for the y axis
* The filter being applied on the query for the y-axis
*
* @var Filter
*/
protected $yAxisFilter;
/**
* The query to fetch the leading x-axis rows and their headers
*
* @var SimpleQuery
*/
protected $xAxisQuery;
/**
* The query to fetch the leading y-axis rows and their headers
*
* @var SimpleQuery
*/
protected $yAxisQuery;
/**
* X-axis header column
*
* @var string|null
*/
protected $xAxisHeader;
/**
* Y-axis header column
*
* @var string|null
*/
protected $yAxisHeader;
/**
* Create a new pivot table
*
* @param SimpleQuery $query The query to fetch as pivot table
* @param string $xAxisColumn The column that contains the labels for the x axis
* @param string $yAxisColumn The column that contains the labels for the y axis
* @param SimpleQuery $query The query to fetch as pivot table
* @param string $xAxisColumn X-axis pivot column
* @param string $yAxisColumn Y-axis pivot column
*/
public function __construct(SimpleQuery $query, $xAxisColumn, $yAxisColumn)
{
@ -75,7 +95,32 @@ class PivotTable
}
/**
* Set the filter to apply on the query for the x axis
* {@inheritdoc}
*/
public function getOrder()
{
return $this->order;
}
/**
* {@inheritdoc}
*/
public function hasOrder()
{
return ! empty($this->order);
}
/**
* {@inheritdoc}
*/
public function order($field, $direction = null)
{
$this->order[$field] = $direction;
return $this;
}
/**
* Set the filter to apply on the query for the x-axis
*
* @param Filter $filter
*
@ -88,7 +133,7 @@ class PivotTable
}
/**
* Set the filter to apply on the query for the y axis
* Set the filter to apply on the query for the y-axis
*
* @param Filter $filter
*
@ -100,6 +145,56 @@ class PivotTable
return $this;
}
/**
* Get the x-axis header
*
* Defaults to {@link $xAxisColumn} in case no x-axis header has been set using {@link setXAxisHeader()}
*
* @return string
*/
public function getXAxisHeader()
{
return $this->xAxisHeader !== null ? $this->xAxisHeader : $this->xAxisColumn;
}
/**
* Set the x-axis header
*
* @param string $xAxisHeader
*
* @return $this
*/
public function setXAxisHeader($xAxisHeader)
{
$this->xAxisHeader = (string) $xAxisHeader;
return $this;
}
/**
* Get the y-axis header
*
* Defaults to {@link $yAxisColumn} in case no x-axis header has been set using {@link setYAxisHeader()}
*
* @return string
*/
public function getYAxisHeader()
{
return $this->yAxisHeader !== null ? $this->yAxisHeader : $this->yAxisColumn;
}
/**
* Set the y-axis header
*
* @param string $yAxisHeader
*
* @return $this
*/
public function setYAxisHeader($yAxisHeader)
{
$this->yAxisHeader = (string) $yAxisHeader;
return $this;
}
/**
* Return the value for the given request parameter
*
@ -107,7 +202,7 @@ class PivotTable
* @param string $param The parameter name to return
* @param int $default The default value to return
*
* @return int
* @return int
*/
protected function getPaginationParameter($axis, $param, $default = null)
{
@ -125,23 +220,25 @@ class PivotTable
/**
* Query horizontal (x) axis
*
* @return SimpleQuery
* @return SimpleQuery
*/
protected function queryXAxis()
{
if ($this->xAxisQuery === null) {
$this->xAxisQuery = clone $this->baseQuery;
$this->xAxisQuery->group($this->xAxisColumn);
$this->xAxisQuery->columns(array($this->xAxisColumn));
$this->xAxisQuery->setUseSubqueryCount();
$xAxisHeader = $this->getXAxisHeader();
$columns = array($this->xAxisColumn, $xAxisHeader);
$this->xAxisQuery->group(array_unique($columns)); // xAxisColumn and header may be the same column
$this->xAxisQuery->columns($columns);
if ($this->xAxisFilter !== null) {
$this->xAxisQuery->addFilter($this->xAxisFilter);
}
if (! $this->xAxisQuery->hasOrder($this->xAxisColumn)) {
$this->xAxisQuery->order($this->xAxisColumn, 'asc');
}
$this->xAxisQuery->order(
$xAxisHeader,
isset($this->order[$xAxisHeader]) ? $this->order[$xAxisHeader] : self::SORT_ASC
);
}
return $this->xAxisQuery;
@ -150,30 +247,31 @@ class PivotTable
/**
* Query vertical (y) axis
*
* @return SimpleQuery
* @return SimpleQuery
*/
protected function queryYAxis()
{
if ($this->yAxisQuery === null) {
$this->yAxisQuery = clone $this->baseQuery;
$this->yAxisQuery->group($this->yAxisColumn);
$this->yAxisQuery->columns(array($this->yAxisColumn));
$this->yAxisQuery->setUseSubqueryCount();
$yAxisHeader = $this->getYAxisHeader();
$columns = array($this->yAxisColumn, $yAxisHeader);
$this->yAxisQuery->group(array_unique($columns)); // yAxisColumn and header may be the same column
$this->yAxisQuery->columns($columns);
if ($this->yAxisFilter !== null) {
$this->yAxisQuery->addFilter($this->yAxisFilter);
}
if (! $this->yAxisQuery->hasOrder($this->yAxisColumn)) {
$this->yAxisQuery->order($this->yAxisColumn, 'asc');
}
$this->yAxisQuery->order(
$yAxisHeader,
isset($this->order[$yAxisHeader]) ? $this->order[$yAxisHeader] : self::SORT_ASC
);
}
return $this->yAxisQuery;
}
/**
* Return a pagination adapter for the x axis query
* Return a pagination adapter for the x-axis query
*
* $limit and $page are taken from the current request if not given.
*
@ -204,7 +302,7 @@ class PivotTable
}
/**
* Return a pagination adapter for the y axis query
* Return a pagination adapter for the y-axis query
*
* $limit and $page are taken from the current request if not given.
*
@ -235,9 +333,9 @@ class PivotTable
}
/**
* Return the pivot table as array
* Return the pivot table as an array of pivot data and pivot header
*
* @return array
* @return array
*/
public function toArray()
{
@ -245,33 +343,39 @@ class PivotTable
($this->xAxisFilter === null && $this->yAxisFilter === null)
|| ($this->xAxisFilter !== null && $this->yAxisFilter !== null)
) {
$xAxis = $this->queryXAxis()->fetchColumn();
$yAxis = $this->queryYAxis()->fetchColumn();
$xAxis = $this->queryXAxis()->fetchPairs();
$yAxis = $this->queryYAxis()->fetchPairs();
} else {
if ($this->xAxisFilter !== null) {
$xAxis = $this->queryXAxis()->fetchColumn();
$yAxis = $this->queryYAxis()->where($this->xAxisColumn, $xAxis)->fetchColumn();
$xAxis = $this->queryXAxis()->fetchPairs();
$yAxis = $this->queryYAxis()->where($this->xAxisColumn, $xAxis)->fetchPairs();
} else { // $this->yAxisFilter !== null
$yAxis = $this->queryYAxis()->fetchColumn();
$xAxis = $this->queryXAxis()->where($this->yAxisColumn, $yAxis)->fetchColumn();
$yAxis = $this->queryYAxis()->fetchPairs();
$xAxis = $this->queryXAxis()->where($this->yAxisColumn, $yAxis)->fetchPairs();
}
}
$pivotData = array();
$pivotHeader = array(
'cols' => $xAxis,
'rows' => $yAxis
);
if (! empty($xAxis) && ! empty($yAxis)) {
$xAxisKeys = array_keys($xAxis);
$yAxisKeys = array_keys($yAxis);
$this->baseQuery
->where($this->xAxisColumn, $xAxisKeys)
->where($this->yAxisColumn, $yAxisKeys);
$pivot = array();
if (!empty($xAxis) && !empty($yAxis)) {
$this->baseQuery->where($this->xAxisColumn, $xAxis)->where($this->yAxisColumn, $yAxis);
foreach ($yAxis as $yLabel) {
foreach ($xAxis as $xLabel) {
$pivot[$yLabel][$xLabel] = null;
foreach ($yAxisKeys as $yAxisKey) {
foreach ($xAxisKeys as $xAxisKey) {
$pivotData[$yAxisKey][$xAxisKey] = null;
}
}
foreach ($this->baseQuery as $row) {
$pivot[$row->{$this->yAxisColumn}][$row->{$this->xAxisColumn}] = $row;
$pivotData[$row->{$this->yAxisColumn}][$row->{$this->xAxisColumn}] = $row;
}
}
return $pivot;
return array($pivotData, $pivotHeader);
}
}

View File

@ -12,6 +12,7 @@ class StyleSheet
protected static $lessFiles = array(
'../application/fonts/fontello-ifont/css/ifont-embedded.css',
'css/vendor/tipsy.css',
'css/icinga/mixins.less',
'css/icinga/defaults.less',
'css/icinga/animation.less',
'css/icinga/layout-colors.less',

View File

@ -118,12 +118,12 @@ class SortBox extends AbstractWidget
if ($request === null) {
$request = Icinga::app()->getRequest();
}
if (($sort = $request->getParam('sort'))) {
$this->query->order($sort, $request->getParam('dir'));
} elseif (($dir = $request->getParam('dir'))) {
$this->query->order(null, $dir);
if (null === $sort = $request->getParam('sort')) {
list($sort, $dir) = $this->getSortDefaults();
} else {
list($_, $dir) = $this->getSortDefaults($sort);
}
$this->query->order($sort, $request->getParam('dir', $dir));
}
return $this;
@ -148,8 +148,10 @@ class SortBox extends AbstractWidget
if ($column !== null && isset($sortRules[$column]['order'])) {
$direction = strtoupper($sortRules[$column]['order']) === Sortable::SORT_DESC ? 'desc' : 'asc';
}
} elseif ($column === null) {
reset($this->sortFields);
$column = key($this->sortFields);
}
return array($column, $direction);
}

View File

@ -556,29 +556,36 @@ class Monitoring_ListController extends Controller
{
$this->addTitleTab('servicegrid', $this->translate('Service Grid'), $this->translate('Show the Service Grid'));
$this->setAutorefreshInterval(15);
$problems = (bool) $this->params->shift('problems', 0);
$query = $this->backend->select()->from('servicestatus', array(
'host_display_name',
'host_name',
'service_description',
'service_state',
'service_display_name',
'service_handled',
'service_output',
'service_handled'
'service_state'
));
$this->applyRestriction('monitoring/filter/objects', $query);
$this->filterQuery($query);
$filter = (bool) $this->params->shift('problems', false) ? Filter::where('service_problem', 1) : null;
$pivot = $query
->pivot(
'service_description',
'host_name',
$filter,
$filter ? clone $filter : null
)
->setXAxisHeader('service_display_name')
->setYAxisHeader('host_display_name');
$this->setupSortControl(array(
'host_name' => $this->translate('Hostname'),
'service_description' => $this->translate('Service description')
), $query);
$pivot = $query->pivot(
'service_description',
'host_name',
$problems ? Filter::where('service_problem', 1) : null,
$problems ? Filter::where('service_problem', 1) : null
);
$this->view->pivot = $pivot;
'host_display_name' => $this->translate('Hostname'),
'service_display_name' => $this->translate('Service Name')
), $pivot);
$this->view->horizontalPaginator = $pivot->paginateXAxis();
$this->view->verticalPaginator = $pivot->paginateYAxis();
$this->view->verticalPaginator = $pivot->paginateYAxis();
list($pivotData, $pivotHeader) = $pivot->toArray();
$this->view->pivotData = $pivotData;
$this->view->pivotHeader = $pivotHeader;
}
/**

View File

@ -12,97 +12,84 @@ if (! $this->compact): ?>
<?php endif ?>
<div class="content" data-base-target="_next">
<?php
$hasHeader = false;
$pivotData = $this->pivot->toArray();
if (count($pivotData) === 0) {
if (empty($pivotData)) {
echo $this->translate('No services found matching the filter') . '</div>';
return;
}
$hostFilter = '(host_name=' . implode('|host_name=', array_keys($pivotData)) . ')';
?>
<table class="pivot servicestates">
<?php foreach ($pivotData as $host_name => $serviceStates): ?>
<?php if (!$hasHeader): ?>
<thead>
<tr>
<th><?= $this->partial(
'joystickPagination.phtml',
'default',
array(
'xAxisPaginator' => $horizontalPaginator,
'yAxisPaginator' => $verticalPaginator
)
); ?></th>
<th colspan="<?= count($serviceStates); ?>">
<div>
<?php
$serviceDescriptions = array_keys($serviceStates);
$serviceFilter = '(service_description=' . implode('|service_description=', $serviceDescriptions) . ')';
foreach ($serviceDescriptions as $service_description): ?>
<span>
<?= $this->qlink(
'<abbr>' . (strlen($service_description) > 18 ? substr($service_description, 0, 18) . '...' : $service_description) . '</abbr>',
<table class="service-grid-table">
<thead>
<tr>
<th><?= $this->partial(
'joystickPagination.phtml',
'default',
array(
'xAxisPaginator' => $horizontalPaginator,
'yAxisPaginator' => $verticalPaginator
)
); ?></th>
<?php foreach ($pivotHeader['cols'] as $serviceDescription => $serviceDisplayName): ?>
<th class="rotate-45"><div><span><?= $this->qlink(
$this->ellipsis($serviceDisplayName, 18),
'monitoring/list/services?' . $hostFilter,
array(
'service_description' => $service_description
),
array(
'title' => sprintf($this->translate('List all services with the name "%s" on all reported hosts'), $service_description)
),
array('service_description' => $serviceDescription),
array('title' => sprintf(
$this->translate('List all services with the name "%s" on all reported hosts'),
$serviceDisplayName
)),
false
); ?>
</span>
<?php endforeach ?>
</div>
</th>
</tr>
) ?></span></div></th>
<?php endforeach ?>
</tr>
</thead>
<tbody>
<?php $hasHeader = true; ?>
<?php endif ?>
<tr>
<th>
<?= $this->qlink(
$host_name,
'monitoring/list/services?' . $serviceFilter,
array('host' => $host_name),
array('title' => sprintf($this->translate('List all reported services on host %s'), $host_name))
); ?>
</th>
<?php foreach (array_values($serviceStates) as $service): ?>
<?php if ($service !== null): ?>
<td>
<span class="sr-only" id="<?= $service->host_name . '_' . $service->service_description . '_desc'; ?>">
<?= $this->escape($service->service_output); ?>
</span>
<?= $this->qlink(
'',
'monitoring/show/service',
array(
'host' => $service->host_name,
'service' => $service->service_description
),
array(
'aria-describedby' => $service->host_name . '_' . $service->service_description . '_desc',
'class' => 'state_' . Service::getStateText($service->service_state). ($service->service_handled ? ' handled' : ''),
'title' => $this->escape($service->service_output),
'aria-label' => sprintf(
$this->translate('Show detailed information for service %s on host %s'),
$service->service_description,
$service->host_name
)
)
); ?>
</td>
<?php else: ?>
<td><span aria-hidden="true">&middot;</span></td>
<?php endif ?>
<?php endforeach ?>
</tr>
<?php endforeach ?>
<?php foreach ($pivotHeader['rows'] as $hostName => $hostDisplayName): ?>
<tr>
<th><?php
$services = $pivotData[$hostName];
$serviceFilter = '(service_description=' . implode('|service_description=', array_keys($services)) . ')';
echo $this->qlink(
$hostDisplayName,
'monitoring/list/services?' . $serviceFilter,
array('host_name' => $hostName),
array('title' => sprintf($this->translate('List all reported services on host %s'), $hostDisplayName))
);
?></th>
<?php foreach (array_keys($pivotHeader['cols']) as $serviceDescription): ?>
<td>
<?php
$service = $pivotData[$hostName][$serviceDescription];
if ($service === null): ?>
<span aria-hidden="true">&middot;</span>
<?php continue; endif ?>
<?php $ariaDescribedById = $this->protectId($service->host_name . '_' . $service->service_description . '_desc') ?>
<span class="sr-only" id="<?= $ariaDescribedById ?>">
<?= $this->escape($service->service_output) ?>
</span>
<?= $this->qlink(
'',
'monitoring/show/service',
array(
'host' => $hostName,
'service' => $serviceDescription
),
array(
'aria-describedby' => $ariaDescribedById,
'class' => 'bg-state-' . Service::getStateText($service->service_state) . ($service->service_handled ? ' handled' : ''),
'title' => $this->escape($service->service_output),
'aria-label' => sprintf(
$this->translate('Show detailed information for service %s on host %s'),
$service->service_display_name,
$service->host_display_name
)
)
); ?>
</td>
<?php endforeach ?>
</tr>
<?php endforeach ?>
</tbody>
</table>
</table>
</div>

View File

@ -240,3 +240,9 @@ $dashboard->add(
$this->translate('Host Problems'),
'monitoring/list/hosts?host_problem=1&sort=host_severity'
);
/*
* CSS
*/
$this->provideCssFile('colors.less');
$this->provideCssFile('service-grid.less');

View File

@ -257,62 +257,65 @@ class HoststatusQuery extends IdoQuery
*/
public function getGroup()
{
$group = array();
if ($this->hasJoinedVirtualTable('hostgroups') || $this->hasJoinedVirtualTable('services')) {
$group = array('h.host_id', 'ho.object_id');
if ($this->hasJoinedVirtualTable('hoststatus')) {
$group[] = 'hs.hoststatus_id';
}
if ($this->hasJoinedVirtualTable('serviceproblemsummary')) {
$group[] = 'sps.unhandled_services_count';
}
if ($this->hasJoinedVirtualTable('hostgroups')) {
$selected = false;
foreach ($this->columns as $alias => $column) {
if ($column instanceof Zend_Db_Expr) {
continue;
}
$table = $this->aliasToTableName(
$this->hasAliasName($alias) ? $alias : $this->customAliasToAlias($alias)
);
if ($table === 'hostgroups') {
$selected = true;
break;
}
}
if ($selected) {
$group[] = 'hg.hostgroup_id';
$group[] = 'hgo.object_id';
}
}
if ($this->hasJoinedVirtualTable('servicegroups')) {
$selected = false;
foreach ($this->columns as $alias => $column) {
if ($column instanceof Zend_Db_Expr) {
continue;
}
$table = $this->aliasToTableName(
$this->hasAliasName($alias) ? $alias : $this->customAliasToAlias($alias)
);
if ($table === 'servicegroups') {
$selected = true;
break;
}
}
if ($selected) {
$group[] = 'sg.servicegroup_id';
$group[] = 'sgo.object_id';
}
$group = parent::getGroup() ?: array();
if (! is_array($group)) {
$group = array($group);
}
$groupedTables = array();
if ($this->hasJoinedVirtualTable('servicegroups')) {
$serviceGroupColumns = array_keys($this->columnMap['servicegroups']);
$selectedServiceGroupColumns = array_intersect($serviceGroupColumns, array_keys($this->columns));
if (! empty($selectedServiceGroupColumns)) {
$group[] = 'ho.object_id';
$group[] = 'h.host_id';
$group[] = 'sgo.object_id';
$group[] = 'sg.servicegroup_id';
$groupedTables['hosts'] = true;
$groupedTables['servicegroups'] = true;
}
}
if ($this->hasJoinedVirtualTable('hostgroups')) {
$hostGroupColumns = array_keys($this->columnMap['hostgroups']);
$selectedHostGroupColumns = array_intersect($hostGroupColumns, array_keys($this->columns));
if (! empty($selectedHostGroupColumns)) {
if (! isset($groupedTables['hosts'])) {
$group[] = 'ho.object_id';
$group[] = 'h.host_id';
$groupedTables['hosts'] = true;
}
$group[] = 'hgo.object_id';
$group[] = 'hg.hostgroup_id';
$groupedTables['hostgroups'] = true;
}
}
if (! empty($groupedTables)) {
foreach ($this->columns as $alias => $column) {
if ($column instanceof Zend_Db_Expr || $column === '(NULL)') {
continue;
}
$tableName = $this->aliasToTableName(
$this->hasAliasName($alias) ? $alias : $this->customAliasToAlias($alias)
);
if (isset($groupedTables[$tableName])) {
continue;
}
switch ($tableName) {
case 'hoststatus':
$group[] = 'hs.hoststatus_id';
break;
case 'serviceproblemsummary':
$group[] = 'sps.unhandled_services_count';
break;
case 'services':
$group[] = 'so.object_id';
$group[] = 's.service_id';
break;
default:
continue 2;
}
$groupedTables[$tableName] = true;
}
}
return $group;
}

View File

@ -461,17 +461,23 @@ abstract class IdoQuery extends DbQuery
if ($filter->getExpression() === '*') {
return; // Wildcard only filters are ignored so stop early here to avoid joining a table for nothing
}
$col = $filter->getColumn();
$this->requireColumn($col);
if ($this->isCustomvar($col)) {
$col = $this->getCustomvarColumnName($col);
$alias = $filter->getColumn();
$this->requireColumn($alias);
if ($this->isCustomvar($alias)) {
$column = $this->getCustomvarColumnName($alias);
} else {
$col = $this->aliasToColumnName($col);
$column = $this->aliasToColumnName($alias);
}
if (isset($this->columnsWithoutCollation[$alias])) {
$expression = $filter->getExpression();
if (is_array($expression)) {
$filter->setExpression(array_map('strtolower', $expression));
} else {
$filter->setExpression(strtolower($expression));
$filter->setColumn($col);
}
}
$filter->setColumn($column);
} else {
foreach ($filter->filters() as $filter) {
$this->requireFilterColumns($filter);
@ -489,48 +495,11 @@ abstract class IdoQuery extends DbQuery
return parent::addFilter($filter);
}
/**
* Recurse the given filter and ensure that any string conversion is case-insensitive
*
* @param Filter $filter
*/
protected function lowerColumnsWithoutCollation(Filter $filter)
{
if ($filter instanceof FilterExpression) {
if (
in_array($filter->getColumn(), $this->columnsWithoutCollation)
&& strpos($filter->getColumn(), 'LOWER') !== 0
) {
$filter->setColumn('LOWER(' . $filter->getColumn() . ')');
$expression = $filter->getExpression();
if (is_array($expression)) {
$filter->setExpression(array_map('strtolower', $expression));
} else {
$filter->setExpression(strtolower($expression));
}
}
} else {
foreach ($filter->filters() as $chainedFilter) {
$this->lowerColumnsWithoutCollation($chainedFilter);
}
}
}
protected function applyFilterSql($select)
{
if (! empty($this->columnsWithoutCollation)) {
$this->lowerColumnsWithoutCollation($this->filter);
}
parent::applyFilterSql($select);
}
public function where($condition, $value = null)
{
if ($value === '*') {
return $this; // Wildcard only filters are ignored so stop early here to avoid joining a table for nothing
}
$this->requireColumn($condition);
$col = $this->getMappedField($condition);
if ($col === null) {
@ -578,7 +547,6 @@ abstract class IdoQuery extends DbQuery
if (! empty($this->columnsWithoutCollation)) {
return in_array($column, $this->columnsWithoutCollation) || strpos($column, 'LOWER') !== 0;
}
return preg_match('/ COLLATE .+$/', $column) === 1;
}
@ -603,27 +571,27 @@ abstract class IdoQuery extends DbQuery
}
/**
* Apply postgresql specific query initialization
* Apply PostgreSQL specific query initialization
*/
private function initializeForPostgres()
{
$this->customVarsJoinTemplate =
'%1$s = %2$s.object_id AND LOWER(%2$s.varname) = %3$s';
foreach ($this->columnMap as $table => & $columns) {
foreach ($columns as $key => & $value) {
$value = preg_replace('/ COLLATE .+$/', '', $value, -1, $count);
if ($count > 0) {
$this->columnsWithoutCollation[] = $this->getMappedField($key);
foreach ($this->columnMap as $table => &$columns) {
foreach ($columns as $alias => &$column) {
if (false !== $pos = strpos($column, ' COLLATE')) {
$column = 'LOWER(' . substr($column, 0, $pos) . ')';
$this->columnsWithoutCollation[$alias] = true;
}
$value = preg_replace(
$column = preg_replace(
'/inet_aton\(([[:word:].]+)\)/i',
'(CASE WHEN $1 ~ \'(?:[0-9]{1,3}\\\\.){3}[0-9]{1,3}\' THEN $1::inet - \'0.0.0.0\' ELSE NULL END)',
$value
$column
);
$value = preg_replace(
$column = preg_replace(
'/UNIX_TIMESTAMP(\((?>[^()]|(?-1))*\))/i',
'CASE WHEN ($1 < \'1970-01-03 00:00:00+00\'::timestamp with time zone) THEN 0 ELSE UNIX_TIMESTAMP($1) END',
$value
$column
);
}
}

View File

@ -381,68 +381,60 @@ class ServicestatusQuery extends IdoQuery
if (! is_array($group)) {
$group = array($group);
}
if ($this->hasJoinedVirtualTable('hostgroups') || $this->hasJoinedVirtualTable('servicegroups')) {
$group[] = 's.service_id';
$group[] = 'so.object_id';
if ($this->hasJoinedVirtualTable('hosts')) {
$group[] = 'h.host_id';
}
if ($this->hasJoinedVirtualTable('hoststatus')) {
$group[] = 'hs.hoststatus_id';
}
if ($this->hasJoinedVirtualTable('servicestatus')) {
$group[] = 'ss.servicestatus_id';
}
if ($this->hasJoinedVirtualTable('hostgroups')) {
$selected = false;
foreach ($this->columns as $alias => $column) {
if ($column instanceof Zend_Db_Expr) {
continue;
}
$table = $this->aliasToTableName(
$this->hasAliasName($alias) ? $alias : $this->customAliasToAlias($alias)
);
if ($table === 'hostgroups') {
$selected = true;
break;
}
}
if ($selected) {
$group[] = 'hg.hostgroup_id';
$group[] = 'hgo.object_id';
}
}
if ($this->hasJoinedVirtualTable('servicegroups')) {
$selected = false;
foreach ($this->columns as $alias => $column) {
if ($column instanceof Zend_Db_Expr) {
continue;
}
$table = $this->aliasToTableName(
$this->hasAliasName($alias) ? $alias : $this->customAliasToAlias($alias)
);
if ($table === 'servicegroups') {
$selected = true;
break;
}
}
if ($selected) {
$group[] = 'sg.servicegroup_id';
$group[] = 'sgo.object_id';
}
$groupedTables = array();
if ($this->hasJoinedVirtualTable('servicegroups')) {
$serviceGroupColumns = array_keys($this->columnMap['servicegroups']);
$selectedServiceGroupColumns = array_intersect($serviceGroupColumns, array_keys($this->columns));
if (! empty($selectedServiceGroupColumns)) {
$group[] = 'so.object_id';
$group[] = 's.service_id';
$group[] = 'sgo.object_id';
$group[] = 'sg.servicegroup_id';
$groupedTables['services'] = true;
$groupedTables['servicegroups'] = true;
}
}
if ($this->hasJoinedVirtualTable('hostgroups')) {
$hostGroupColumns = array_keys($this->columnMap['hostgroups']);
$selectedHostGroupColumns = array_intersect($hostGroupColumns, array_keys($this->columns));
if (! empty($selectedHostGroupColumns)) {
if (! isset($groupedTables['services'])) {
$group[] = 'so.object_id';
$group[] = 's.service_id';
$groupedTables['services'] = true;
}
$group[] = 'hgo.object_id';
$group[] = 'hg.hostgroup_id';
$groupedTables['hostgroups'] = true;
}
}
if (! empty($groupedTables)) {
foreach ($this->columns as $alias => $column) {
if ($column instanceof Zend_Db_Expr || $column === '(NULL)') {
continue;
}
$tableName = $this->aliasToTableName(
$this->hasAliasName($alias) ? $alias : $this->customAliasToAlias($alias)
);
if (isset($groupedTables[$tableName])) {
continue;
}
switch ($tableName) {
case 'hosts':
$group[] = 'h.host_id';
break;
case 'hoststatus':
$group[] = 'hs.hoststatus_id';
break;
case 'servicestatus':
$group[] = 'ss.servicestatus_id';
break;
default:
continue 2;
}
$groupedTables[$tableName] = true;
}
}
return $group;
}
}

View File

@ -0,0 +1,43 @@
/*! Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
.bg-state-ok,
.bg-state-up {
background-color: @colorOk;
}
.bg-state-warning {
background-color: @colorWarning;
&.handled {
background-color: @colorWarningHandled;
}
}
.bg-state-critical,
.bg-state-down {
background-color: @colorCritical;
&.handled {
background-color: @colorCriticalHandled;
}
}
.bg-state-unreachable {
background-color: @colorUnreachable;
&.handled {
background-color: @colorUnreachableHandled;
}
}
.bg-state-unknown {
background-color: @colorUnknown;
&.handled {
background-color: @colorUnknownHandled;
}
}
.bg-state-pending {
background-color: @colorPending;
}

View File

@ -842,112 +842,6 @@ table.joystick-pagination {
}
}
table.pivot {
a {
text-decoration: none;
color: black;
&:hover {
color: @colorTextDefault;
}
}
& > thead {
th {
height: 6em;
div {
margin-right: -1.5em;
padding-left: 1.3em;
span {
width: 1.5em;
margin-right: 0.25em;
margin-top: 4em;
line-height: 2em;
white-space: nowrap;
display: block;
float: left;
transform: rotate(-45deg);
transform-origin: bottom left;
-o-transform: rotate(-45deg);
-o-transform-origin: bottom left;
-ms-transform: rotate(-45deg);
-ms-transform-origin: bottom left;
-moz-transform: rotate(-45deg);
-moz-transform-origin: bottom left;
-webkit-transform: rotate(-45deg);
-webkit-transform-origin: bottom left;
//filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
abbr {
border: 0; // Remove highlighting in firefox
font-size: 0.8em;
}
}
}
}
}
& > tbody {
th {
padding: 0 14px 0 0;
white-space: nowrap;
a {
font-size: 0.8em;
}
}
td {
padding: 2px;
min-width: 1.5em;
line-height: 1.5em;
text-align: center;
a {
width: 1.5em;
height: 1.5em;
display: block;
border-radius: 0.5em;
&.state_ok {
background-color: @colorOk;
}
&.state_pending {
background-color: @colorPending;
}
&.state_warning {
background-color: @colorWarning;
&.handled {
background-color: @colorWarningHandled;
}
}
&.state_critical {
background-color: @colorCritical;
&.handled {
background-color: @colorCriticalHandled;
}
}
&.state_unknown {
background-color: @colorUnknown;
&.handled {
background-color: @colorUnknownHandled;
}
}
}
}
}
}
/* End of monitoring pivot table styles */
/* Monitoring timeline styles */

View File

@ -0,0 +1,38 @@
/*! Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
table.service-grid-table {
white-space: nowrap;
th {
a {
color: @colorMainLink;
font-weight: normal;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
td {
text-align: center;
width: 1.5em;
a {
.rounded-corners(0.4em);
display: block;
height: 1.5em;
width: 1.5em;
}
}
th.rotate-45 {
height: 6em;
div {
.transform(translate(0.4em, 2.1em) rotate(315deg));
width: 1.5em;
}
}
}

View File

@ -0,0 +1,19 @@
/*! Icinga Web 2 | (c) 2013-2015 Icinga Development Team | GPLv2+ */
.transform(@transform) {
-webkit-transform: @transform;
-moz-transform: @transform;
-ms-transform: @transform;
-o-transform: @transform;
transform: @transform;
}
.rounded-corners(@border-radius: 0.4em) {
-webkit-border-radius: @border-radius;
-moz-border-radius: @border-radius;
border-radius: @border-radius;
-webkit-background-clip: padding-box;
-moz-background-clip: padding;
background-clip: padding-box;
}