commit
e871ba4d4e
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
namespace Icinga\Module\Director\Controllers;
|
namespace Icinga\Module\Director\Controllers;
|
||||||
|
|
||||||
|
use gipfl\IcingaWeb2\Link;
|
||||||
use Icinga\Module\Director\Objects\IcingaEndpoint;
|
use Icinga\Module\Director\Objects\IcingaEndpoint;
|
||||||
use Icinga\Module\Director\PlainObjectRenderer;
|
use Icinga\Module\Director\PlainObjectRenderer;
|
||||||
use Icinga\Module\Director\Web\Controller\ActionController;
|
use Icinga\Module\Director\Web\Controller\ActionController;
|
||||||
|
@ -10,9 +11,9 @@ use Icinga\Module\Director\Web\Table\CoreApiObjectsTable;
|
||||||
use Icinga\Module\Director\Web\Table\CoreApiPrototypesTable;
|
use Icinga\Module\Director\Web\Table\CoreApiPrototypesTable;
|
||||||
use Icinga\Module\Director\Web\Tabs\ObjectTabs;
|
use Icinga\Module\Director\Web\Tabs\ObjectTabs;
|
||||||
use Icinga\Module\Director\Web\Tree\InspectTreeRenderer;
|
use Icinga\Module\Director\Web\Tree\InspectTreeRenderer;
|
||||||
use ipl\Html\Html;
|
|
||||||
use gipfl\IcingaWeb2\Link;
|
|
||||||
use Icinga\Module\Director\Web\Widget\IcingaObjectInspection;
|
use Icinga\Module\Director\Web\Widget\IcingaObjectInspection;
|
||||||
|
use Icinga\Module\Director\Web\Widget\InspectPackages;
|
||||||
|
use ipl\Html\Html;
|
||||||
|
|
||||||
class InspectController extends ActionController
|
class InspectController extends ActionController
|
||||||
{
|
{
|
||||||
|
@ -24,9 +25,7 @@ class InspectController extends ActionController
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws \Icinga\Exception\Http\HttpNotFoundException
|
* @throws \Icinga\Exception\NotFoundError
|
||||||
* @throws \Icinga\Exception\IcingaException
|
|
||||||
* @throws \Icinga\Exception\ProgrammingError
|
|
||||||
*/
|
*/
|
||||||
public function typesAction()
|
public function typesAction()
|
||||||
{
|
{
|
||||||
|
@ -55,8 +54,7 @@ class InspectController extends ActionController
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws \Icinga\Exception\IcingaException
|
* @throws \Icinga\Exception\NotFoundError
|
||||||
* @throws \Icinga\Exception\ProgrammingError
|
|
||||||
*/
|
*/
|
||||||
public function typeAction()
|
public function typeAction()
|
||||||
{
|
{
|
||||||
|
@ -95,9 +93,7 @@ class InspectController extends ActionController
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws \Icinga\Exception\ConfigurationError
|
* @throws \Icinga\Exception\NotFoundError
|
||||||
* @throws \Icinga\Exception\IcingaException
|
|
||||||
* @throws \Icinga\Exception\ProgrammingError
|
|
||||||
*/
|
*/
|
||||||
public function objectAction()
|
public function objectAction()
|
||||||
{
|
{
|
||||||
|
@ -116,7 +112,6 @@ class InspectController extends ActionController
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param IcingaEndpoint $endpoint
|
* @param IcingaEndpoint $endpoint
|
||||||
* @throws \Icinga\Exception\IcingaException
|
|
||||||
*/
|
*/
|
||||||
protected function showEndpointInformation(IcingaEndpoint $endpoint)
|
protected function showEndpointInformation(IcingaEndpoint $endpoint)
|
||||||
{
|
{
|
||||||
|
@ -132,8 +127,6 @@ class InspectController extends ActionController
|
||||||
/**
|
/**
|
||||||
* @param IcingaEndpoint $endpoint
|
* @param IcingaEndpoint $endpoint
|
||||||
* @return Link
|
* @return Link
|
||||||
* @throws \Icinga\Exception\ProgrammingError
|
|
||||||
* @throws \Icinga\Exception\IcingaException
|
|
||||||
*/
|
*/
|
||||||
protected function linkToEndpoint(IcingaEndpoint $endpoint)
|
protected function linkToEndpoint(IcingaEndpoint $endpoint)
|
||||||
{
|
{
|
||||||
|
@ -143,8 +136,7 @@ class InspectController extends ActionController
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws \Icinga\Exception\IcingaException
|
* @throws \Icinga\Exception\NotFoundError
|
||||||
* @throws \Icinga\Exception\ProgrammingError
|
|
||||||
*/
|
*/
|
||||||
public function statusAction()
|
public function statusAction()
|
||||||
{
|
{
|
||||||
|
@ -157,10 +149,40 @@ class InspectController extends ActionController
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \Icinga\Exception\NotFoundError
|
||||||
|
*/
|
||||||
|
public function packagesAction()
|
||||||
|
{
|
||||||
|
$db = $this->db();
|
||||||
|
$endpointName = $this->params->get('endpoint');
|
||||||
|
$package = $this->params->get('package');
|
||||||
|
$stage = $this->params->get('stage');
|
||||||
|
$file = $this->params->get('file');
|
||||||
|
if ($endpointName === null) {
|
||||||
|
$endpoint = null;
|
||||||
|
} else {
|
||||||
|
$endpoint = IcingaEndpoint::load($endpointName, $db);
|
||||||
|
}
|
||||||
|
if ($endpoint === null) {
|
||||||
|
$this->addSingleTab($this->translate('Inspect Packages'));
|
||||||
|
} elseif ($file !== null) {
|
||||||
|
$this->addSingleTab($this->translate('Inspect File Content'));
|
||||||
|
} else {
|
||||||
|
$this->tabs(
|
||||||
|
new ObjectTabs('endpoint', $this->Auth(), $endpoint)
|
||||||
|
)->activate('packages');
|
||||||
|
}
|
||||||
|
$widget = new InspectPackages($this->db(), 'director/inspect/packages');
|
||||||
|
$this->addTitle($widget->getTitle($endpoint, $package, $stage, $file));
|
||||||
|
if ($file === null) {
|
||||||
|
$this->actions()->add($widget->getBreadCrumb($endpoint, $package, $stage));
|
||||||
|
}
|
||||||
|
$this->content()->add($widget->getContent($endpoint, $package, $stage, $file));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return IcingaEndpoint
|
* @return IcingaEndpoint
|
||||||
* @throws \Icinga\Exception\ConfigurationError
|
|
||||||
* @throws \Icinga\Exception\IcingaException
|
|
||||||
* @throws \Icinga\Exception\NotFoundError
|
* @throws \Icinga\Exception\NotFoundError
|
||||||
*/
|
*/
|
||||||
protected function endpoint()
|
protected function endpoint()
|
||||||
|
|
|
@ -14,6 +14,7 @@ next (will be 1.8.0)
|
||||||
### User Interface
|
### User Interface
|
||||||
* FIX: It's now possible to set Endpoint ports > 32767 on PostgreSQL (#928)
|
* FIX: It's now possible to set Endpoint ports > 32767 on PostgreSQL (#928)
|
||||||
* FEATURE: Data Fields can now be grouped into categories (#1969)
|
* FEATURE: Data Fields can now be grouped into categories (#1969)
|
||||||
|
* FEATURE: Inspect is now available for Packages, Stages and Files (#1995)
|
||||||
|
|
||||||
1.7.2
|
1.7.2
|
||||||
-----
|
-----
|
||||||
|
|
|
@ -708,24 +708,30 @@ constants
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function listStageFiles($stage)
|
public function listStageFiles($stage, $packageName = null)
|
||||||
{
|
{
|
||||||
|
if ($packageName === null) {
|
||||||
|
$packageName = $this->getPackageName();
|
||||||
|
}
|
||||||
return array_keys(
|
return array_keys(
|
||||||
$this->client()->get(sprintf(
|
$this->client()->get(\sprintf(
|
||||||
'config/stages/%s/%s',
|
'config/stages/%s/%s',
|
||||||
urlencode($this->getPackageName()),
|
\urlencode($packageName),
|
||||||
urlencode($stage)
|
\urlencode($stage)
|
||||||
))->getResult('name', array('type' => 'file'))
|
))->getResult('name', array('type' => 'file'))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getStagedFile($stage, $file)
|
public function getStagedFile($stage, $file, $packageName = null)
|
||||||
{
|
{
|
||||||
return $this->client()->getRaw(sprintf(
|
if ($packageName === null) {
|
||||||
|
$packageName = $this->getPackageName();
|
||||||
|
}
|
||||||
|
return $this->client()->getRaw(\sprintf(
|
||||||
'config/files/%s/%s/%s',
|
'config/files/%s/%s/%s',
|
||||||
urlencode($this->getPackageName()),
|
\urlencode($packageName),
|
||||||
urlencode($stage),
|
\urlencode($stage),
|
||||||
urlencode($file)
|
\urlencode($file)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -192,6 +192,26 @@ class Db extends DbConnection
|
||||||
return $db->fetchOne($query) > 0;
|
return $db->fetchOne($query) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getEndpointNamesInDeploymentZone()
|
||||||
|
{
|
||||||
|
$db = $this->db();
|
||||||
|
$query = $db->select()->from(
|
||||||
|
array('z' => 'icinga_zone'),
|
||||||
|
array('object_name' => 'e.object_name')
|
||||||
|
)->join(
|
||||||
|
array('e' => 'icinga_endpoint'),
|
||||||
|
'e.zone_id = z.id',
|
||||||
|
array()
|
||||||
|
)->join(
|
||||||
|
array('au' => 'icinga_apiuser'),
|
||||||
|
'e.apiuser_id = au.id',
|
||||||
|
array()
|
||||||
|
)->where('z.object_name = ?', $this->getMasterZoneName())
|
||||||
|
->order('e.object_name ASC');
|
||||||
|
|
||||||
|
return $db->fetchCol($query) ?: [];
|
||||||
|
}
|
||||||
|
|
||||||
public function getDeploymentEndpointName()
|
public function getDeploymentEndpointName()
|
||||||
{
|
{
|
||||||
$db = $this->db();
|
$db = $this->db();
|
||||||
|
|
|
@ -131,6 +131,11 @@ class ObjectTabs extends Tabs
|
||||||
'urlParams' => ['endpoint' => $object->getObjectName()],
|
'urlParams' => ['endpoint' => $object->getObjectName()],
|
||||||
'label' => $this->translate('Inspect')
|
'label' => $this->translate('Inspect')
|
||||||
]);
|
]);
|
||||||
|
$this->add('packages', [
|
||||||
|
'url' => 'director/inspect/packages',
|
||||||
|
'urlParams' => ['endpoint' => $object->getObjectName()],
|
||||||
|
'label' => $this->translate('Packages')
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($object->getShortTableName() === 'host') {
|
if ($object->getShortTableName() === 'host') {
|
||||||
|
|
|
@ -0,0 +1,174 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Icinga\Module\Director\Web\Widget;
|
||||||
|
|
||||||
|
use gipfl\IcingaWeb2\Link;
|
||||||
|
use gipfl\Translation\TranslationHelper;
|
||||||
|
use Icinga\Module\Director\Db;
|
||||||
|
use Icinga\Module\Director\Objects\IcingaEndpoint;
|
||||||
|
use ipl\Html\Html;
|
||||||
|
use ipl\Html\Table;
|
||||||
|
|
||||||
|
class InspectPackages
|
||||||
|
{
|
||||||
|
use TranslationHelper;
|
||||||
|
|
||||||
|
/** @var Db */
|
||||||
|
protected $db;
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
protected $baseUrl;
|
||||||
|
|
||||||
|
public function __construct(Db $db, $baseUrl)
|
||||||
|
{
|
||||||
|
$this->db = $db;
|
||||||
|
$this->baseUrl = $baseUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContent(IcingaEndpoint $endpoint = null, $package = null, $stage = null, $file = null)
|
||||||
|
{
|
||||||
|
if ($endpoint === null) {
|
||||||
|
return $this->getRootEndpoints();
|
||||||
|
} elseif ($package === null) {
|
||||||
|
return $this->getPackages($endpoint);
|
||||||
|
} elseif ($stage === null) {
|
||||||
|
return $this->getStages($endpoint, $package);
|
||||||
|
} elseif ($file === null) {
|
||||||
|
return $this->getFiles($endpoint, $package, $stage);
|
||||||
|
} else {
|
||||||
|
return $this->getFile($endpoint, $package, $stage, $file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle(IcingaEndpoint $endpoint = null, $package = null, $stage = null, $file = null)
|
||||||
|
{
|
||||||
|
if ($endpoint === null) {
|
||||||
|
return $this->translate('Endpoint in your Root Zone');
|
||||||
|
} elseif ($package === null) {
|
||||||
|
return \sprintf($this->translate('Packages on Endpoint: %s'), $endpoint->getObjectName());
|
||||||
|
} elseif ($stage === null) {
|
||||||
|
return \sprintf($this->translate('Stages in Package: %s'), $package);
|
||||||
|
} elseif ($file === null) {
|
||||||
|
return \sprintf($this->translate('Files in Stage: %s'), $stage);
|
||||||
|
} else {
|
||||||
|
return \sprintf($this->translate('File Content: %s'), $file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBreadCrumb(IcingaEndpoint $endpoint = null, $package = null, $stage = null)
|
||||||
|
{
|
||||||
|
$parts = [
|
||||||
|
'endpoint' => $endpoint === null ? null : $endpoint->getObjectName(),
|
||||||
|
'package' => $package,
|
||||||
|
'stage' => $stage,
|
||||||
|
];
|
||||||
|
|
||||||
|
$params = [];
|
||||||
|
// No root zone link for now:
|
||||||
|
// $result = [Link::create($this->translate('Root Zone'), $this->baseUrl)];
|
||||||
|
$result = [Html::tag('a', ['href' => '#'], $this->translate('Root Zone'))];
|
||||||
|
foreach ($parts as $name => $value) {
|
||||||
|
if ($value === null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$params[$name] = $value;
|
||||||
|
$result[] = Link::create($value, $this->baseUrl, $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Html::tag('ul', ['class' => 'breadcrumb'], Html::wrapEach($result, 'li'));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getRootEndpoints()
|
||||||
|
{
|
||||||
|
$table = $this->prepareTable();
|
||||||
|
foreach ($this->db->getEndpointNamesInDeploymentZone() as $name) {
|
||||||
|
$table->add(Table::row([
|
||||||
|
Link::create($name, $this->baseUrl, [
|
||||||
|
'endpoint' => $name,
|
||||||
|
])
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $table;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getPackages(IcingaEndpoint $endpoint)
|
||||||
|
{
|
||||||
|
$table = $this->prepareTable();
|
||||||
|
$api = $endpoint->api();
|
||||||
|
foreach ($api->getPackages() as $package) {
|
||||||
|
$table->add(Table::row([
|
||||||
|
Link::create($package->name, $this->baseUrl, [
|
||||||
|
'endpoint' => $endpoint->getObjectName(),
|
||||||
|
'package' => $package->name,
|
||||||
|
])
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $table;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getStages(IcingaEndpoint $endpoint, $packageName)
|
||||||
|
{
|
||||||
|
$table = $this->prepareTable();
|
||||||
|
$api = $endpoint->api();
|
||||||
|
foreach ($api->getPackages() as $package) {
|
||||||
|
if ($package->name !== $packageName) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
foreach ($package->stages as $stage) {
|
||||||
|
$label = [$stage];
|
||||||
|
if ($stage === $package->{'active-stage'}) {
|
||||||
|
$label[] = Html::tag('small', [' (', $this->translate('active'), ')']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$table->add(Table::row([
|
||||||
|
Link::create($label, $this->baseUrl, [
|
||||||
|
'endpoint' => $endpoint->getObjectName(),
|
||||||
|
'package' => $package->name,
|
||||||
|
'stage' => $stage
|
||||||
|
])
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $table;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getFiles(IcingaEndpoint $endpoint, $package, $stage)
|
||||||
|
{
|
||||||
|
$table = $this->prepareTable();
|
||||||
|
$table->getAttributes()->set('data-base-target', '_next');
|
||||||
|
foreach ($endpoint->api()->listStageFiles($stage, $package) as $filename) {
|
||||||
|
$table->add($table->row([
|
||||||
|
Link::create($filename, $this->baseUrl, [
|
||||||
|
'endpoint' => $endpoint->getObjectName(),
|
||||||
|
'package' => $package,
|
||||||
|
'stage' => $stage,
|
||||||
|
'file' => $filename
|
||||||
|
])
|
||||||
|
]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $table;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getFile(IcingaEndpoint $endpoint, $package, $stage, $file)
|
||||||
|
{
|
||||||
|
return Html::tag('pre', $endpoint->api()->getStagedFile($stage, $file, $package));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function prepareTable($headerCols = [])
|
||||||
|
{
|
||||||
|
$table = new Table();
|
||||||
|
$table->addAttributes([
|
||||||
|
'class' => ['common-table', 'table-row-selectable'],
|
||||||
|
'data-base-target' => '_self'
|
||||||
|
]);
|
||||||
|
if (! empty($headerCols)) {
|
||||||
|
$table->add($table::row($headerCols, null, 'th'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $table;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1453,6 +1453,132 @@ input[type=submit].icon-button {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** BEGIN breadcrumb **/
|
||||||
|
// Hint: .badges is unused right now
|
||||||
|
.breadcrumb {
|
||||||
|
list-style: none;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.badges {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0 0 0 0.5em;
|
||||||
|
.badge {
|
||||||
|
line-height: 1.25em;
|
||||||
|
font-size: 0.8em;
|
||||||
|
border: 1px solid white;
|
||||||
|
margin: -0.25em 1px 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb {
|
||||||
|
> .critical a { background: @colorCritical; }
|
||||||
|
> .critical.handled a { background: @colorCriticalHandled; }
|
||||||
|
> .unknown a { background: @colorUnknown; }
|
||||||
|
> .unknown.handled a { background: @colorUnknownHandled; }
|
||||||
|
> .warning a { background: @colorWarning; }
|
||||||
|
> .warning.handled a { background: @colorWarningHandled; }
|
||||||
|
> .ok a { background: @colorOk; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb {
|
||||||
|
> .critical a:after { border-left-color: @colorCritical; }
|
||||||
|
> .critical.handled a:after { border-left-color: @colorCriticalHandled; }
|
||||||
|
> .unknown a:after { border-left-color: @colorUnknown; }
|
||||||
|
> .unknown.handled a:after { border-left-color: @colorUnknownHandled; }
|
||||||
|
> .warning a:after { border-left-color: @colorWarning; }
|
||||||
|
> .warning.handled a:after { border-left-color: @colorWarningHandled; }
|
||||||
|
> .ok a:after { border-left-color: @colorOk; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb:after {
|
||||||
|
content:'';
|
||||||
|
display:block;
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
.breadcrumb li {
|
||||||
|
float: left;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
-ms-user-select: none;
|
||||||
|
|
||||||
|
}
|
||||||
|
.breadcrumb li a {
|
||||||
|
// color: white;
|
||||||
|
color: @icinga-blue;
|
||||||
|
margin: 0;
|
||||||
|
// font-size: 1.2em;
|
||||||
|
text-decoration: none;
|
||||||
|
padding-left: 2em;
|
||||||
|
// line-height: 1.5em;
|
||||||
|
// background: @icinga-blue;
|
||||||
|
border: 1px solid @icinga-blue;
|
||||||
|
border-top: none;
|
||||||
|
border-bottom: none;
|
||||||
|
position: relative;
|
||||||
|
display: block;
|
||||||
|
float: left;
|
||||||
|
&:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.action-bar .breadcrumb li a {
|
||||||
|
padding-left: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb li a:before, .breadcrumb li a:after {
|
||||||
|
content: " ";
|
||||||
|
display: block;
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-top: 1.3em solid transparent;
|
||||||
|
border-bottom: 1.2em solid transparent;
|
||||||
|
position: absolute;
|
||||||
|
margin-top: -1.2em;
|
||||||
|
top: 50%;
|
||||||
|
left: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb li a:before {
|
||||||
|
border-left: 1.2em solid white;
|
||||||
|
border-left: 1.2em solid @icinga-blue;
|
||||||
|
margin-left: 1px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb li a:after {
|
||||||
|
border-left: 1.2em solid @icinga-blue;
|
||||||
|
border-left: 1.2em solid white;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb li:first-child a {
|
||||||
|
padding-left: 1em;
|
||||||
|
padding-right: 0.5em;
|
||||||
|
}
|
||||||
|
.breadcrumb li:last-child a {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.breadcrumb li:last-child a:hover {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb li:not(:last-child) a:hover { background: @text-color; color: white; }
|
||||||
|
.breadcrumb li:not(:last-child) a:hover:after { border-left-color: @text-color; }
|
||||||
|
|
||||||
|
.breadcrumb li a:focus {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
/** END of breadcrumb **/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ul.filter-root {
|
ul.filter-root {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
Loading…
Reference in New Issue