diff --git a/library/Icinga/Application/Modules/Module.php b/library/Icinga/Application/Modules/Module.php index 9ec35b565..7c415e05c 100644 --- a/library/Icinga/Application/Modules/Module.php +++ b/library/Icinga/Application/Modules/Module.php @@ -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', diff --git a/library/Icinga/Data/PivotTable.php b/library/Icinga/Data/PivotTable.php index cf703ae9c..8463b483e 100644 --- a/library/Icinga/Data/PivotTable.php +++ b/library/Icinga/Data/PivotTable.php @@ -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); } } diff --git a/library/Icinga/Web/StyleSheet.php b/library/Icinga/Web/StyleSheet.php index af807c6e4..f22e66d33 100644 --- a/library/Icinga/Web/StyleSheet.php +++ b/library/Icinga/Web/StyleSheet.php @@ -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', diff --git a/library/Icinga/Web/Widget/SortBox.php b/library/Icinga/Web/Widget/SortBox.php index c9d38f2b7..862dad22c 100644 --- a/library/Icinga/Web/Widget/SortBox.php +++ b/library/Icinga/Web/Widget/SortBox.php @@ -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); } diff --git a/modules/monitoring/application/controllers/ListController.php b/modules/monitoring/application/controllers/ListController.php index 44172f1ea..7fba86647 100644 --- a/modules/monitoring/application/controllers/ListController.php +++ b/modules/monitoring/application/controllers/ListController.php @@ -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; } /** diff --git a/modules/monitoring/application/views/scripts/list/servicegrid.phtml b/modules/monitoring/application/views/scripts/list/servicegrid.phtml index 73fbfe3b5..22d730515 100644 --- a/modules/monitoring/application/views/scripts/list/servicegrid.phtml +++ b/modules/monitoring/application/views/scripts/list/servicegrid.phtml @@ -12,97 +12,84 @@ if (! $this->compact): ?>
pivot->toArray(); -if (count($pivotData) === 0) { +if (empty($pivotData)) { echo $this->translate('No services found matching the filter') . '
'; return; } - $hostFilter = '(host_name=' . implode('|host_name=', array_keys($pivotData)) . ')'; ?> - - $serviceStates): ?> - - - - -
partial( - 'joystickPagination.phtml', - 'default', - array( - 'xAxisPaginator' => $horizontalPaginator, - 'yAxisPaginator' => $verticalPaginator - ) - ); ?> -
- - - qlink( - '' . (strlen($service_description) > 18 ? substr($service_description, 0, 18) . '...' : $service_description) . '', + + + + + $serviceDisplayName): ?> + - + ) ?> + + - - - - - - - - - - - - - + $hostDisplayName): ?> + + + + + + + -
partial( + 'joystickPagination.phtml', + 'default', + array( + 'xAxisPaginator' => $horizontalPaginator, + 'yAxisPaginator' => $verticalPaginator + ) + ); ?>
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 - ); ?> - - -
-
- 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)) - ); ?> - - - escape($service->service_output); ?> - - 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 - ) - ) - ); ?> -
qlink( + $hostDisplayName, + 'monitoring/list/services?' . $serviceFilter, + array('host_name' => $hostName), + array('title' => sprintf($this->translate('List all reported services on host %s'), $hostDisplayName)) + ); + ?> + + + + protectId($service->host_name . '_' . $service->service_description . '_desc') ?> + + escape($service->service_output) ?> + + 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 + ) + ) + ); ?> +
+
diff --git a/modules/monitoring/configuration.php b/modules/monitoring/configuration.php index fa95c3785..12b8236ab 100644 --- a/modules/monitoring/configuration.php +++ b/modules/monitoring/configuration.php @@ -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'); diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HoststatusQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HoststatusQuery.php index 1d15243db..d3d4cfcb8 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/HoststatusQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/HoststatusQuery.php @@ -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; } diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/IdoQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/IdoQuery.php index c8d2e3140..122e13dc0 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/IdoQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/IdoQuery.php @@ -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 ); } } diff --git a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicestatusQuery.php b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicestatusQuery.php index c5fc6e755..21cc09baa 100644 --- a/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicestatusQuery.php +++ b/modules/monitoring/library/Monitoring/Backend/Ido/Query/ServicestatusQuery.php @@ -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; } } diff --git a/modules/monitoring/public/css/colors.less b/modules/monitoring/public/css/colors.less new file mode 100644 index 000000000..c7e5ef5c6 --- /dev/null +++ b/modules/monitoring/public/css/colors.less @@ -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; +} diff --git a/modules/monitoring/public/css/module.less b/modules/monitoring/public/css/module.less index 6ea967a1c..5c52ebacd 100644 --- a/modules/monitoring/public/css/module.less +++ b/modules/monitoring/public/css/module.less @@ -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 */ diff --git a/modules/monitoring/public/css/service-grid.less b/modules/monitoring/public/css/service-grid.less new file mode 100644 index 000000000..d89e3d5b4 --- /dev/null +++ b/modules/monitoring/public/css/service-grid.less @@ -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; + } + } +} diff --git a/public/css/icinga/mixins.less b/public/css/icinga/mixins.less new file mode 100644 index 000000000..e22b4cd51 --- /dev/null +++ b/public/css/icinga/mixins.less @@ -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; +}