mirror of
https://github.com/Icinga/icingaweb2-module-businessprocess.git
synced 2025-12-20 23:00:16 -05:00
Add Service Override
This commit is contained in:
parent
8f868d79c9
commit
98b6b2c6d0
13 changed files with 424 additions and 43 deletions
|
|
@ -207,6 +207,11 @@ class AddNodeForm extends QuickForm
|
|||
$this->addHostElement();
|
||||
if ($host = $this->getSentValue('host')) {
|
||||
$this->addServicesElement($host);
|
||||
$this->addServiceOverrideCheckbox();
|
||||
|
||||
if ($this->getSentValue('service_override') === '1') {
|
||||
$this->addServiceOverrideElements();
|
||||
}
|
||||
} else {
|
||||
$this->setSubmitLabel($this->translate('Next'));
|
||||
}
|
||||
|
|
@ -253,6 +258,36 @@ class AddNodeForm extends QuickForm
|
|||
]);
|
||||
}
|
||||
|
||||
protected function addServiceOverrideCheckbox()
|
||||
{
|
||||
$this->addElement('checkbox', 'service_override', [
|
||||
'ignore' => true,
|
||||
'class' => 'autosubmit',
|
||||
'label' => $this->translate('Override Service State'),
|
||||
'description' => $this->translate('Enable service state overrides')
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addServiceOverrideElements()
|
||||
{
|
||||
$elements = [];
|
||||
foreach ($this->enumServiceStateList() as $state => $stateName) {
|
||||
if ($state === 0) {
|
||||
continue;
|
||||
}
|
||||
$this->addElement('select', $stateName, [
|
||||
'label' => $this->translate($stateName),
|
||||
'ignore' => true,
|
||||
'multiOptions' => $this->optionalEnum($this->enumServiceStateList()),
|
||||
]);
|
||||
$elements[] = $stateName;
|
||||
}
|
||||
|
||||
$this->addSimpleDisplayGroup($elements, 'override_group', [
|
||||
'legend' => $this->translate('State Overrides')
|
||||
]);
|
||||
}
|
||||
|
||||
protected function selectProcess()
|
||||
{
|
||||
if ($this->hasParentNode()) {
|
||||
|
|
@ -391,6 +426,34 @@ class AddNodeForm extends QuickForm
|
|||
return $services;
|
||||
}
|
||||
|
||||
protected function enumServiceStateList()
|
||||
{
|
||||
$serviceStateList = [
|
||||
0 => $this->translate('OK'),
|
||||
1 => $this->translate('WARNING'),
|
||||
2 => $this->translate('CRITICAL'),
|
||||
3 => $this->translate('UNKNOWN'),
|
||||
99 => $this->translate('PENDING'),
|
||||
];
|
||||
|
||||
return $serviceStateList;
|
||||
}
|
||||
|
||||
protected function statesToString()
|
||||
{
|
||||
$stateString = null;
|
||||
foreach ($this->enumServiceStateList() as $state => $stateName) {
|
||||
if ($this->getValue($stateName) !== null && $this->getValue($stateName) !== '') {
|
||||
if ($stateString !== null) {
|
||||
$stateString .= ',';
|
||||
}
|
||||
$stateString .= $state . '-' . $this->getValue($stateName);
|
||||
}
|
||||
}
|
||||
|
||||
return $stateString;
|
||||
}
|
||||
|
||||
protected function hasProcesses()
|
||||
{
|
||||
return count($this->enumProcesses()) > 0;
|
||||
|
|
@ -464,8 +527,18 @@ class AddNodeForm extends QuickForm
|
|||
{
|
||||
$changes = ProcessChanges::construct($this->bp, $this->session);
|
||||
switch ($this->getValue('node_type')) {
|
||||
case 'host':
|
||||
case 'service':
|
||||
if ($this->statesToString() !== null) {
|
||||
$services = [];
|
||||
foreach ($this->getValue('children') as $service) {
|
||||
$services[] = $service . ':' . $this->statesToString();
|
||||
}
|
||||
$changes->addChildrenToNode($services, $this->parent);
|
||||
} else {
|
||||
$changes->addChildrenToNode($this->getValue('children'), $this->parent);
|
||||
}
|
||||
break;
|
||||
case 'host':
|
||||
case 'process':
|
||||
if ($this->hasParentNode()) {
|
||||
$changes->addChildrenToNode($this->getValue('children'), $this->parent);
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ class EditNodeForm extends QuickForm
|
|||
|
||||
protected $host;
|
||||
|
||||
protected $statesOverride = [];
|
||||
|
||||
/** @var SessionNamespace */
|
||||
protected $session;
|
||||
|
||||
|
|
@ -43,7 +45,8 @@ class EditNodeForm extends QuickForm
|
|||
{
|
||||
$this->host = substr($this->getNode()->getName(), 0, strpos($this->getNode()->getName(), ';'));
|
||||
if ($this->isService()) {
|
||||
$this->service = substr($this->getNode()->getName(), strpos($this->getNode()->getName(), ';') + 1);
|
||||
$this->service = $this->getNode()->getShortName();
|
||||
$this->statesOverride = $this->getNode()->getStatesOverride();
|
||||
}
|
||||
|
||||
$view = $this->getView();
|
||||
|
|
@ -190,8 +193,16 @@ class EditNodeForm extends QuickForm
|
|||
|
||||
if ($this->getSentValue('hosts') === null) {
|
||||
$this->addServicesElement($this->host);
|
||||
$this->addServiceOverrideCheckbox();
|
||||
if (!empty($this->statesOverride) || $this->getSentValue('service_override') === '1') {
|
||||
$this->addServiceOverrideElements();
|
||||
}
|
||||
} elseif ($host = $this->getSentValue('hosts')) {
|
||||
$this->addServicesElement($host);
|
||||
$this->addServiceOverrideCheckbox();
|
||||
if ($this->getSentValue('service_override') === '1') {
|
||||
$this->addServiceOverrideElements();
|
||||
}
|
||||
} else {
|
||||
$this->setSubmitLabel($this->translate('Next'));
|
||||
}
|
||||
|
|
@ -214,14 +225,58 @@ class EditNodeForm extends QuickForm
|
|||
{
|
||||
$this->addElement('select', 'children', array(
|
||||
'required' => true,
|
||||
'value' => $this->getNode()->getName(),
|
||||
'value' => $this->service,
|
||||
'multiOptions' => $this->enumServiceList($host),
|
||||
'label' => $this->translate('Service'),
|
||||
'description' => $this->translate('The service for this business process node'),
|
||||
'validators' => [[new NoDuplicateChildrenValidator($this, $this->bp, $this->parent), true]]
|
||||
'validators' => [
|
||||
['Callback', true, [
|
||||
'callback' => function ($value) {
|
||||
if ($this->node->getShortName() === $value) {
|
||||
return true;
|
||||
}
|
||||
return ! $this->parent->hasMatchingChild($value);
|
||||
},
|
||||
'messages' => [
|
||||
'callbackValue' => $this->translate('%value% is already defined in this process')
|
||||
]
|
||||
]]
|
||||
]
|
||||
));
|
||||
}
|
||||
|
||||
protected function addServiceOverrideCheckbox()
|
||||
{
|
||||
$this->addElement('checkbox', 'service_override', [
|
||||
'ignore' => true,
|
||||
'class' => 'autosubmit',
|
||||
'value' => ! empty($this->statesOverride) ? '1' : null,
|
||||
'label' => $this->translate('Override Service State'),
|
||||
'description' => $this->translate('Enable service state overrides')
|
||||
]);
|
||||
}
|
||||
|
||||
protected function addServiceOverrideElements()
|
||||
{
|
||||
$elements = [];
|
||||
foreach ($this->enumServiceStateList() as $state => $stateName) {
|
||||
if ($state == 0) {
|
||||
continue;
|
||||
}
|
||||
$this->addElement('select', $stateName, [
|
||||
'label' => $this->translate($stateName),
|
||||
'value' => isset($this->statesOverride[$state]) ? $this->statesOverride[$state] : null,
|
||||
'ignore' => true,
|
||||
'multiOptions' => $this->optionalEnum($this->enumServiceStateList()),
|
||||
]);
|
||||
$elements[] = $stateName;
|
||||
}
|
||||
|
||||
$this->addSimpleDisplayGroup($elements, 'override_group', [
|
||||
'legend' => $this->translate('State Overrides')
|
||||
]);
|
||||
}
|
||||
|
||||
protected function selectProcess()
|
||||
{
|
||||
$this->addElement('multiselect', 'children', array(
|
||||
|
|
@ -341,6 +396,34 @@ class EditNodeForm extends QuickForm
|
|||
return $services;
|
||||
}
|
||||
|
||||
protected function enumServiceStateList()
|
||||
{
|
||||
$serviceStateList = [
|
||||
0 => $this->translate('OK'),
|
||||
1 => $this->translate('WARNING'),
|
||||
2 => $this->translate('CRITICAL'),
|
||||
3 => $this->translate('UNKNOWN'),
|
||||
99 => $this->translate('PENDING'),
|
||||
];
|
||||
|
||||
return $serviceStateList;
|
||||
}
|
||||
|
||||
protected function statesToString()
|
||||
{
|
||||
$stateString = null;
|
||||
foreach ($this->enumServiceStateList() as $state => $stateName) {
|
||||
if ($this->getValue($stateName) !== null && $this->getValue($stateName) !== '') {
|
||||
if ($stateString !== null) {
|
||||
$stateString .= ',';
|
||||
}
|
||||
$stateString .= $state . '-' . $this->getValue($stateName);
|
||||
}
|
||||
}
|
||||
|
||||
return $stateString;
|
||||
}
|
||||
|
||||
protected function hasProcesses()
|
||||
{
|
||||
return count($this->enumProcesses()) > 0;
|
||||
|
|
@ -405,8 +488,17 @@ class EditNodeForm extends QuickForm
|
|||
$changes->deleteNode($this->node, $this->parent->getName());
|
||||
|
||||
switch ($this->getValue('node_type')) {
|
||||
case 'host':
|
||||
case 'service':
|
||||
if ($this->statesToString() !== null) {
|
||||
$changes->addChildrenToNode(
|
||||
$this->getValue('children') . ':' . $this->statesToString(),
|
||||
$this->parent
|
||||
);
|
||||
} else {
|
||||
$changes->addChildrenToNode($this->getValue('children'), $this->parent);
|
||||
}
|
||||
break;
|
||||
case 'host':
|
||||
case 'process':
|
||||
$changes->addChildrenToNode($this->getValue('children'), $this->parent);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -460,16 +460,21 @@ class BpConfig
|
|||
return array_key_exists($name, $this->root_nodes);
|
||||
}
|
||||
|
||||
public function createService($host, $service)
|
||||
public function createService($host, $service, $statesOverride = null)
|
||||
{
|
||||
$node = new ServiceNode(
|
||||
(object) array(
|
||||
'hostname' => $host,
|
||||
'service' => $service
|
||||
'service' => $service,
|
||||
'statesOverride'=> $statesOverride
|
||||
)
|
||||
);
|
||||
$node->setBpConfig($this);
|
||||
if (isset($statesOverride)) {
|
||||
$this->nodes[$host . ';' . $service . ':' . $statesOverride] = $node;
|
||||
} else {
|
||||
$this->nodes[$host . ';' . $service] = $node;
|
||||
}
|
||||
$this->hosts[$host] = true;
|
||||
return $node;
|
||||
}
|
||||
|
|
@ -657,6 +662,19 @@ class BpConfig
|
|||
);
|
||||
}
|
||||
|
||||
public function getMatchingNodeNams($name)
|
||||
{
|
||||
$matching = [];
|
||||
|
||||
foreach ($this->nodes as $nodeName => $node) {
|
||||
if ($name === $node->getShortName()) {
|
||||
$matching[] = $nodeName;
|
||||
}
|
||||
}
|
||||
|
||||
return $matching;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BpNode
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -142,6 +142,11 @@ class BpNode extends Node
|
|||
return in_array($name, $this->getChildNames());
|
||||
}
|
||||
|
||||
public function hasMatchingChild($name)
|
||||
{
|
||||
return in_array($name, $this->getChildShortNames());
|
||||
}
|
||||
|
||||
public function removeChild($name)
|
||||
{
|
||||
if (($key = array_search($name, $this->getChildNames())) !== false) {
|
||||
|
|
@ -498,6 +503,20 @@ class BpNode extends Node
|
|||
return $this->children;
|
||||
}
|
||||
|
||||
protected function getChildShortNames()
|
||||
{
|
||||
$ChidrenShortNames = array();
|
||||
|
||||
foreach ($this->getChildren() as $child) {
|
||||
$name = $child->getShortName();
|
||||
array_push($ChidrenShortNames, $name);
|
||||
}
|
||||
|
||||
return $ChidrenShortNames;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Reorder this node's children, in case manual order is not applied
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ class NodeAddChildrenAction extends NodeAction
|
|||
foreach ($this->children as $name) {
|
||||
if (! $config->hasNode($name) || $config->getNode($name)->getBpConfig()->getName() !== $config->getName()) {
|
||||
if (strpos($name, ';') !== false) {
|
||||
if (strpos($name, ':') !== false) {
|
||||
list($host, $service, $statesOverride) = preg_split('~[;:]~', $name, 3);
|
||||
$config->createService($host, $service, $statesOverride);
|
||||
} else {
|
||||
list($host, $service) = preg_split('/;/', $name, 2);
|
||||
|
||||
if ($service === 'Hoststatus') {
|
||||
|
|
@ -41,6 +45,7 @@ class NodeAddChildrenAction extends NodeAction
|
|||
} else {
|
||||
$config->createService($host, $service);
|
||||
}
|
||||
}
|
||||
} elseif ($name[0] === '@' && strpos($name, ':') !== false) {
|
||||
list($configName, $nodeName) = preg_split('~:\s*~', substr($name, 1), 2);
|
||||
$config->createImportedNode($configName, $nodeName);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,8 @@ abstract class Node
|
|||
self::NODE_EMPTY => 0
|
||||
);
|
||||
|
||||
protected $stateOverride = [];
|
||||
|
||||
/** @var string Alias of the node */
|
||||
protected $alias;
|
||||
|
||||
|
|
@ -197,6 +199,28 @@ abstract class Node
|
|||
return $this;
|
||||
}
|
||||
|
||||
protected function setStateOverride($state, $oveRrideState)
|
||||
{
|
||||
$this->stateOverride[(int) $state] = (int) $oveRrideState;
|
||||
}
|
||||
|
||||
public function setStatesOverride($statesOverrideString)
|
||||
{
|
||||
$overrides = explode(',', $statesOverrideString);
|
||||
|
||||
foreach ($overrides as $overrideState) {
|
||||
if (strpos($overrideState, '-') !== false) {
|
||||
list($key, $value) = explode('-', $overrideState);
|
||||
$this->setStateOverride($key, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getStatesOverride()
|
||||
{
|
||||
return $this->stateOverride;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forget my state
|
||||
*
|
||||
|
|
@ -246,6 +270,24 @@ abstract class Node
|
|||
);
|
||||
}
|
||||
|
||||
if (isset($this->stateOverride[$this->state])) {
|
||||
return $this->stateOverride[$this->state];
|
||||
} else {
|
||||
return $this->state;
|
||||
}
|
||||
}
|
||||
|
||||
public function getRealState()
|
||||
{
|
||||
if ($this->state === null) {
|
||||
throw new ProgrammingError(
|
||||
sprintf(
|
||||
'Node %s is unable to retrieve it\'s state',
|
||||
$this->name
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return $this->state;
|
||||
}
|
||||
|
||||
|
|
@ -364,6 +406,11 @@ abstract class Node
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getShortName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function hasParents()
|
||||
{
|
||||
return count($this->parents) > 0;
|
||||
|
|
|
|||
|
|
@ -113,6 +113,15 @@ class NodeTile extends BaseHtmlElement
|
|||
));
|
||||
}
|
||||
|
||||
if ($node instanceof ServiceNode && $node->getRealState() !== $node->getState()) {
|
||||
$this->add((new StateBall(strtolower($node->getStateName($node->getRealState()))))->addAttributes([
|
||||
'title' => sprintf(
|
||||
'%s',
|
||||
$node->getStateName($node->getRealState())
|
||||
)
|
||||
]));
|
||||
}
|
||||
|
||||
if ($node instanceof BpNode && !$renderer->isBreadcrumb()) {
|
||||
$this->add(Html::tag(
|
||||
'p',
|
||||
|
|
|
|||
|
|
@ -136,6 +136,45 @@ class TreeRenderer extends Renderer
|
|||
return $icons;
|
||||
}
|
||||
|
||||
public function getNodeRealState(Node $node)
|
||||
{
|
||||
$realState = [];
|
||||
$realState[] = Html::tag('i', ['class' => 'icon icon-flash']);
|
||||
$realState[] = (new StateBall(strtolower($node->getStateName($node->getRealState()))))->addAttributes([
|
||||
'title' => sprintf(
|
||||
'%s',
|
||||
$node->getStateName($node->getRealState())
|
||||
)
|
||||
]);
|
||||
|
||||
return $realState;
|
||||
}
|
||||
|
||||
public function getNodeStateOverride(Node $node)
|
||||
{
|
||||
$statesOverrights = [];
|
||||
|
||||
$states = $node->getStatesOverride();
|
||||
foreach ($states as $originalState => $overrideState) {
|
||||
$statesOverrights[] = (new StateBall(strtolower($node->getStateName($originalState))))->addAttributes([
|
||||
'title' => sprintf(
|
||||
'%s',
|
||||
$node->getStateName($originalState)
|
||||
)
|
||||
]);
|
||||
$statesOverrights[] = Html::tag('i', ['class' => 'icon icon-right-small']);
|
||||
$statesOverrights[] = (new StateBall(strtolower($node->getStateName($overrideState))))->addAttributes([
|
||||
'title' => sprintf(
|
||||
'%s',
|
||||
$node->getStateName($overrideState)
|
||||
),
|
||||
'class' => 'last'
|
||||
]);
|
||||
}
|
||||
|
||||
return $statesOverrights;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BpConfig $bp
|
||||
* @param Node $node
|
||||
|
|
@ -243,10 +282,24 @@ class TreeRenderer extends Renderer
|
|||
$link->getAttributes()->set('data-base-target', '_next');
|
||||
$li->add($link);
|
||||
|
||||
if (! $this->isLocked() && $node->getBpConfig()->getName() === $this->getBusinessProcess()->getName()) {
|
||||
$li->add($this->getActionIcons($bp, $node));
|
||||
if ($node->getRealState() !== $node->getState()) {
|
||||
$li->add($this->getNodeRealState($node));
|
||||
}
|
||||
|
||||
$leftAlign = Html::tag('div', [
|
||||
'class' => 'left-side',
|
||||
]);
|
||||
|
||||
if (!empty($node->getStatesOverride())) {
|
||||
$leftAlign->add($this->getNodeStateOverride($node));
|
||||
}
|
||||
|
||||
if (! $this->isLocked() && $node->getBpConfig()->getName() === $this->getBusinessProcess()->getName()) {
|
||||
$leftAlign->add($this->getActionIcons($bp, $node));
|
||||
}
|
||||
|
||||
$li->add($leftAlign);
|
||||
|
||||
return $li;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,12 @@ class ServiceNode extends MonitoredNode
|
|||
|
||||
public function __construct($object)
|
||||
{
|
||||
if (isset($object->statesOverride)) {
|
||||
$this->name = $object->hostname . ';' . $object->service . ':' . $object->statesOverride;
|
||||
$this->setStatesOverride($object->statesOverride);
|
||||
} else {
|
||||
$this->name = $object->hostname . ';' . $object->service;
|
||||
}
|
||||
$this->hostname = $object->hostname;
|
||||
$this->service = $object->service;
|
||||
if (isset($object->state)) {
|
||||
|
|
@ -68,6 +73,11 @@ class ServiceNode extends MonitoredNode
|
|||
return $this->getHostAlias() . ': ' . $this->alias;
|
||||
}
|
||||
|
||||
public function getShortName()
|
||||
{
|
||||
return $this->hostname . ';' . $this->service;
|
||||
}
|
||||
|
||||
public function getUrl()
|
||||
{
|
||||
$params = array(
|
||||
|
|
|
|||
|
|
@ -122,12 +122,14 @@ class MonitoringState
|
|||
$key .= ';Hoststatus';
|
||||
}
|
||||
|
||||
$nodesNames = $config->getMatchingNodeNams($key);
|
||||
foreach ($nodesNames as $nodeName) {
|
||||
// We fetch more states than we need, so skip unknown ones
|
||||
if (! $config->hasNode($key)) {
|
||||
if (! $config->hasNode($nodeName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$node = $config->getNode($key);
|
||||
$node = $config->getNode($nodeName);
|
||||
|
||||
if ($row->state !== null) {
|
||||
$node->setState($row->state)->setMissing(false);
|
||||
|
|
@ -141,6 +143,7 @@ class MonitoringState
|
|||
if ((int) $row->ack === 1) {
|
||||
$node->setAck(true);
|
||||
}
|
||||
}
|
||||
|
||||
$node->setAlias($row->display_name);
|
||||
|
||||
|
|
|
|||
|
|
@ -334,14 +334,20 @@ class LegacyConfigParser
|
|||
if (strpos($val, ';') !== false) {
|
||||
if ($bp->hasNode($val)) {
|
||||
$node->addChild($bp->getNode($val));
|
||||
} else {
|
||||
if (strpos($val, ':') !== false) {
|
||||
list($host, $service, $statesOverride) = preg_split('~[;:]~', $val, 3);
|
||||
$node->addChild($bp->createService($host, $service, $statesOverride));
|
||||
} else {
|
||||
list($host, $service) = preg_split('~;~', $val, 2);
|
||||
|
||||
if ($service === 'Hoststatus') {
|
||||
$node->addChild($bp->createHost($host));
|
||||
} else {
|
||||
$node->addChild($bp->createService($host, $service));
|
||||
}
|
||||
}
|
||||
}
|
||||
} elseif ($val[0] === '@') {
|
||||
if (strpos($val, ':') === false) {
|
||||
throw new ConfigurationError(
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class NoDuplicateChildrenValidator extends Zend_Validate_Abstract
|
|||
} elseif ($this->form instanceof EditNodeForm && $this->form->getNode()->getName() === $value) {
|
||||
$found = false;
|
||||
} else {
|
||||
$found = $this->parent->hasChild($value);
|
||||
$found = $this->parent->hasMatchingChild($value);
|
||||
}
|
||||
|
||||
if (! $found) {
|
||||
|
|
|
|||
|
|
@ -214,12 +214,12 @@ ul.bp {
|
|||
margin-right: .5em;
|
||||
}
|
||||
|
||||
> a.action-link {
|
||||
margin-left: auto; // Let the first action link move everything to the right
|
||||
|
||||
& + a.action-link {
|
||||
margin-left: 0; // But really only the first one
|
||||
a.action-link {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
> .left-side {
|
||||
margin-left: auto; // Left alligned Icons
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -260,13 +260,18 @@ ul.bp {
|
|||
}
|
||||
|
||||
li.process > div > .state-ball,
|
||||
li:not(.process) > .state-ball {
|
||||
li:not(.process) > .state-ball,
|
||||
div.left-side > .state-ball {
|
||||
border: .15em solid white;
|
||||
|
||||
&.size-s {
|
||||
width: 7em/6em;
|
||||
height: 7em/6em;
|
||||
line-height: 7em/6em;
|
||||
|
||||
&.last {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -416,6 +421,14 @@ td > a > .badges {
|
|||
font-size: 0.5em;
|
||||
}
|
||||
|
||||
.state-ball {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
margin: 5px 5px 5px 5px;
|
||||
border: 1px solid white;
|
||||
}
|
||||
|
||||
> a {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
|
|
@ -949,6 +962,39 @@ form dt label {
|
|||
}
|
||||
}
|
||||
|
||||
fieldset.collapsed {
|
||||
dd, dt, ul, div {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
fieldset {
|
||||
margin-top: 15px;
|
||||
padding: 0 0 1.5em 0;
|
||||
border: none;
|
||||
|
||||
legend {
|
||||
margin: 0em 0 0.5em 0;
|
||||
font-size: 1em;
|
||||
box-shadow: 0 3px 4px -4px rgba(0,0,0,0.2);
|
||||
font-weight: bold;
|
||||
width: 100%;
|
||||
padding-left: 0.5em;
|
||||
line-height: 2em;
|
||||
user-select: none;
|
||||
|
||||
&:hover {
|
||||
border-color: @text-color;
|
||||
}
|
||||
}
|
||||
dd, dt, ul, div {
|
||||
display: none;
|
||||
}
|
||||
|
||||
margin-bottom: 0.2em;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
form fieldset {
|
||||
min-width: 36em;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue