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:
commit
c294283636
|
@ -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',
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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">·</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">·</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>
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue