mirror of
https://github.com/Icinga/icingaweb2-module-businessprocess.git
synced 2025-12-30 10:29:34 -05:00
Merge branch 'feature/drag-and-drop'
This commit is contained in:
commit
4b76251f86
51 changed files with 4471 additions and 1137 deletions
|
|
@ -34,7 +34,7 @@ class ProcessCommand extends Command
|
|||
|
||||
public function init()
|
||||
{
|
||||
$this->storage = new LegacyStorage($this->Config()->getSection('global'));
|
||||
$this->storage = LegacyStorage::getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -25,23 +25,70 @@ class NodeController extends Controller
|
|||
foreach ($this->storage()->listProcessNames() as $configName) {
|
||||
$config = $this->storage()->loadProcess($configName);
|
||||
|
||||
if (! $config->hasNode($name)) {
|
||||
$parents = [];
|
||||
if ($config->hasNode($name)) {
|
||||
foreach ($config->getNode($name)->getPaths() as $path) {
|
||||
array_pop($path); // Remove the monitored node
|
||||
$immediateParentName = array_pop($path); // The directly affected process
|
||||
$parents[] = [$config->getNode($immediateParentName), $path];
|
||||
}
|
||||
}
|
||||
|
||||
$askedConfigs = [];
|
||||
foreach ($config->getImportedNodes() as $importedNode) {
|
||||
$importedConfig = $importedNode->getBpConfig();
|
||||
|
||||
if (isset($askedConfigs[$importedConfig->getName()])) {
|
||||
continue;
|
||||
} else {
|
||||
$askedConfigs[$importedConfig->getName()] = true;
|
||||
}
|
||||
|
||||
if ($importedConfig->hasNode($name)) {
|
||||
$node = $importedConfig->getNode($name);
|
||||
$nativePaths = $node->getPaths($config);
|
||||
|
||||
do {
|
||||
$path = array_pop($nativePaths);
|
||||
$importedNodePos = array_search($importedNode->getIdentifier(), $path, true);
|
||||
if ($importedNodePos !== false) {
|
||||
array_pop($path); // Remove the monitored node
|
||||
$immediateParentName = array_pop($path); // The directly affected process
|
||||
$importedPath = array_slice($path, $importedNodePos + 1);
|
||||
|
||||
// We may get multiple native paths. Though, only the right hand of the path
|
||||
// is what we're interested in. The left part is not what is getting imported.
|
||||
$antiDuplicator = join('|', $importedPath) . '|' . $immediateParentName;
|
||||
if (isset($parents[$antiDuplicator])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($importedNode->getPaths($config) as $targetPath) {
|
||||
if ($targetPath[count($targetPath) - 1] === $immediateParentName) {
|
||||
array_pop($targetPath);
|
||||
$parent = $importedNode;
|
||||
} else {
|
||||
$parent = $importedConfig->getNode($immediateParentName);
|
||||
}
|
||||
|
||||
$parents[$antiDuplicator] = [$parent, array_merge($targetPath, $importedPath)];
|
||||
}
|
||||
}
|
||||
} while (! empty($nativePaths));
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($parents)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MonitoringState::apply($config);
|
||||
$config->applySimulation($simulation);
|
||||
|
||||
foreach ($config->getNode($name)->getPaths() as $path) {
|
||||
array_pop($path);
|
||||
$node = array_pop($path);
|
||||
$renderer = new TileRenderer($config, $config->getNode($node));
|
||||
$renderer->setUrl(
|
||||
Url::fromPath(
|
||||
'businessprocess/process/show',
|
||||
array('config' => $configName)
|
||||
)
|
||||
)->setPath($path);
|
||||
foreach ($parents as $parentAndPath) {
|
||||
$renderer = (new TileRenderer($config, array_shift($parentAndPath)))
|
||||
->setUrl(Url::fromPath('businessprocess/process/show', ['config' => $configName]))
|
||||
->setPath(array_shift($parentAndPath));
|
||||
|
||||
$bc = Breadcrumb::create($renderer);
|
||||
$bc->getAttributes()->set('data-base-target', '_next');
|
||||
|
|
|
|||
|
|
@ -88,12 +88,14 @@ class ProcessController extends Controller
|
|||
|
||||
$renderer = $this->prepareRenderer($bp, $node);
|
||||
|
||||
if ($this->params->get('unlocked')) {
|
||||
$renderer->unlock();
|
||||
}
|
||||
if (! $this->showFullscreen && ($node === null || ! $renderer->rendersImportedNode())) {
|
||||
if ($this->params->get('unlocked')) {
|
||||
$renderer->unlock();
|
||||
}
|
||||
|
||||
if ($bp->isEmpty() && $renderer->isLocked()) {
|
||||
$this->redirectNow($this->url()->with('unlocked', true));
|
||||
if ($bp->isEmpty() && $renderer->isLocked()) {
|
||||
$this->redirectNow($this->url()->with('unlocked', true));
|
||||
}
|
||||
}
|
||||
|
||||
$this->handleFormatRequest($bp, $node);
|
||||
|
|
@ -137,11 +139,10 @@ class ProcessController extends Controller
|
|||
|
||||
if (! ($this->showFullscreen || $this->view->compact)) {
|
||||
$controls->add($this->getProcessTabs($bp, $renderer));
|
||||
$controls->getAttributes()->add('class', 'separated');
|
||||
}
|
||||
if (! $this->view->compact) {
|
||||
$controls->add(Html::tag('h1')->setContent($this->view->title));
|
||||
}
|
||||
$controls->add(Breadcrumb::create($renderer));
|
||||
|
||||
$controls->add(Breadcrumb::create(clone $renderer));
|
||||
if (! $this->showFullscreen && ! $this->view->compact) {
|
||||
$controls->add(
|
||||
new RenderedProcessActionBar($bp, $renderer, $this->Auth(), $this->url())
|
||||
|
|
@ -250,6 +251,13 @@ class ProcessController extends Controller
|
|||
->setNode($bp->getNode($this->params->get('simulationnode')))
|
||||
->setSimulation(Simulation::fromSession($this->session()))
|
||||
->handleRequest();
|
||||
} elseif ($action === 'move') {
|
||||
$form = $this->loadForm('MoveNode')
|
||||
->setProcess($bp)
|
||||
->setParentNode($node)
|
||||
->setSession($this->session())
|
||||
->setNode($bp->getNode($this->params->get('movenode')))
|
||||
->handleRequest();
|
||||
}
|
||||
|
||||
if ($form) {
|
||||
|
|
|
|||
|
|
@ -122,15 +122,20 @@ class AddNodeForm extends QuickForm
|
|||
)
|
||||
));
|
||||
|
||||
$display = 1;
|
||||
if ($this->bp->getMetadata()->isManuallyOrdered() && !$this->bp->isEmpty()) {
|
||||
$rootNodes = $this->bp->getRootNodes();
|
||||
$display = end($rootNodes)->getDisplay() + 1;
|
||||
}
|
||||
$this->addElement('select', 'display', array(
|
||||
'label' => $this->translate('Visualization'),
|
||||
'required' => true,
|
||||
'description' => $this->translate(
|
||||
'Where to show this process'
|
||||
),
|
||||
'value' => $this->hasParentNode() ? '0' : '1',
|
||||
'value' => $this->hasParentNode() ? '0' : "$display",
|
||||
'multiOptions' => array(
|
||||
'1' => $this->translate('Toplevel Process'),
|
||||
"$display" => $this->translate('Toplevel Process'),
|
||||
'0' => $this->translate('Subprocess only'),
|
||||
)
|
||||
));
|
||||
|
|
@ -184,7 +189,7 @@ class AddNodeForm extends QuickForm
|
|||
|
||||
protected function selectHost()
|
||||
{
|
||||
$this->addElement('multiselect','children', [
|
||||
$this->addElement('multiselect', 'children', [
|
||||
'label' => $this->translate('Hosts'),
|
||||
'required' => true,
|
||||
'size' => 8,
|
||||
|
|
@ -220,7 +225,7 @@ class AddNodeForm extends QuickForm
|
|||
|
||||
protected function addServicesElement($host)
|
||||
{
|
||||
$this->addElement('multiselect','children', [
|
||||
$this->addElement('multiselect', 'children', [
|
||||
'label' => $this->translate('Services'),
|
||||
'required' => true,
|
||||
'size' => 8,
|
||||
|
|
@ -255,7 +260,7 @@ class AddNodeForm extends QuickForm
|
|||
}
|
||||
|
||||
if (($file = $this->getSentValue('file')) || !$this->hasParentNode()) {
|
||||
$this->addElement('multiselect','children', [
|
||||
$this->addElement('multiselect', 'children', [
|
||||
'label' => $this->translate('Process nodes'),
|
||||
'required' => true,
|
||||
'size' => 8,
|
||||
|
|
@ -420,11 +425,13 @@ class AddNodeForm extends QuickForm
|
|||
$name = '@' . $file . ':' . $name;
|
||||
}
|
||||
|
||||
$list[$name] = (string) $node; // display name?
|
||||
$list[$name] = $node->getName(); // display name?
|
||||
}
|
||||
}
|
||||
|
||||
natcasesort($list);
|
||||
if (! $this->bp->getMetadata()->isManuallyOrdered()) {
|
||||
natcasesort($list);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -126,15 +126,16 @@ class EditNodeForm extends QuickForm
|
|||
)
|
||||
));
|
||||
|
||||
$display = $this->getNode()->getDisplay() ?: 1;
|
||||
$this->addElement('select', 'display', array(
|
||||
'label' => $this->translate('Visualization'),
|
||||
'required' => true,
|
||||
'description' => $this->translate(
|
||||
'Where to show this process'
|
||||
),
|
||||
'value' => $this->hasParentNode() ? '0' : '1',
|
||||
'value' => $display,
|
||||
'multiOptions' => array(
|
||||
'1' => $this->translate('Toplevel Process'),
|
||||
"$display" => $this->translate('Toplevel Process'),
|
||||
'0' => $this->translate('Subprocess only'),
|
||||
)
|
||||
));
|
||||
|
|
@ -358,11 +359,13 @@ class EditNodeForm extends QuickForm
|
|||
|
||||
foreach ($this->bp->getNodes() as $node) {
|
||||
if ($node instanceof BpNode && ! isset($parents[$node->getName()])) {
|
||||
$list[(string) $node] = (string) $node; // display name?
|
||||
$list[$node->getName()] = $node->getName(); // display name?
|
||||
}
|
||||
}
|
||||
|
||||
natcasesort($list);
|
||||
if (! $this->bp->getMetadata()->isManuallyOrdered()) {
|
||||
natcasesort($list);
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
|
|
|
|||
185
application/forms/MoveNodeForm.php
Normal file
185
application/forms/MoveNodeForm.php
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Module\Businessprocess\Forms;
|
||||
|
||||
use Icinga\Application\Icinga;
|
||||
use Icinga\Exception\Http\HttpException;
|
||||
use Icinga\Module\Businessprocess\BpConfig;
|
||||
use Icinga\Module\Businessprocess\BpNode;
|
||||
use Icinga\Module\Businessprocess\Exception\ModificationError;
|
||||
use Icinga\Module\Businessprocess\Modification\ProcessChanges;
|
||||
use Icinga\Module\Businessprocess\Node;
|
||||
use Icinga\Module\Businessprocess\Web\Form\CsrfToken;
|
||||
use Icinga\Module\Businessprocess\Web\Form\QuickForm;
|
||||
use Icinga\Web\Session;
|
||||
use Icinga\Web\Session\SessionNamespace;
|
||||
|
||||
class MoveNodeForm extends QuickForm
|
||||
{
|
||||
/** @var BpConfig */
|
||||
protected $bp;
|
||||
|
||||
/** @var Node */
|
||||
protected $node;
|
||||
|
||||
/** @var BpNode */
|
||||
protected $parentNode;
|
||||
|
||||
/** @var SessionNamespace */
|
||||
protected $session;
|
||||
|
||||
public function __construct($options = null)
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
// Zend's plugin loader reverses the order of added prefix paths thus trying our paths first before trying
|
||||
// Zend paths
|
||||
$this->addPrefixPaths(array(
|
||||
array(
|
||||
'prefix' => 'Icinga\\Web\\Form\\Element\\',
|
||||
'path' => Icinga::app()->getLibraryDir('Icinga/Web/Form/Element'),
|
||||
'type' => static::ELEMENT
|
||||
),
|
||||
array(
|
||||
'prefix' => 'Icinga\\Web\\Form\\Decorator\\',
|
||||
'path' => Icinga::app()->getLibraryDir('Icinga/Web/Form/Decorator'),
|
||||
'type' => static::DECORATOR
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
public function setup()
|
||||
{
|
||||
$this->addElement(
|
||||
'text',
|
||||
'parent',
|
||||
[
|
||||
'allowEmpty' => true,
|
||||
'filters' => ['Null'],
|
||||
'validators' => [
|
||||
['Callback', true, [
|
||||
'callback' => function ($name) {
|
||||
return empty($name) || $this->bp->hasBpNode($name);
|
||||
},
|
||||
'messages' => [
|
||||
'callbackValue' => $this->translate('No process found with name %value%')
|
||||
]
|
||||
]]
|
||||
]
|
||||
]
|
||||
);
|
||||
$this->addElement(
|
||||
'number',
|
||||
'from',
|
||||
[
|
||||
'required' => true,
|
||||
'min' => 0
|
||||
]
|
||||
);
|
||||
$this->addElement(
|
||||
'number',
|
||||
'to',
|
||||
[
|
||||
'required' => true,
|
||||
'min' => 0
|
||||
]
|
||||
);
|
||||
$this->addElement(
|
||||
'hidden',
|
||||
'csrfToken',
|
||||
[
|
||||
'required' => true
|
||||
]
|
||||
);
|
||||
|
||||
$this->setSubmitLabel('movenode');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BpConfig $process
|
||||
* @return $this
|
||||
*/
|
||||
public function setProcess(BpConfig $process)
|
||||
{
|
||||
$this->bp = $process;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node $node
|
||||
* @return $this
|
||||
*/
|
||||
public function setNode(Node $node)
|
||||
{
|
||||
$this->node = $node;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BpNode|null $node
|
||||
* @return $this
|
||||
*/
|
||||
public function setParentNode(BpNode $node = null)
|
||||
{
|
||||
$this->parentNode = $node;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SessionNamespace $session
|
||||
* @return $this
|
||||
*/
|
||||
public function setSession(SessionNamespace $session)
|
||||
{
|
||||
$this->session = $session;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function onSuccess()
|
||||
{
|
||||
if (! CsrfToken::isValid($this->getValue('csrfToken'))) {
|
||||
throw new HttpException(403, 'nope');
|
||||
}
|
||||
|
||||
$changes = ProcessChanges::construct($this->bp, $this->session);
|
||||
if (! $this->bp->getMetadata()->isManuallyOrdered()) {
|
||||
$changes->applyManualOrder();
|
||||
}
|
||||
|
||||
try {
|
||||
$changes->moveNode(
|
||||
$this->node,
|
||||
$this->getValue('from'),
|
||||
$this->getValue('to'),
|
||||
$this->getValue('parent'),
|
||||
$this->parentNode !== null ? $this->parentNode->getName() : null
|
||||
);
|
||||
} catch (ModificationError $e) {
|
||||
$this->notifyError($e->getMessage());
|
||||
Icinga::app()->getResponse()
|
||||
// Web 2's JS forces a content update for non-200s. Our own JS
|
||||
// can't prevent this, hence we're not making this a 400 :(
|
||||
//->setHttpResponseCode(400)
|
||||
->setHeader('X-Icinga-Container', 'ignore')
|
||||
->sendResponse();
|
||||
exit;
|
||||
}
|
||||
|
||||
// Trigger session destruction to make sure it get's stored.
|
||||
unset($changes);
|
||||
|
||||
$this->notifySuccess($this->getSuccessMessage($this->translate('Node order updated')));
|
||||
|
||||
$response = $this->getRequest()->getResponse()
|
||||
->setHeader('X-Icinga-Container', 'ignore');
|
||||
|
||||
Session::getSession()->write();
|
||||
$response->sendResponse();
|
||||
exit;
|
||||
}
|
||||
|
||||
public function hasBeenSent()
|
||||
{
|
||||
return true; // This form has no id
|
||||
}
|
||||
}
|
||||
|
|
@ -73,14 +73,20 @@ class ProcessForm extends QuickForm
|
|||
)
|
||||
));
|
||||
|
||||
if ($this->node !== null) {
|
||||
$display = $this->node->getDisplay() ?: 1;
|
||||
} else {
|
||||
$display = 1;
|
||||
}
|
||||
$this->addElement('select', 'display', array(
|
||||
'label' => $this->translate('Visualization'),
|
||||
'required' => true,
|
||||
'description' => $this->translate(
|
||||
'Where to show this process'
|
||||
),
|
||||
'value' => $display,
|
||||
'multiOptions' => array(
|
||||
'1' => $this->translate('Toplevel Process'),
|
||||
"$display" => $this->translate('Toplevel Process'),
|
||||
'0' => $this->translate('Subprocess only'),
|
||||
)
|
||||
));
|
||||
|
|
@ -97,7 +103,6 @@ class ProcessForm extends QuickForm
|
|||
$this->getElement('alias')->setValue($node->getAlias());
|
||||
}
|
||||
$this->getElement('operator')->setValue($node->getOperator());
|
||||
$this->getElement('display')->setValue($node->getDisplay());
|
||||
if ($node->hasInfoUrl()) {
|
||||
$this->getElement('url')->setValue($node->getInfoUrl());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,7 @@ $section = $this->menuSection(N_('Business Processes'), array(
|
|||
));
|
||||
|
||||
try {
|
||||
$storage = new LegacyStorage(
|
||||
$this->getConfig()->getSection('global')
|
||||
);
|
||||
$storage = LegacyStorage::getInstance();
|
||||
|
||||
$prio = 0;
|
||||
foreach ($storage->listProcessNames() as $name) {
|
||||
|
|
@ -57,3 +55,9 @@ $this->provideRestriction(
|
|||
'businessprocess/prefix',
|
||||
$this->translate('Restrict access to configurations with the given prefix')
|
||||
);
|
||||
|
||||
$this->provideJsFile('vendor/Sortable.js');
|
||||
$this->provideJsFile('behavior/sortable.js');
|
||||
$this->provideJsFile('vendor/jquery.fn.sortable.js');
|
||||
|
||||
$this->provideCssFile('state-ball.less');
|
||||
|
|
|
|||
|
|
@ -2,12 +2,14 @@
|
|||
|
||||
namespace Icinga\Module\Businessprocess;
|
||||
|
||||
use Exception;
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Exception\IcingaException;
|
||||
use Icinga\Exception\NotFoundError;
|
||||
use Icinga\Module\Businessprocess\Exception\NestingError;
|
||||
use Icinga\Module\Businessprocess\Modification\ProcessChanges;
|
||||
use Icinga\Module\Businessprocess\Storage\LegacyStorage;
|
||||
use Icinga\Module\Monitoring\Backend\MonitoringBackend;
|
||||
use Exception;
|
||||
|
||||
class BpConfig
|
||||
{
|
||||
|
|
@ -29,6 +31,11 @@ class BpConfig
|
|||
*/
|
||||
protected $backend;
|
||||
|
||||
/**
|
||||
* @var LegacyStorage
|
||||
*/
|
||||
protected $storage;
|
||||
|
||||
/** @var Metadata */
|
||||
protected $metadata;
|
||||
|
||||
|
|
@ -81,6 +88,20 @@ class BpConfig
|
|||
*/
|
||||
protected $root_nodes = array();
|
||||
|
||||
/**
|
||||
* Imported nodes
|
||||
*
|
||||
* @var ImportedNode[]
|
||||
*/
|
||||
protected $importedNodes = [];
|
||||
|
||||
/**
|
||||
* Imported configs
|
||||
*
|
||||
* @var BpConfig[]
|
||||
*/
|
||||
protected $importedConfigs = [];
|
||||
|
||||
/**
|
||||
* All host names { 'hostA' => true, ... }
|
||||
*
|
||||
|
|
@ -388,14 +409,32 @@ class BpConfig
|
|||
*/
|
||||
public function getRootNodes()
|
||||
{
|
||||
ksort($this->root_nodes, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
if ($this->getMetadata()->isManuallyOrdered()) {
|
||||
uasort($this->root_nodes, function (BpNode $a, BpNode $b) {
|
||||
$a = $a->getDisplay();
|
||||
$b = $b->getDisplay();
|
||||
return $a > $b ? 1 : ($a < $b ? -1 : 0);
|
||||
});
|
||||
} else {
|
||||
ksort($this->root_nodes, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
}
|
||||
|
||||
return $this->root_nodes;
|
||||
}
|
||||
|
||||
public function listRootNodes()
|
||||
{
|
||||
$names = array_keys($this->root_nodes);
|
||||
natcasesort($names);
|
||||
if ($this->getMetadata()->isManuallyOrdered()) {
|
||||
uasort($names, function ($a, $b) {
|
||||
$a = $this->root_nodes[$a]->getDisplay();
|
||||
$b = $this->root_nodes[$b]->getDisplay();
|
||||
return $a > $b ? 1 : ($a < $b ? -1 : 0);
|
||||
});
|
||||
} else {
|
||||
natcasesort($names);
|
||||
}
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
|
|
@ -406,7 +445,14 @@ class BpConfig
|
|||
|
||||
public function hasNode($name)
|
||||
{
|
||||
return array_key_exists($name, $this->nodes);
|
||||
if (array_key_exists($name, $this->nodes)) {
|
||||
return true;
|
||||
} elseif ($name[0] === '@') {
|
||||
list($configName, $nodeName) = preg_split('~:\s*~', substr($name, 1), 2);
|
||||
return $this->getImportedConfig($configName)->hasNode($nodeName);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function hasRootNode($name)
|
||||
|
|
@ -417,12 +463,12 @@ class BpConfig
|
|||
public function createService($host, $service)
|
||||
{
|
||||
$node = new ServiceNode(
|
||||
$this,
|
||||
(object) array(
|
||||
'hostname' => $host,
|
||||
'service' => $service
|
||||
)
|
||||
);
|
||||
$node->setBpConfig($this);
|
||||
$this->nodes[$host . ';' . $service] = $node;
|
||||
$this->hosts[$host] = true;
|
||||
return $node;
|
||||
|
|
@ -430,7 +476,8 @@ class BpConfig
|
|||
|
||||
public function createHost($host)
|
||||
{
|
||||
$node = new HostNode($this, (object) array('hostname' => $host));
|
||||
$node = new HostNode((object) array('hostname' => $host));
|
||||
$node->setBpConfig($this);
|
||||
$this->nodes[$host . ';Hoststatus'] = $node;
|
||||
$this->hosts[$host] = true;
|
||||
return $node;
|
||||
|
|
@ -454,9 +501,21 @@ class BpConfig
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function listInvolvedHostNames()
|
||||
public function listInvolvedHostNames(&$usedConfigs = null)
|
||||
{
|
||||
return array_keys($this->hosts);
|
||||
$hosts = $this->hosts;
|
||||
if (! empty($this->importedNodes)) {
|
||||
$usedConfigs[$this->getName()] = true;
|
||||
foreach ($this->importedNodes as $node) {
|
||||
if (isset($usedConfigs[$node->getConfigName()])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$hosts += array_flip($node->getBpConfig()->listInvolvedHostNames($usedConfigs));
|
||||
}
|
||||
}
|
||||
|
||||
return array_keys($hosts);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -469,11 +528,12 @@ class BpConfig
|
|||
*/
|
||||
public function createBp($name, $operator = '&')
|
||||
{
|
||||
$node = new BpNode($this, (object) array(
|
||||
$node = new BpNode((object) array(
|
||||
'name' => $name,
|
||||
'operator' => $operator,
|
||||
'child_names' => array(),
|
||||
));
|
||||
$node->setBpConfig($this);
|
||||
|
||||
$this->addNode($name, $node);
|
||||
return $node;
|
||||
|
|
@ -502,14 +562,66 @@ class BpConfig
|
|||
}
|
||||
|
||||
$node = new ImportedNode($this, $params);
|
||||
$this->importedNodes[$node->getName()] = $node;
|
||||
$this->nodes[$node->getName()] = $node;
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function getImportedNodes()
|
||||
{
|
||||
return $this->importedNodes;
|
||||
}
|
||||
|
||||
public function getImportedConfig($name)
|
||||
{
|
||||
if (! isset($this->importedConfigs[$name])) {
|
||||
$import = $this->storage()->loadProcess($name);
|
||||
|
||||
if ($this->usesSoftStates()) {
|
||||
$import->useSoftStates();
|
||||
} else {
|
||||
$import->useHardStates();
|
||||
}
|
||||
|
||||
$this->importedConfigs[$name] = $import;
|
||||
}
|
||||
|
||||
return $this->importedConfigs[$name];
|
||||
}
|
||||
|
||||
public function listInvolvedConfigs(&$configs = null)
|
||||
{
|
||||
if ($configs === null) {
|
||||
$configs[$this->getName()] = $this;
|
||||
}
|
||||
|
||||
foreach ($this->importedNodes as $node) {
|
||||
if (! isset($configs[$node->getConfigName()])) {
|
||||
$config = $node->getBpConfig();
|
||||
$configs[$node->getConfigName()] = $config;
|
||||
$config->listInvolvedConfigs($configs);
|
||||
}
|
||||
}
|
||||
|
||||
return $configs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @return Node
|
||||
* @throws Exception
|
||||
* @return LegacyStorage
|
||||
*/
|
||||
protected function storage()
|
||||
{
|
||||
if ($this->storage === null) {
|
||||
$this->storage = LegacyStorage::getInstance();
|
||||
}
|
||||
|
||||
return $this->storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return Node
|
||||
* @throws Exception
|
||||
*/
|
||||
public function getNode($name)
|
||||
{
|
||||
|
|
@ -521,6 +633,11 @@ class BpConfig
|
|||
return $this->nodes[$name];
|
||||
}
|
||||
|
||||
if ($name[0] === '@') {
|
||||
list($configName, $nodeName) = preg_split('~:\s*~', substr($name, 1), 2);
|
||||
return $this->getImportedConfig($configName)->getNode($nodeName);
|
||||
}
|
||||
|
||||
// Fallback: if it is a service, create an empty one:
|
||||
$this->warn(sprintf('The node "%s" doesn\'t exist', $name));
|
||||
$pos = strpos($name, ';');
|
||||
|
|
@ -550,11 +667,12 @@ class BpConfig
|
|||
$this->calculateAllStates();
|
||||
|
||||
$names = array_keys($this->getUnboundNodes());
|
||||
$bp = new BpNode($this, (object) array(
|
||||
$bp = new BpNode((object) array(
|
||||
'name' => '__unbound__',
|
||||
'operator' => '&',
|
||||
'child_names' => $names
|
||||
));
|
||||
$bp->setBpConfig($this);
|
||||
$bp->setAlias($this->translate('Unbound nodes'));
|
||||
return $bp;
|
||||
}
|
||||
|
|
@ -685,7 +803,16 @@ class BpConfig
|
|||
$nodes[$name] = $name === $alias ? $name : sprintf('%s (%s)', $alias, $node);
|
||||
}
|
||||
|
||||
natcasesort($nodes);
|
||||
if ($this->getMetadata()->isManuallyOrdered()) {
|
||||
uasort($nodes, function ($a, $b) {
|
||||
$a = $this->nodes[$a]->getDisplay();
|
||||
$b = $this->nodes[$b]->getDisplay();
|
||||
return $a > $b ? 1 : ($a < $b ? -1 : 0);
|
||||
});
|
||||
} else {
|
||||
natcasesort($nodes);
|
||||
}
|
||||
|
||||
return $nodes;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,12 +48,11 @@ class BpNode extends Node
|
|||
|
||||
protected $className = 'process';
|
||||
|
||||
public function __construct(BpConfig $bp, $object)
|
||||
public function __construct($object)
|
||||
{
|
||||
$this->bp = $bp;
|
||||
$this->name = $object->name;
|
||||
$this->setOperator($object->operator);
|
||||
$this->setChildNames($object->child_names);
|
||||
$this->operator = $object->operator;
|
||||
$this->childNames = $object->child_names;
|
||||
}
|
||||
|
||||
public function getStateSummary()
|
||||
|
|
@ -140,12 +139,12 @@ class BpNode extends Node
|
|||
|
||||
public function hasChild($name)
|
||||
{
|
||||
return in_array($name, $this->childNames);
|
||||
return in_array($name, $this->getChildNames());
|
||||
}
|
||||
|
||||
public function removeChild($name)
|
||||
{
|
||||
if (($key = array_search($name, $this->childNames)) !== false) {
|
||||
if (($key = array_search($name, $this->getChildNames())) !== false) {
|
||||
unset($this->childNames[$key]);
|
||||
|
||||
if (! empty($this->children)) {
|
||||
|
|
@ -161,7 +160,7 @@ class BpNode extends Node
|
|||
$tree = array();
|
||||
|
||||
foreach ($this->getProblematicChildren() as $child) {
|
||||
$name = (string) $child;
|
||||
$name = $child->getName();
|
||||
$tree[$name] = array(
|
||||
'node' => $child,
|
||||
'children' => array()
|
||||
|
|
@ -178,7 +177,7 @@ class BpNode extends Node
|
|||
{
|
||||
if ($this->missing === null) {
|
||||
$exists = false;
|
||||
$bp = $this->bp;
|
||||
$bp = $this->getBpConfig();
|
||||
$bp->beginLoopDetection($this->name);
|
||||
foreach ($this->getChildren() as $child) {
|
||||
if (! $child->isMissing()) {
|
||||
|
|
@ -198,11 +197,11 @@ class BpNode extends Node
|
|||
|
||||
foreach ($this->getChildren() as $child) {
|
||||
if ($child->isMissing()) {
|
||||
$missing[(string) $child] = $child;
|
||||
$missing[$child->getName()] = $child;
|
||||
}
|
||||
|
||||
foreach ($child->getMissingChildren() as $m) {
|
||||
$missing[(string) $m] = $m;
|
||||
$missing[$m->getName()] = $m;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -276,7 +275,7 @@ class BpNode extends Node
|
|||
|
||||
public function hasAlias()
|
||||
{
|
||||
return $this->alias !== null;
|
||||
return $this->getAlias() !== null;
|
||||
}
|
||||
|
||||
public function getAlias()
|
||||
|
|
@ -299,8 +298,8 @@ class BpNode extends Node
|
|||
try {
|
||||
$this->reCalculateState();
|
||||
} catch (NestingError $e) {
|
||||
$this->bp->addError(
|
||||
$this->bp->translate('Nesting error detected: %s'),
|
||||
$this->getBpConfig()->addError(
|
||||
$this->getBpConfig()->translate('Nesting error detected: %s'),
|
||||
$e->getMessage()
|
||||
);
|
||||
|
||||
|
|
@ -314,7 +313,7 @@ class BpNode extends Node
|
|||
|
||||
public function getHtmlId()
|
||||
{
|
||||
return 'businessprocess-' . preg_replace('/[\r\n\t\s]/', '_', (string) $this);
|
||||
return 'businessprocess-' . preg_replace('/[\r\n\t\s]/', '_', $this->getName());
|
||||
}
|
||||
|
||||
protected function invertSortingState($state)
|
||||
|
|
@ -327,7 +326,7 @@ class BpNode extends Node
|
|||
*/
|
||||
public function reCalculateState()
|
||||
{
|
||||
$bp = $this->bp;
|
||||
$bp = $this->getBpConfig();
|
||||
|
||||
$sort_states = array();
|
||||
$lastStateChange = 0;
|
||||
|
|
@ -357,7 +356,7 @@ class BpNode extends Node
|
|||
|
||||
$this->setLastStateChange($lastStateChange);
|
||||
|
||||
switch ($this->operator) {
|
||||
switch ($this->getOperator()) {
|
||||
case self::OP_AND:
|
||||
$sort_state = max($sort_states);
|
||||
break;
|
||||
|
|
@ -401,7 +400,7 @@ class BpNode extends Node
|
|||
|
||||
public function checkForLoops()
|
||||
{
|
||||
$bp = $this->bp;
|
||||
$bp = $this->getBpConfig();
|
||||
foreach ($this->getChildren() as $child) {
|
||||
$bp->beginLoopDetection($this->name);
|
||||
if ($child instanceof BpNode) {
|
||||
|
|
@ -426,7 +425,11 @@ class BpNode extends Node
|
|||
|
||||
public function setChildNames($names)
|
||||
{
|
||||
natcasesort($names);
|
||||
if (! $this->getBpConfig()->getMetadata()->isManuallyOrdered()) {
|
||||
natcasesort($names);
|
||||
$names = array_values($names);
|
||||
}
|
||||
|
||||
$this->childNames = $names;
|
||||
$this->children = null;
|
||||
return $this;
|
||||
|
|
@ -434,7 +437,8 @@ class BpNode extends Node
|
|||
|
||||
public function hasChildren($filter = null)
|
||||
{
|
||||
return !empty($this->childNames);
|
||||
$childNames = $this->getChildNames();
|
||||
return !empty($childNames);
|
||||
}
|
||||
|
||||
public function getChildNames()
|
||||
|
|
@ -446,9 +450,13 @@ class BpNode extends Node
|
|||
{
|
||||
if ($this->children === null) {
|
||||
$this->children = array();
|
||||
natcasesort($this->childNames);
|
||||
foreach ($this->childNames as $name) {
|
||||
$this->children[$name] = $this->bp->getNode($name);
|
||||
if (! $this->getBpConfig()->getMetadata()->isManuallyOrdered()) {
|
||||
$childNames = $this->getChildNames();
|
||||
natcasesort($childNames);
|
||||
$this->childNames = array_values($childNames);
|
||||
}
|
||||
foreach ($this->getChildNames() as $name) {
|
||||
$this->children[$name] = $this->getBpConfig()->getNode($name);
|
||||
$this->children[$name]->addParent($this);
|
||||
}
|
||||
}
|
||||
|
|
@ -490,22 +498,22 @@ class BpNode extends Node
|
|||
|
||||
protected function assertNumericOperator()
|
||||
{
|
||||
if (! is_numeric($this->operator)) {
|
||||
if (! is_numeric($this->getOperator())) {
|
||||
throw new ConfigurationError('Got invalid operator: %s', $this->operator);
|
||||
}
|
||||
}
|
||||
|
||||
public function operatorHtml()
|
||||
{
|
||||
switch ($this->operator) {
|
||||
switch ($this->getOperator()) {
|
||||
case self::OP_AND:
|
||||
return 'and';
|
||||
return 'AND';
|
||||
break;
|
||||
case self::OP_OR:
|
||||
return 'or';
|
||||
return 'OR';
|
||||
break;
|
||||
case self::OP_NOT:
|
||||
return 'not';
|
||||
return 'NOT';
|
||||
break;
|
||||
default:
|
||||
// MIN
|
||||
|
|
@ -513,4 +521,10 @@ class BpNode extends Node
|
|||
return 'min:' . $this->operator;
|
||||
}
|
||||
}
|
||||
|
||||
public function getIcon()
|
||||
{
|
||||
$this->icon = $this->hasParents() ? 'cubes' : 'sitemap';
|
||||
return parent::getIcon();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Icinga\Module\Businessprocess\Director;
|
||||
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Module\Director\Hook\ShipConfigFilesHook;
|
||||
use Icinga\Module\Businessprocess\Storage\LegacyStorage;
|
||||
|
||||
|
|
@ -12,9 +11,7 @@ class ShipConfigFiles extends ShipConfigFilesHook
|
|||
{
|
||||
$files = array();
|
||||
|
||||
$storage = new LegacyStorage(
|
||||
Config::module('businessprocess')->getSection('global')
|
||||
);
|
||||
$storage = LegacyStorage::getInstance();
|
||||
|
||||
foreach ($storage->listProcesses() as $name => $title) {
|
||||
$files['processes/' . $name . '.bp'] = $storage->getSource($name);
|
||||
|
|
|
|||
9
library/Businessprocess/Exception/ModificationError.php
Normal file
9
library/Businessprocess/Exception/ModificationError.php
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Module\Businessprocess\Exception;
|
||||
|
||||
use Icinga\Exception\IcingaException;
|
||||
|
||||
class ModificationError extends IcingaException
|
||||
{
|
||||
}
|
||||
|
|
@ -32,11 +32,12 @@ class HostNode extends MonitoredNode
|
|||
|
||||
protected $className = 'host';
|
||||
|
||||
public function __construct(BpConfig $bp, $object)
|
||||
protected $icon = 'host';
|
||||
|
||||
public function __construct($object)
|
||||
{
|
||||
$this->name = $object->hostname . ';Hoststatus';
|
||||
$this->hostname = $object->hostname;
|
||||
$this->bp = $bp;
|
||||
if (isset($object->state)) {
|
||||
$this->setState($object->state);
|
||||
} else {
|
||||
|
|
@ -60,8 +61,8 @@ class HostNode extends MonitoredNode
|
|||
'host' => $this->getHostname(),
|
||||
);
|
||||
|
||||
if ($this->bp->hasBackendName()) {
|
||||
$params['backend'] = $this->bp->getBackendName();
|
||||
if ($this->getBpConfig()->hasBackendName()) {
|
||||
$params['backend'] = $this->getBpConfig()->getBackendName();
|
||||
}
|
||||
|
||||
return Url::fromPath('businessprocess/host/show', $params);
|
||||
|
|
|
|||
|
|
@ -2,15 +2,13 @@
|
|||
|
||||
namespace Icinga\Module\Businessprocess;
|
||||
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Module\Businessprocess\State\MonitoringState;
|
||||
use Icinga\Module\Businessprocess\Storage\LegacyStorage;
|
||||
use Exception;
|
||||
use Icinga\Web\Url;
|
||||
use ipl\Html\Html;
|
||||
|
||||
class ImportedNode extends Node
|
||||
class ImportedNode extends BpNode
|
||||
{
|
||||
/** @var BpConfig */
|
||||
protected $parentBp;
|
||||
|
||||
/** @var string */
|
||||
protected $configName;
|
||||
|
||||
|
|
@ -18,36 +16,25 @@ class ImportedNode extends Node
|
|||
protected $nodeName;
|
||||
|
||||
/** @var BpNode */
|
||||
private $node;
|
||||
protected $importedNode;
|
||||
|
||||
protected $className = 'subtree';
|
||||
/** @var string */
|
||||
protected $className = 'process subtree';
|
||||
|
||||
/** @var BpConfig */
|
||||
private $config;
|
||||
/** @var string */
|
||||
protected $icon = 'download';
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function __construct(BpConfig $bp, $object)
|
||||
public function __construct(BpConfig $parentBp, $object)
|
||||
{
|
||||
$this->bp = $bp;
|
||||
$this->parentBp = $parentBp;
|
||||
$this->configName = $object->configName;
|
||||
$this->name = '@' . $object->configName;
|
||||
if (property_exists($object, 'node')) {
|
||||
$this->nodeName = $object->node;
|
||||
$this->name .= ':' . $object->node;
|
||||
} else {
|
||||
$this->useAllRootNodes();
|
||||
}
|
||||
$this->nodeName = $object->node;
|
||||
|
||||
if (isset($object->state)) {
|
||||
$this->setState($object->state);
|
||||
}
|
||||
}
|
||||
|
||||
public function hasNode()
|
||||
{
|
||||
return $this->nodeName !== null;
|
||||
parent::__construct((object) [
|
||||
'name' => '@' . $this->configName . ':' . $this->nodeName,
|
||||
'operator' => null,
|
||||
'child_names' => null
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -59,75 +46,52 @@ class ImportedNode extends Node
|
|||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @return string
|
||||
*/
|
||||
public function getState()
|
||||
public function getNodeName()
|
||||
{
|
||||
if ($this->state === null) {
|
||||
try {
|
||||
MonitoringState::apply($this->importedConfig());
|
||||
} catch (Exception $e) {
|
||||
}
|
||||
|
||||
$this->state = $this->importedNode()->getState();
|
||||
$this->setMissing(false);
|
||||
}
|
||||
|
||||
return $this->state;
|
||||
return $this->nodeName;
|
||||
}
|
||||
|
||||
public function getIdentifier()
|
||||
{
|
||||
return $this->getName();
|
||||
}
|
||||
|
||||
public function getBpConfig()
|
||||
{
|
||||
if ($this->bp === null) {
|
||||
$this->bp = $this->parentBp->getImportedConfig($this->configName);
|
||||
}
|
||||
|
||||
return $this->bp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getAlias()
|
||||
{
|
||||
return $this->importedNode()->getAlias();
|
||||
}
|
||||
|
||||
public function getUrl()
|
||||
{
|
||||
$params = array(
|
||||
'config' => $this->getConfigName(),
|
||||
'node' => $this->importedNode()->getName()
|
||||
);
|
||||
|
||||
return Url::fromPath('businessprocess/process/show', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function isMissing()
|
||||
{
|
||||
$this->getState();
|
||||
// Probably doesn't work, as we create a fake node with worse state
|
||||
return $this->missing;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function isInDowntime()
|
||||
{
|
||||
if ($this->downtime === null) {
|
||||
$this->getState();
|
||||
$this->downtime = $this->importedNode()->isInDowntime();
|
||||
if ($this->alias === null) {
|
||||
$this->alias = $this->importedNode()->getAlias();
|
||||
}
|
||||
|
||||
return $this->downtime;
|
||||
return $this->alias;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function isAcknowledged()
|
||||
public function getOperator()
|
||||
{
|
||||
if ($this->ack === null) {
|
||||
$this->getState();
|
||||
$this->downtime = $this->importedNode()->isAcknowledged();
|
||||
if ($this->operator === null) {
|
||||
$this->operator = $this->importedNode()->getOperator();
|
||||
}
|
||||
|
||||
return $this->ack;
|
||||
return $this->operator;
|
||||
}
|
||||
|
||||
public function getChildNames()
|
||||
{
|
||||
if ($this->childNames === null) {
|
||||
$this->childNames = $this->importedNode()->getChildNames();
|
||||
}
|
||||
|
||||
return $this->childNames;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -135,68 +99,15 @@ class ImportedNode extends Node
|
|||
*/
|
||||
protected function importedNode()
|
||||
{
|
||||
if ($this->node === null) {
|
||||
$this->node = $this->loadImportedNode();
|
||||
}
|
||||
|
||||
return $this->node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BpNode
|
||||
*/
|
||||
protected function loadImportedNode()
|
||||
{
|
||||
try {
|
||||
$import = $this->importedConfig();
|
||||
|
||||
return $import->getNode($this->nodeName);
|
||||
} catch (Exception $e) {
|
||||
return $this->createFailedNode($e);
|
||||
}
|
||||
}
|
||||
|
||||
protected function useAllRootNodes()
|
||||
{
|
||||
try {
|
||||
$bp = $this->importedConfig();
|
||||
$this->node = new BpNode($bp, (object) array(
|
||||
'name' => $this->getName(),
|
||||
'operator' => '&',
|
||||
'child_names' => $bp->listRootNodes(),
|
||||
));
|
||||
} catch (Exception $e) {
|
||||
$this->createFailedNode($e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BpConfig
|
||||
*/
|
||||
protected function importedConfig()
|
||||
{
|
||||
if ($this->config === null) {
|
||||
$import = $this->storage()->loadProcess($this->configName);
|
||||
if ($this->bp->usesSoftStates()) {
|
||||
$import->useSoftStates();
|
||||
} else {
|
||||
$import->useHardStates();
|
||||
if ($this->importedNode === null) {
|
||||
try {
|
||||
$this->importedNode = $this->getBpConfig()->getBpNode($this->nodeName);
|
||||
} catch (Exception $e) {
|
||||
return $this->createFailedNode($e);
|
||||
}
|
||||
|
||||
$this->config = $import;
|
||||
}
|
||||
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LegacyStorage
|
||||
*/
|
||||
protected function storage()
|
||||
{
|
||||
return new LegacyStorage(
|
||||
Config::module('businessprocess')->getSection('global')
|
||||
);
|
||||
return $this->importedNode;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -206,12 +117,13 @@ class ImportedNode extends Node
|
|||
*/
|
||||
protected function createFailedNode(Exception $e)
|
||||
{
|
||||
$this->bp->addError($e->getMessage());
|
||||
$node = new BpNode($this->importedConfig(), (object) array(
|
||||
$this->parentBp->addError($e->getMessage());
|
||||
$node = new BpNode((object) array(
|
||||
'name' => $this->getName(),
|
||||
'operator' => '&',
|
||||
'child_names' => array()
|
||||
'child_names' => []
|
||||
));
|
||||
$node->setBpConfig($this->getBpConfig());
|
||||
$node->setState(2);
|
||||
$node->setMissing(false)
|
||||
->setDowntime(false)
|
||||
|
|
@ -220,21 +132,4 @@ class ImportedNode extends Node
|
|||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getLink()
|
||||
{
|
||||
return Html::tag(
|
||||
'a',
|
||||
[
|
||||
'href' => Url::fromPath('businessprocess/process/show', [
|
||||
'config' => $this->configName,
|
||||
'node' => $this->nodeName
|
||||
])
|
||||
],
|
||||
$this->getAlias()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ class Metadata
|
|||
'AddToMenu' => null,
|
||||
'Backend' => null,
|
||||
'Statetype' => null,
|
||||
'ManualOrder' => null,
|
||||
// 'SLAHosts' => null
|
||||
);
|
||||
|
||||
|
|
@ -251,6 +252,11 @@ class Metadata
|
|||
return false;
|
||||
}
|
||||
|
||||
public function isManuallyOrdered()
|
||||
{
|
||||
return $this->get('ManualOrder') === 'yes';
|
||||
}
|
||||
|
||||
protected function splitCommaSeparated($string)
|
||||
{
|
||||
return preg_split('/\s*,\s*/', $string, -1, PREG_SPLIT_NO_EMPTY);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
namespace Icinga\Module\Businessprocess\Modification;
|
||||
|
||||
use Icinga\Module\Businessprocess\BpConfig;
|
||||
use Icinga\Module\Businessprocess\Exception\ModificationError;
|
||||
use Icinga\Module\Businessprocess\Node;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
|
||||
|
|
@ -48,10 +49,13 @@ abstract class NodeAction
|
|||
abstract public function applyTo(BpConfig $config);
|
||||
|
||||
/**
|
||||
* Every NodeAction must be able to tell whether it could be applied to a BusinessProcess
|
||||
* Every NodeAction must be able to tell whether it can be applied to a BusinessProcess
|
||||
*
|
||||
* @param BpConfig $config
|
||||
* @return bool
|
||||
* @param BpConfig $config
|
||||
*
|
||||
* @throws ModificationError
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function appliesTo(BpConfig $config);
|
||||
|
||||
|
|
@ -81,6 +85,21 @@ abstract class NodeAction
|
|||
return $this->getActionName() === $actionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw a ModificationError
|
||||
*
|
||||
* @param string $msg
|
||||
* @param mixed ...
|
||||
*
|
||||
* @throws ModificationError
|
||||
*/
|
||||
protected function error($msg)
|
||||
{
|
||||
$error = ModificationError::create(func_get_args());
|
||||
/** @var ModificationError $error */
|
||||
throw $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an instance of a given actionName for a specific Node
|
||||
*
|
||||
|
|
|
|||
|
|
@ -17,11 +17,11 @@ class NodeAddChildrenAction extends NodeAction
|
|||
{
|
||||
$name = $this->getNodeName();
|
||||
|
||||
if (! $config->hasNode($name)) {
|
||||
return false;
|
||||
if (! $config->hasBpNode($name)) {
|
||||
$this->error('Process "%s" not found', $name);
|
||||
}
|
||||
|
||||
return $config->getNode($name) instanceof BpNode;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -32,7 +32,7 @@ class NodeAddChildrenAction extends NodeAction
|
|||
$node = $config->getBpNode($this->getNodeName());
|
||||
|
||||
foreach ($this->children as $name) {
|
||||
if (! $config->hasNode($name)) {
|
||||
if (! $config->hasNode($name) || $config->getNode($name)->getBpConfig()->getName() !== $config->getName()) {
|
||||
if (strpos($name, ';') !== false) {
|
||||
list($host, $service) = preg_split('/;/', $name, 2);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Module\Businessprocess\Modification;
|
||||
|
||||
use Icinga\Module\Businessprocess\BpConfig;
|
||||
|
||||
class NodeApplyManualOrderAction extends NodeAction
|
||||
{
|
||||
public function appliesTo(BpConfig $config)
|
||||
{
|
||||
return $config->getMetadata()->get('ManualOrder') !== 'yes';
|
||||
}
|
||||
|
||||
public function applyTo(BpConfig $config)
|
||||
{
|
||||
$i = 0;
|
||||
foreach ($config->getBpNodes() as $name => $node) {
|
||||
if ($node->getDisplay() > 0) {
|
||||
$node->setDisplay(++$i);
|
||||
}
|
||||
|
||||
if ($node->hasChildren()) {
|
||||
$node->setChildNames($node->getChildNames());
|
||||
}
|
||||
}
|
||||
|
||||
$config->getMetadata()->set('ManualOrder', 'yes');
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ class NodeCreateAction extends NodeAction
|
|||
*/
|
||||
public function setParent(Node $name)
|
||||
{
|
||||
$this->parentName = (string) $name;
|
||||
$this->parentName = $name->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -72,7 +72,17 @@ class NodeCreateAction extends NodeAction
|
|||
*/
|
||||
public function appliesTo(BpConfig $config)
|
||||
{
|
||||
return ! $config->hasNode($this->getNodeName());
|
||||
$name = $this->getNodeName();
|
||||
if ($config->hasNode($name)) {
|
||||
$this->error('A node with name "%s" already exists', $name);
|
||||
}
|
||||
|
||||
$parent = $this->getParentName();
|
||||
if ($parent !== null && !$config->hasBpNode($parent)) {
|
||||
$this->error('Parent process "%s" missing', $parent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -91,7 +101,8 @@ class NodeCreateAction extends NodeAction
|
|||
} else {
|
||||
$properties['child_names'] = array();
|
||||
}
|
||||
$node = new BpNode($config, (object) $properties);
|
||||
$node = new BpNode((object) $properties);
|
||||
$node->setBpConfig($config);
|
||||
|
||||
foreach ($this->getProperties() as $key => $val) {
|
||||
if ($key === 'parentName') {
|
||||
|
|
@ -102,6 +113,15 @@ class NodeCreateAction extends NodeAction
|
|||
$node->$func($val);
|
||||
}
|
||||
|
||||
if ($node->getDisplay() > 1) {
|
||||
$i = $node->getDisplay();
|
||||
foreach ($config->getRootNodes() as $_ => $rootNode) {
|
||||
if ($rootNode->getDisplay() >= $node->getDisplay()) {
|
||||
$rootNode->setDisplay(++$i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$config->addNode($name, $node);
|
||||
|
||||
return $node;
|
||||
|
|
|
|||
|
|
@ -47,15 +47,21 @@ class NodeModifyAction extends NodeAction
|
|||
$name = $this->getNodeName();
|
||||
|
||||
if (! $config->hasNode($name)) {
|
||||
return false;
|
||||
$this->error('Node "%s" not found', $name);
|
||||
}
|
||||
|
||||
$node = $config->getNode($name);
|
||||
|
||||
foreach ($this->properties as $key => $val) {
|
||||
$func = 'get' . ucfirst($key);
|
||||
if ($this->formerProperties[$key] !== $node->$func()) {
|
||||
return false;
|
||||
$currentVal = $node->{'get' . ucfirst($key)}();
|
||||
if ($this->formerProperties[$key] !== $currentVal) {
|
||||
$this->error(
|
||||
'Property %s of node "%s" changed its value from "%s" to "%s"',
|
||||
$key,
|
||||
$name,
|
||||
$this->formerProperties[$key],
|
||||
$currentVal
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
212
library/Businessprocess/Modification/NodeMoveAction.php
Normal file
212
library/Businessprocess/Modification/NodeMoveAction.php
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Module\Businessprocess\Modification;
|
||||
|
||||
use Icinga\Module\Businessprocess\BpConfig;
|
||||
use Icinga\Module\Businessprocess\BpNode;
|
||||
|
||||
class NodeMoveAction extends NodeAction
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $parent;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $newParent;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $from;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $to;
|
||||
|
||||
protected $preserveProperties = ['parent', 'newParent', 'from', 'to'];
|
||||
|
||||
public function setParent($name)
|
||||
{
|
||||
$this->parent = $name;
|
||||
}
|
||||
|
||||
public function getParent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
public function setNewParent($name)
|
||||
{
|
||||
$this->newParent = $name;
|
||||
}
|
||||
|
||||
public function getNewParent()
|
||||
{
|
||||
return $this->newParent;
|
||||
}
|
||||
|
||||
public function setFrom($from)
|
||||
{
|
||||
$this->from = (int) $from;
|
||||
}
|
||||
|
||||
public function getFrom()
|
||||
{
|
||||
return $this->from;
|
||||
}
|
||||
|
||||
public function setTo($to)
|
||||
{
|
||||
$this->to = (int) $to;
|
||||
}
|
||||
|
||||
public function getTo()
|
||||
{
|
||||
return $this->to;
|
||||
}
|
||||
|
||||
public function appliesTo(BpConfig $config)
|
||||
{
|
||||
if (! $config->getMetadata()->isManuallyOrdered()) {
|
||||
$this->error('Process configuration is not manually ordered yet');
|
||||
}
|
||||
|
||||
$name = $this->getNodeName();
|
||||
if ($this->parent !== null) {
|
||||
if (! $config->hasBpNode($this->parent)) {
|
||||
$this->error('Parent process "%s" missing', $this->parent);
|
||||
}
|
||||
$parent = $config->getBpNode($this->parent);
|
||||
if (! $parent->hasChild($name)) {
|
||||
$this->error('Node "%s" not found in process "%s"', $name, $this->parent);
|
||||
}
|
||||
|
||||
$nodes = $parent->getChildNames();
|
||||
if (! isset($nodes[$this->from]) || $nodes[$this->from] !== $name) {
|
||||
$this->error('Node "%s" not found at position %d', $name, $this->from);
|
||||
}
|
||||
} else {
|
||||
if (! $config->hasRootNode($name)) {
|
||||
$this->error('Toplevel process "%s" not found', $name);
|
||||
}
|
||||
|
||||
$nodes = $config->listRootNodes();
|
||||
if (! isset($nodes[$this->from]) || $nodes[$this->from] !== $name) {
|
||||
$this->error('Toplevel process "%s" not found at position %d', $name, $this->from);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->parent !== $this->newParent) {
|
||||
if ($this->newParent !== null) {
|
||||
if (! $config->hasBpNode($this->newParent)) {
|
||||
$this->error('New parent process "%s" missing', $this->newParent);
|
||||
} elseif ($config->getBpNode($this->newParent)->hasChild($name)) {
|
||||
$this->error(
|
||||
'New parent process "%s" already has a node with the name "%s"',
|
||||
$this->newParent,
|
||||
$name
|
||||
);
|
||||
}
|
||||
|
||||
$childrenCount = $config->getBpNode($this->newParent)->countChildren();
|
||||
if ($this->to > 0 && $childrenCount < $this->to) {
|
||||
$this->error(
|
||||
'New parent process "%s" has not enough children. Target position %d out of range',
|
||||
$this->newParent,
|
||||
$this->to
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if ($config->hasRootNode($name)) {
|
||||
$this->error('Process "%s" is already a toplevel process', $name);
|
||||
}
|
||||
|
||||
$childrenCount = $config->countChildren();
|
||||
if ($this->to > 0 && $childrenCount < $this->to) {
|
||||
$this->error(
|
||||
'Process configuration has not enough toplevel processes. Target position %d out of range',
|
||||
$this->to
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function applyTo(BpConfig $config)
|
||||
{
|
||||
$name = $this->getNodeName();
|
||||
if ($this->parent !== null) {
|
||||
$nodes = $config->getBpNode($this->parent)->getChildren();
|
||||
} else {
|
||||
$nodes = $config->getRootNodes();
|
||||
}
|
||||
|
||||
$node = $nodes[$name];
|
||||
$nodes = array_merge(
|
||||
array_slice($nodes, 0, $this->from, true),
|
||||
array_slice($nodes, $this->from + 1, null, true)
|
||||
);
|
||||
if ($this->parent === $this->newParent) {
|
||||
$nodes = array_merge(
|
||||
array_slice($nodes, 0, $this->to, true),
|
||||
[$name => $node],
|
||||
array_slice($nodes, $this->to, null, true)
|
||||
);
|
||||
} else {
|
||||
if ($this->newParent !== null) {
|
||||
$newNodes = $config->getBpNode($this->newParent)->getChildren();
|
||||
} else {
|
||||
$newNodes = $config->getRootNodes();
|
||||
}
|
||||
|
||||
$newNodes = array_merge(
|
||||
array_slice($newNodes, 0, $this->to, true),
|
||||
[$name => $node],
|
||||
array_slice($newNodes, $this->to, null, true)
|
||||
);
|
||||
|
||||
if ($this->newParent !== null) {
|
||||
$config->getBpNode($this->newParent)->setChildNames(array_keys($newNodes));
|
||||
} else {
|
||||
$config->addRootNode($name);
|
||||
|
||||
$i = 0;
|
||||
foreach ($newNodes as $newName => $newNode) {
|
||||
/** @var BpNode $newNode */
|
||||
if ($newNode->getDisplay() > 0 || $newName === $name) {
|
||||
$i += 1;
|
||||
if ($newNode->getDisplay() !== $i) {
|
||||
$newNode->setDisplay($i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->parent !== null) {
|
||||
$config->getBpNode($this->parent)->setChildNames(array_keys($nodes));
|
||||
} else {
|
||||
if ($this->newParent !== null) {
|
||||
$config->removeRootNode($name);
|
||||
$node->setDisplay(0);
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
foreach ($nodes as $_ => $oldNode) {
|
||||
/** @var BpNode $oldNode */
|
||||
if ($oldNode->getDisplay() > 0) {
|
||||
$i += 1;
|
||||
if ($oldNode->getDisplay() !== $i) {
|
||||
$oldNode->setDisplay($i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -40,12 +40,21 @@ class NodeRemoveAction extends NodeAction
|
|||
*/
|
||||
public function appliesTo(BpConfig $config)
|
||||
{
|
||||
$name = $this->getNodeName();
|
||||
$parent = $this->getParentName();
|
||||
if ($parent === null) {
|
||||
return $config->hasNode($this->getNodeName());
|
||||
if (!$config->hasNode($name)) {
|
||||
$this->error('Toplevel process "%s" not found', $name);
|
||||
}
|
||||
} else {
|
||||
return $config->hasNode($this->getNodeName()) && $config->hasNode($this->getParentName());
|
||||
if (! $config->hasNode($parent)) {
|
||||
$this->error('Parent process "%s" missing', $parent);
|
||||
} elseif (! $config->getBpNode($parent)->hasChild($name)) {
|
||||
$this->error('Node "%s" not found in process "%s"', $name, $parent);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -57,7 +66,18 @@ class NodeRemoveAction extends NodeAction
|
|||
$name = $this->getNodeName();
|
||||
$parentName = $this->getParentName();
|
||||
if ($parentName === null) {
|
||||
$oldDisplay = $config->getBpNode($name)->getDisplay();
|
||||
$config->removeNode($name);
|
||||
if ($config->getMetadata()->isManuallyOrdered()) {
|
||||
foreach ($config->getRootNodes() as $_ => $node) {
|
||||
$nodeDisplay = $node->getDisplay();
|
||||
if ($nodeDisplay > $oldDisplay) {
|
||||
$node->setDisplay($node->getDisplay() - 1);
|
||||
} elseif ($nodeDisplay === $oldDisplay) {
|
||||
break; // Stop immediately to not make things worse ;)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$node = $config->getNode($name);
|
||||
$parent = $config->getBpNode($parentName);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ class ProcessChanges
|
|||
/** @var Session */
|
||||
protected $session;
|
||||
|
||||
/** @var BpConfig */
|
||||
protected $config;
|
||||
|
||||
/** @var bool */
|
||||
protected $hasBeenModified = false;
|
||||
|
||||
|
|
@ -47,6 +50,7 @@ class ProcessChanges
|
|||
}
|
||||
}
|
||||
$changes->session = $session;
|
||||
$changes->config = $bp;
|
||||
|
||||
return $changes;
|
||||
}
|
||||
|
|
@ -61,7 +65,7 @@ class ProcessChanges
|
|||
{
|
||||
$action = new NodeModifyAction($node);
|
||||
$action->setNodeProperties($node, $properties);
|
||||
return $this->push($action);
|
||||
return $this->push($action, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -74,7 +78,7 @@ class ProcessChanges
|
|||
{
|
||||
$action = new NodeAddChildrenAction($node);
|
||||
$action->setChildren($children);
|
||||
return $this->push($action);
|
||||
return $this->push($action, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -91,7 +95,7 @@ class ProcessChanges
|
|||
if ($parent !== null) {
|
||||
$action->setParent($parent);
|
||||
}
|
||||
return $this->push($action);
|
||||
return $this->push($action, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -117,18 +121,55 @@ class ProcessChanges
|
|||
$action->setParentName($parentName);
|
||||
}
|
||||
|
||||
return $this->push($action);
|
||||
return $this->push($action, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the given node
|
||||
*
|
||||
* @param Node $node
|
||||
* @param int $from
|
||||
* @param int $to
|
||||
* @param string $newParent
|
||||
* @param string $parent
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function moveNode(Node $node, $from, $to, $newParent, $parent = null)
|
||||
{
|
||||
$action = new NodeMoveAction($node);
|
||||
$action->setParent($parent);
|
||||
$action->setNewParent($newParent);
|
||||
$action->setFrom($from);
|
||||
$action->setTo($to);
|
||||
|
||||
return $this->push($action, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply manual order on the entire bp configuration file
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function applyManualOrder()
|
||||
{
|
||||
return $this->push(new NodeApplyManualOrderAction(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new action to the stack
|
||||
*
|
||||
* @param NodeAction $change
|
||||
* @param NodeAction $change
|
||||
* @param bool $apply
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function push(NodeAction $change)
|
||||
public function push(NodeAction $change, $apply = false)
|
||||
{
|
||||
if ($apply && $change->appliesTo($this->config)) {
|
||||
$change->applyTo($this->config);
|
||||
}
|
||||
|
||||
$this->changes[] = $change;
|
||||
$this->hasBeenModified = true;
|
||||
return $this;
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ abstract class Node
|
|||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $parents;
|
||||
protected $parents = array();
|
||||
|
||||
/**
|
||||
* Node identifier
|
||||
|
|
@ -83,6 +83,13 @@ abstract class Node
|
|||
// obsolete
|
||||
protected $duration;
|
||||
|
||||
/**
|
||||
* This node's icon
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $icon;
|
||||
|
||||
/**
|
||||
* Last state change, unix timestamp
|
||||
*
|
||||
|
|
@ -102,7 +109,18 @@ abstract class Node
|
|||
99 => 'PENDING'
|
||||
);
|
||||
|
||||
abstract public function __construct(BpConfig $bp, $object);
|
||||
abstract public function __construct($object);
|
||||
|
||||
public function setBpConfig(BpConfig $bp)
|
||||
{
|
||||
$this->bp = $bp;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBpConfig()
|
||||
{
|
||||
return $this->bp;
|
||||
}
|
||||
|
||||
public function setMissing($missing = true)
|
||||
{
|
||||
|
|
@ -286,7 +304,7 @@ abstract class Node
|
|||
|
||||
public function hasParents()
|
||||
{
|
||||
return count($this->getParents()) > 0;
|
||||
return count($this->parents) > 0;
|
||||
}
|
||||
|
||||
public function hasParentName($name)
|
||||
|
|
@ -303,7 +321,7 @@ abstract class Node
|
|||
public function removeParent($name)
|
||||
{
|
||||
$this->parents = array_filter(
|
||||
$this->getParents(),
|
||||
$this->parents,
|
||||
function (BpNode $parent) use ($name) {
|
||||
return $parent->getName() !== $name;
|
||||
}
|
||||
|
|
@ -317,35 +335,35 @@ abstract class Node
|
|||
*/
|
||||
public function getParents()
|
||||
{
|
||||
if ($this->parents === null) {
|
||||
$this->parents = [];
|
||||
foreach ($this->bp->getBpNodes() as $name => $node) {
|
||||
if ($node->hasChild($this->getName())) {
|
||||
$this->parents[] = $node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->parents;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param BpConfig $rootConfig
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPaths()
|
||||
public function getPaths($rootConfig = null)
|
||||
{
|
||||
if ($this->bp->hasRootNode($this->getName())) {
|
||||
return array(array($this->getName()));
|
||||
$differentConfig = false;
|
||||
if ($rootConfig === null) {
|
||||
$rootConfig = $this->getBpConfig();
|
||||
} else {
|
||||
$differentConfig = $this->getBpConfig()->getName() !== $rootConfig->getName();
|
||||
}
|
||||
|
||||
$paths = array();
|
||||
foreach ($this->getParents() as $parent) {
|
||||
foreach ($parent->getPaths() as $path) {
|
||||
$path[] = $this->getName();
|
||||
$paths = [];
|
||||
foreach ($this->parents as $parent) {
|
||||
foreach ($parent->getPaths($rootConfig) as $path) {
|
||||
$path[] = $differentConfig ? $this->getIdentifier() : $this->getName();
|
||||
$paths[] = $path;
|
||||
}
|
||||
}
|
||||
|
||||
if (! $this instanceof ImportedNode && $this->getBpConfig()->hasRootNode($this->getName())) {
|
||||
$paths[] = [$differentConfig ? $this->getIdentifier() : $this->getName()];
|
||||
}
|
||||
|
||||
return $paths;
|
||||
}
|
||||
|
||||
|
|
@ -379,7 +397,14 @@ abstract class Node
|
|||
|
||||
public function getLink()
|
||||
{
|
||||
return Html::tag('a', ['href' => '#'], $this->getAlias());
|
||||
return Html::tag('a', ['href' => '#', 'class' => 'toggle'], Html::tag('i', [
|
||||
'class' => 'icon icon-down-dir'
|
||||
]));
|
||||
}
|
||||
|
||||
public function getIcon()
|
||||
{
|
||||
return Html::tag('i', ['class' => 'icon icon-' . ($this->icon ?: 'attention-circled')]);
|
||||
}
|
||||
|
||||
public function operatorHtml()
|
||||
|
|
@ -392,6 +417,11 @@ abstract class Node
|
|||
return $this->name;
|
||||
}
|
||||
|
||||
public function getIdentifier()
|
||||
{
|
||||
return '@' . $this->getBpConfig()->getName() . ':' . $this->getName();
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getName();
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ class Breadcrumb extends BaseHtmlElement
|
|||
'href' => Url::fromPath('businessprocess'),
|
||||
'title' => mt('businessprocess', 'Show Overview')
|
||||
],
|
||||
Html::tag('i', ['class' => 'icon icon-dashboard'])
|
||||
Html::tag('i', ['class' => 'icon icon-home'])
|
||||
)
|
||||
));
|
||||
$breadcrumb->add(Html::tag('li')->add(
|
||||
|
|
@ -46,10 +46,12 @@ class Breadcrumb extends BaseHtmlElement
|
|||
$path = $renderer->getCurrentPath();
|
||||
|
||||
$parts = array();
|
||||
while ($node = array_pop($path)) {
|
||||
while ($nodeName = array_pop($path)) {
|
||||
$node = $bp->getNode($nodeName);
|
||||
$renderer->setParentNode($node);
|
||||
array_unshift(
|
||||
$parts,
|
||||
static::renderNode($bp->getNode($node), $path, $renderer)
|
||||
static::renderNode($node, $path, $renderer)
|
||||
);
|
||||
}
|
||||
$breadcrumb->add($parts);
|
||||
|
|
@ -69,8 +71,7 @@ class Breadcrumb extends BaseHtmlElement
|
|||
// TODO: something more generic than NodeTile?
|
||||
$renderer = clone($renderer);
|
||||
$renderer->lock()->setIsBreadcrumb();
|
||||
$p = new NodeTile($renderer, (string) $node, $node, $path);
|
||||
$p->getAttributes()->add('class', $renderer->getNodeClasses($node));
|
||||
$p = new NodeTile($renderer, $node, $path);
|
||||
$p->setTag('li');
|
||||
return $p;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Icinga\Module\Businessprocess\Renderer;
|
||||
|
||||
use Icinga\Date\DateFormatter;
|
||||
use Icinga\Exception\ProgrammingError;
|
||||
use Icinga\Module\Businessprocess\BpNode;
|
||||
use Icinga\Module\Businessprocess\BpConfig;
|
||||
|
|
@ -11,7 +10,6 @@ use Icinga\Module\Businessprocess\Web\Url;
|
|||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\Html;
|
||||
use ipl\Html\HtmlDocument;
|
||||
use ipl\Html\HtmlString;
|
||||
|
||||
abstract class Renderer extends HtmlDocument
|
||||
{
|
||||
|
|
@ -76,6 +74,17 @@ abstract class Renderer extends HtmlDocument
|
|||
return $this->parent !== null;
|
||||
}
|
||||
|
||||
public function rendersImportedNode()
|
||||
{
|
||||
return $this->parent !== null && $this->parent->getBpConfig()->getName() !== $this->config->getName();
|
||||
}
|
||||
|
||||
public function setParentNode(BpNode $node)
|
||||
{
|
||||
$this->parent = $node;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BpNode
|
||||
*/
|
||||
|
|
@ -187,6 +196,16 @@ abstract class Renderer extends HtmlDocument
|
|||
return $classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node $node
|
||||
* @param $path
|
||||
* @return string
|
||||
*/
|
||||
public function getId(Node $node, $path)
|
||||
{
|
||||
return md5((empty($path) ? '' : implode(';', $path)) . $node->getName());
|
||||
}
|
||||
|
||||
public function setPath(array $path)
|
||||
{
|
||||
$this->path = $path;
|
||||
|
|
@ -194,7 +213,7 @@ abstract class Renderer extends HtmlDocument
|
|||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
* @return array
|
||||
*/
|
||||
public function getPath()
|
||||
{
|
||||
|
|
@ -205,8 +224,11 @@ abstract class Renderer extends HtmlDocument
|
|||
{
|
||||
$path = $this->getPath();
|
||||
if ($this->rendersSubNode()) {
|
||||
$path[] = (string) $this->parent;
|
||||
$path[] = $this->rendersImportedNode()
|
||||
? $this->parent->getIdentifier()
|
||||
: $this->parent->getName();
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
|
|
@ -298,22 +320,6 @@ abstract class Renderer extends HtmlDocument
|
|||
return $this->isBreadcrumb;
|
||||
}
|
||||
|
||||
public function timeSince($time, $timeOnly = false)
|
||||
{
|
||||
if (! $time) {
|
||||
return HtmlString::create('');
|
||||
}
|
||||
|
||||
return Html::tag(
|
||||
'span',
|
||||
[
|
||||
'class' => ['relative-time', 'time-since'],
|
||||
'title' => DateFormatter::formatDateTime($time)
|
||||
],
|
||||
DateFormatter::timeSince($time, $timeOnly)
|
||||
);
|
||||
}
|
||||
|
||||
protected function createUnboundParent(BpConfig $bp)
|
||||
{
|
||||
return $bp->getNode('__unbound__');
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@
|
|||
|
||||
namespace Icinga\Module\Businessprocess\Renderer;
|
||||
|
||||
use Icinga\Module\Businessprocess\ImportedNode;
|
||||
use Icinga\Module\Businessprocess\Renderer\TileRenderer\NodeTile;
|
||||
use Icinga\Module\Businessprocess\Web\Form\CsrfToken;
|
||||
use ipl\Html\Html;
|
||||
|
||||
class TileRenderer extends Renderer
|
||||
|
|
@ -16,23 +18,45 @@ class TileRenderer extends Renderer
|
|||
$nodesDiv = Html::tag(
|
||||
'div',
|
||||
[
|
||||
'class' => ['tiles', $this->howMany()],
|
||||
'data-base-target' => '_next'
|
||||
'class' => ['sortable', 'tiles', $this->howMany()],
|
||||
'data-base-target' => '_next',
|
||||
'data-sortable-disabled' => $this->isLocked() ? 'true' : 'false',
|
||||
'data-sortable-data-id-attr' => 'id',
|
||||
'data-sortable-direction' => 'horizontal', // Otherwise movement is buggy on small lists
|
||||
'data-csrf-token' => CsrfToken::generate()
|
||||
]
|
||||
);
|
||||
|
||||
if ($this->wantsRootNodes()) {
|
||||
$nodesDiv->getAttributes()->add(
|
||||
'data-action-url',
|
||||
$this->getUrl()->setParams(['config' => $bp->getName()])->getAbsoluteUrl()
|
||||
);
|
||||
} else {
|
||||
$nodeName = $this->parent instanceof ImportedNode
|
||||
? $this->parent->getNodeName()
|
||||
: $this->parent->getName();
|
||||
$nodesDiv->getAttributes()
|
||||
->add('data-node-name', $nodeName)
|
||||
->add('data-action-url', $this->getUrl()
|
||||
->setParams([
|
||||
'config' => $this->parent->getBpConfig()->getName(),
|
||||
'node' => $nodeName
|
||||
])
|
||||
->getAbsoluteUrl());
|
||||
}
|
||||
|
||||
$nodes = $this->getChildNodes();
|
||||
|
||||
$path = $this->getCurrentPath();
|
||||
foreach ($nodes as $name => $node) {
|
||||
$this->add(new NodeTile($this, $name, $node, $path));
|
||||
$this->add(new NodeTile($this, $node, $path));
|
||||
}
|
||||
|
||||
if ($this->wantsRootNodes()) {
|
||||
$unbound = $this->createUnboundParent($bp);
|
||||
if ($unbound->hasChildren()) {
|
||||
$name = $unbound->getName();
|
||||
$this->add(new NodeTile($this, $name, $unbound));
|
||||
$this->add(new NodeTile($this, $unbound));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Icinga\Module\Businessprocess\Renderer\TileRenderer;
|
||||
|
||||
use Icinga\Date\DateFormatter;
|
||||
use Icinga\Module\Businessprocess\BpNode;
|
||||
use Icinga\Module\Businessprocess\HostNode;
|
||||
use Icinga\Module\Businessprocess\ImportedNode;
|
||||
|
|
@ -9,6 +10,8 @@ use Icinga\Module\Businessprocess\MonitoredNode;
|
|||
use Icinga\Module\Businessprocess\Node;
|
||||
use Icinga\Module\Businessprocess\Renderer\Renderer;
|
||||
use Icinga\Module\Businessprocess\ServiceNode;
|
||||
use Icinga\Module\Businessprocess\Web\Component\StateBall;
|
||||
use Icinga\Web\Url;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\Html;
|
||||
|
||||
|
|
@ -36,10 +39,9 @@ class NodeTile extends BaseHtmlElement
|
|||
* @param Node $node
|
||||
* @param null $path
|
||||
*/
|
||||
public function __construct(Renderer $renderer, $name, Node $node, $path = null)
|
||||
public function __construct(Renderer $renderer, Node $node, $path = null)
|
||||
{
|
||||
$this->renderer = $renderer;
|
||||
$this->name = $name;
|
||||
$this->node = $node;
|
||||
$this->path = $path;
|
||||
}
|
||||
|
|
@ -72,38 +74,45 @@ class NodeTile extends BaseHtmlElement
|
|||
|
||||
$attributes = $this->getAttributes();
|
||||
$attributes->add('class', $renderer->getNodeClasses($node));
|
||||
$attributes->add('id', 'bp-' . (string) $node);
|
||||
|
||||
$this->addActions();
|
||||
|
||||
$link = $this->getMainNodeLink();
|
||||
$this->add($link);
|
||||
|
||||
if ($node instanceof BpNode) {
|
||||
if ($renderer->isBreadcrumb()) {
|
||||
$link->add($renderer->renderStateBadges($node->getStateSummary()));
|
||||
} else {
|
||||
$this->add(Html::tag(
|
||||
'p',
|
||||
['class' => 'children-count'],
|
||||
$node->hasChildren()
|
||||
? Html::tag(
|
||||
'span',
|
||||
null,
|
||||
sprintf('%u %s', $node->countChildren(), mt('businessprocess', 'Children'))
|
||||
)
|
||||
: null
|
||||
));
|
||||
$this->add($renderer->renderStateBadges($node->getStateSummary()));
|
||||
}
|
||||
$attributes->add('id', $renderer->getId($node, $this->path));
|
||||
if (! $renderer->isLocked()) {
|
||||
$attributes->add('data-node-name', $node->getName());
|
||||
}
|
||||
|
||||
if (! $renderer->isBreadcrumb()) {
|
||||
$this->addDetailsActions();
|
||||
|
||||
if (! $renderer->isLocked()) {
|
||||
$this->addActionLinks();
|
||||
}
|
||||
}
|
||||
|
||||
if (! $renderer->isLocked()) {
|
||||
$this->addActionLinks();
|
||||
$link = $this->getMainNodeLink();
|
||||
if ($renderer->isBreadcrumb()) {
|
||||
$link->prepend((new StateBall(strtolower($node->getStateName())))->addAttributes([
|
||||
'title' => sprintf(
|
||||
'%s %s',
|
||||
$node->getStateName(),
|
||||
DateFormatter::timeSince($node->getLastStateChange())
|
||||
)
|
||||
]));
|
||||
}
|
||||
|
||||
$this->add($link);
|
||||
|
||||
if ($node instanceof BpNode && !$renderer->isBreadcrumb()) {
|
||||
$this->add(Html::tag(
|
||||
'p',
|
||||
['class' => 'children-count'],
|
||||
$node->hasChildren()
|
||||
? Html::tag(
|
||||
'span',
|
||||
null,
|
||||
sprintf('%u %s', $node->countChildren(), mt('businessprocess', 'Children'))
|
||||
)
|
||||
: null
|
||||
));
|
||||
$this->add($renderer->renderStateBadges($node->getStateSummary()));
|
||||
}
|
||||
|
||||
return parent::render();
|
||||
|
|
@ -121,26 +130,21 @@ class NodeTile extends BaseHtmlElement
|
|||
|
||||
protected function buildBaseNodeUrl(Node $node)
|
||||
{
|
||||
$path = $this->path;
|
||||
$name = $this->name; // TODO: ??
|
||||
$renderer = $this->renderer;
|
||||
$url = $this->renderer->getBaseUrl();
|
||||
|
||||
$bp = $renderer->getBusinessProcess();
|
||||
$params = array(
|
||||
'config' => $node instanceof ImportedNode ?
|
||||
$node->getConfigName() :
|
||||
$bp->getName()
|
||||
);
|
||||
|
||||
if ($name !== null) {
|
||||
$params['node'] = $name;
|
||||
$p = $url->getParams();
|
||||
if ($node instanceof ImportedNode
|
||||
&& $this->renderer->getBusinessProcess()->getName() === $node->getBpConfig()->getName()
|
||||
) {
|
||||
$p->set('node', $node->getNodeName());
|
||||
} elseif ($this->renderer->rendersImportedNode()) {
|
||||
$p->set('node', $node->getIdentifier());
|
||||
} else {
|
||||
$p->set('node', $node->getName());
|
||||
}
|
||||
|
||||
$url = $renderer->getBaseUrl();
|
||||
$p = $url->getParams();
|
||||
$p->mergeValues($params);
|
||||
if (! empty($path)) {
|
||||
$p->addValues('path', $path);
|
||||
if (! empty($this->path)) {
|
||||
$p->addValues('path', $this->path);
|
||||
}
|
||||
|
||||
return $url;
|
||||
|
|
@ -151,31 +155,6 @@ class NodeTile extends BaseHtmlElement
|
|||
return $this->buildBaseNodeUrl($node);
|
||||
}
|
||||
|
||||
protected function makeMonitoredNodeUrl(MonitoredNode $node)
|
||||
{
|
||||
$path = $this->path;
|
||||
$name = $this->name; // TODO: ??
|
||||
$renderer = $this->renderer;
|
||||
|
||||
$bp = $renderer->getBusinessProcess();
|
||||
$params = array(
|
||||
'config' => $bp->getName()
|
||||
);
|
||||
|
||||
if ($name !== null) {
|
||||
$params['node'] = $node->getName();
|
||||
}
|
||||
|
||||
$url = $renderer->getBaseUrl();
|
||||
$p = $url->getParams();
|
||||
$p->mergeValues($params);
|
||||
if (! empty($path)) {
|
||||
$p->addValues('path', $path);
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BaseHtmlElement
|
||||
*/
|
||||
|
|
@ -189,11 +168,7 @@ class NodeTile extends BaseHtmlElement
|
|||
$link = Html::tag('a', ['href' => $url, 'data-base-target' => '_next'], $node->getHostname());
|
||||
} else {
|
||||
$link = Html::tag('a', ['href' => $url], $node->getAlias());
|
||||
if ($node instanceof ImportedNode) {
|
||||
$link->getAttributes()->add('data-base-target', '_next');
|
||||
} else {
|
||||
$link->getAttributes()->add('data-base-target', '_self');
|
||||
}
|
||||
$link->getAttributes()->add('data-base-target', '_self');
|
||||
}
|
||||
|
||||
return $link;
|
||||
|
|
@ -260,15 +235,29 @@ class NodeTile extends BaseHtmlElement
|
|||
|
||||
protected function addActionLinks()
|
||||
{
|
||||
$node = $this->node;
|
||||
$renderer = $this->renderer;
|
||||
if ($node instanceof MonitoredNode) {
|
||||
$parent = $this->renderer->getParentNode();
|
||||
if ($parent !== null) {
|
||||
$baseUrl = Url::fromPath('businessprocess/process/show', [
|
||||
'config' => $parent->getBpConfig()->getName(),
|
||||
'node' => $parent instanceof ImportedNode
|
||||
? $parent->getNodeName()
|
||||
: $parent->getName(),
|
||||
'unlocked' => true
|
||||
]);
|
||||
} else {
|
||||
$baseUrl = Url::fromPath('businessprocess/process/show', [
|
||||
'config' => $this->node->getBpConfig()->getName(),
|
||||
'unlocked' => true
|
||||
]);
|
||||
}
|
||||
|
||||
if ($this->node instanceof MonitoredNode) {
|
||||
$this->actions()->add(Html::tag(
|
||||
'a',
|
||||
[
|
||||
'href' => $renderer->getUrl()
|
||||
'href' => $baseUrl
|
||||
->with('action', 'simulation')
|
||||
->with('simulationnode', $this->name),
|
||||
->with('simulationnode', $this->node->getName()),
|
||||
'title' => mt(
|
||||
'businessprocess',
|
||||
'Show the business impact of this node by simulating a specific state'
|
||||
|
|
@ -280,9 +269,9 @@ class NodeTile extends BaseHtmlElement
|
|||
$this->actions()->add(Html::tag(
|
||||
'a',
|
||||
[
|
||||
'href' => $renderer->getUrl()
|
||||
'href' => $baseUrl
|
||||
->with('action', 'editmonitored')
|
||||
->with('editmonitorednode', $node->getName()),
|
||||
->with('editmonitorednode', $this->node->getName()),
|
||||
'title' => mt('businessprocess', 'Modify this monitored node')
|
||||
],
|
||||
Html::tag('i', ['class' => 'icon icon-edit'])
|
||||
|
|
@ -290,18 +279,18 @@ class NodeTile extends BaseHtmlElement
|
|||
}
|
||||
|
||||
if (! $this->renderer->getBusinessProcess()->getMetadata()->canModify()
|
||||
|| $node->getName() === '__unbound__'
|
||||
|| $this->node->getName() === '__unbound__'
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($node instanceof BpNode) {
|
||||
if ($this->node instanceof BpNode) {
|
||||
$this->actions()->add(Html::tag(
|
||||
'a',
|
||||
[
|
||||
'href' => $renderer->getUrl()
|
||||
'href' => $baseUrl
|
||||
->with('action', 'edit')
|
||||
->with('editnode', $node->getName()),
|
||||
->with('editnode', $this->node->getName()),
|
||||
'title' => mt('businessprocess', 'Modify this business process node')
|
||||
],
|
||||
Html::tag('i', ['class' => 'icon icon-edit'])
|
||||
|
|
@ -310,10 +299,16 @@ class NodeTile extends BaseHtmlElement
|
|||
$this->actions()->add(Html::tag(
|
||||
'a',
|
||||
[
|
||||
'href' => $renderer->getUrl()->with([
|
||||
'action' => 'add',
|
||||
'node' => $node->getName()
|
||||
]),
|
||||
'href' => $this->node instanceof ImportedNode
|
||||
? $baseUrl->with([
|
||||
'config' => $this->node->getConfigName(),
|
||||
'node' => $this->node->getNodeName(),
|
||||
'action' => 'add'
|
||||
])
|
||||
: $baseUrl->with([
|
||||
'node' => $this->node->getName(),
|
||||
'action' => 'add'
|
||||
]),
|
||||
'title' => mt('businessprocess', 'Add a new sub-node to this business process')
|
||||
],
|
||||
Html::tag('i', ['class' => 'icon icon-plus'])
|
||||
|
|
@ -322,13 +317,13 @@ class NodeTile extends BaseHtmlElement
|
|||
|
||||
$params = array(
|
||||
'action' => 'delete',
|
||||
'deletenode' => $node->getName(),
|
||||
'deletenode' => $this->node->getName(),
|
||||
);
|
||||
|
||||
$this->actions()->add(Html::tag(
|
||||
'a',
|
||||
[
|
||||
'href' => $renderer->getUrl()->with($params),
|
||||
'href' => $baseUrl->with($params),
|
||||
'title' => mt('businessprocess', 'Delete this node')
|
||||
],
|
||||
Html::tag('i', ['class' => 'icon icon-cancel'])
|
||||
|
|
|
|||
|
|
@ -2,9 +2,13 @@
|
|||
|
||||
namespace Icinga\Module\Businessprocess\Renderer;
|
||||
|
||||
use Icinga\Module\Businessprocess\BpNode;
|
||||
use Icinga\Date\DateFormatter;
|
||||
use Icinga\Module\Businessprocess\BpConfig;
|
||||
use Icinga\Module\Businessprocess\BpNode;
|
||||
use Icinga\Module\Businessprocess\ImportedNode;
|
||||
use Icinga\Module\Businessprocess\Node;
|
||||
use Icinga\Module\Businessprocess\Web\Component\StateBall;
|
||||
use Icinga\Module\Businessprocess\Web\Form\CsrfToken;
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
use ipl\Html\Html;
|
||||
|
||||
|
|
@ -16,15 +20,45 @@ class TreeRenderer extends Renderer
|
|||
public function render()
|
||||
{
|
||||
$bp = $this->config;
|
||||
$this->add(Html::tag(
|
||||
'div',
|
||||
$htmlId = $bp->getHtmlId();
|
||||
$tree = Html::tag(
|
||||
'ul',
|
||||
[
|
||||
'id' => $bp->getHtmlId(),
|
||||
'class' => 'bp'
|
||||
'id' => $htmlId,
|
||||
'class' => ['bp', 'sortable', $this->wantsRootNodes() ? '' : 'process'],
|
||||
'data-sortable-disabled' => $this->isLocked() ? 'true' : 'false',
|
||||
'data-sortable-data-id-attr' => 'id',
|
||||
'data-sortable-direction' => 'vertical',
|
||||
'data-sortable-group' => json_encode([
|
||||
'name' => $this->wantsRootNodes() ? 'root' : $htmlId,
|
||||
'put' => 'function:rowPutAllowed'
|
||||
]),
|
||||
'data-sortable-invert-swap' => 'true',
|
||||
'data-is-root-config' => $this->wantsRootNodes() ? 'true' : 'false',
|
||||
'data-csrf-token' => CsrfToken::generate()
|
||||
],
|
||||
$this->renderBp($bp)
|
||||
));
|
||||
);
|
||||
if ($this->wantsRootNodes()) {
|
||||
$tree->getAttributes()->add(
|
||||
'data-action-url',
|
||||
$this->getUrl()->setParams(['config' => $bp->getName()])->getAbsoluteUrl()
|
||||
);
|
||||
} else {
|
||||
$nodeName = $this->parent instanceof ImportedNode
|
||||
? $this->parent->getNodeName()
|
||||
: $this->parent->getName();
|
||||
$tree->getAttributes()
|
||||
->add('data-node-name', $nodeName)
|
||||
->add('data-action-url', $this->getUrl()
|
||||
->setParams([
|
||||
'config' => $this->parent->getBpConfig()->getName(),
|
||||
'node' => $nodeName
|
||||
])
|
||||
->getAbsoluteUrl());
|
||||
}
|
||||
|
||||
$this->add($tree);
|
||||
return parent::render();
|
||||
}
|
||||
|
||||
|
|
@ -42,22 +76,16 @@ class TreeRenderer extends Renderer
|
|||
}
|
||||
|
||||
foreach ($nodes as $name => $node) {
|
||||
$html[] = $this->renderNode($bp, $node);
|
||||
if ($node instanceof BpNode) {
|
||||
$html[] = $this->renderNode($bp, $node);
|
||||
} else {
|
||||
$html[] = $this->renderChild($bp, $node);
|
||||
}
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Node $node
|
||||
* @param $path
|
||||
* @return string
|
||||
*/
|
||||
protected function getId(Node $node, $path)
|
||||
{
|
||||
return md5(implode(';', $path) . (string) $node);
|
||||
}
|
||||
|
||||
protected function getStateClassNames(Node $node)
|
||||
{
|
||||
$state = strtolower($node->getStateName());
|
||||
|
|
@ -77,11 +105,24 @@ class TreeRenderer extends Renderer
|
|||
|
||||
/**
|
||||
* @param Node $node
|
||||
* @param array $path
|
||||
* @return BaseHtmlElement[]
|
||||
*/
|
||||
public function getNodeIcons(Node $node)
|
||||
public function getNodeIcons(Node $node, array $path = null)
|
||||
{
|
||||
$icons = array();
|
||||
$icons = [];
|
||||
if (empty($path) && $node instanceof BpNode) {
|
||||
$icons[] = Html::tag('i', ['class' => 'icon icon-sitemap']);
|
||||
} else {
|
||||
$icons[] = $node->getIcon();
|
||||
}
|
||||
$icons[] = (new StateBall(strtolower($node->getStateName())))->addAttributes([
|
||||
'title' => sprintf(
|
||||
'%s %s',
|
||||
$node->getStateName(),
|
||||
DateFormatter::timeSince($node->getLastStateChange())
|
||||
)
|
||||
]);
|
||||
if ($node->isInDowntime()) {
|
||||
$icons[] = Html::tag('i', ['class' => 'icon icon-moon']);
|
||||
}
|
||||
|
|
@ -100,17 +141,16 @@ class TreeRenderer extends Renderer
|
|||
*/
|
||||
public function renderNode(BpConfig $bp, Node $node, $path = array())
|
||||
{
|
||||
$table = Html::tag(
|
||||
'table',
|
||||
$htmlId = $this->getId($node, $path);
|
||||
$li = Html::tag(
|
||||
'li',
|
||||
[
|
||||
'id' => $this->getId($node, $path),
|
||||
'class' => array(
|
||||
'bp',
|
||||
$node->getObjectClassName()
|
||||
)
|
||||
'id' => $htmlId,
|
||||
'class' => ['bp', 'movable', $node->getObjectClassName()],
|
||||
'data-node-name' => $node->getName()
|
||||
]
|
||||
);
|
||||
$attributes = $table->getAttributes();
|
||||
$attributes = $li->getAttributes();
|
||||
$attributes->add('class', $this->getStateClassNames($node));
|
||||
if ($node->isHandled()) {
|
||||
$attributes->add('class', 'handled');
|
||||
|
|
@ -121,78 +161,88 @@ class TreeRenderer extends Renderer
|
|||
$attributes->add('class', 'node');
|
||||
}
|
||||
|
||||
$tbody = Html::tag('tbody');
|
||||
$table->add($tbody);
|
||||
$tr = Html::tag('tr');
|
||||
$tbody->add($tr);
|
||||
$div = Html::tag('div');
|
||||
$li->add($div);
|
||||
|
||||
$div->add($node->getLink());
|
||||
$div->add($this->getNodeIcons($node, $path));
|
||||
|
||||
$div->add(Html::tag('span', null, $node->getAlias()));
|
||||
|
||||
if ($node instanceof BpNode) {
|
||||
$tr->add(Html::tag(
|
||||
'th',
|
||||
['rowspan' => $node->countChildren() + 1 + ($this->isLocked() ? 0 : 1)],
|
||||
Html::tag('span', ['class' => 'op'], $node->operatorHtml())
|
||||
));
|
||||
$div->add(Html::tag('span', ['class' => 'op'], $node->operatorHtml()));
|
||||
}
|
||||
$td = Html::tag('td');
|
||||
$tr->add($td);
|
||||
|
||||
if ($node instanceof BpNode && $node->hasInfoUrl()) {
|
||||
$td->add($this->createInfoAction($node));
|
||||
$div->add($this->createInfoAction($node));
|
||||
}
|
||||
|
||||
if (! $this->isLocked()) {
|
||||
$td->add($this->getActionIcons($bp, $node));
|
||||
$differentConfig = $node->getBpConfig()->getName() !== $this->getBusinessProcess()->getName();
|
||||
if (! $this->isLocked() && !$differentConfig) {
|
||||
$div->add($this->getActionIcons($bp, $node));
|
||||
}
|
||||
|
||||
$ul = Html::tag('ul', [
|
||||
'class' => ['bp', 'sortable'],
|
||||
'data-sortable-disabled' => ($this->isLocked() || $differentConfig) ? 'true' : 'false',
|
||||
'data-sortable-invert-swap' => 'true',
|
||||
'data-sortable-data-id-attr' => 'id',
|
||||
'data-sortable-draggable' => '.movable',
|
||||
'data-sortable-direction' => 'vertical',
|
||||
'data-sortable-group' => json_encode([
|
||||
'name' => $htmlId, // Unique, so that the function below is the only deciding factor
|
||||
'put' => 'function:rowPutAllowed'
|
||||
]),
|
||||
'data-csrf-token' => CsrfToken::generate(),
|
||||
'data-action-url' => $this->getUrl()
|
||||
->setParams([
|
||||
'config' => $node->getBpConfig()->getName(),
|
||||
'node' => $node instanceof ImportedNode
|
||||
? $node->getNodeName()
|
||||
: $node->getName()
|
||||
])
|
||||
->getAbsoluteUrl()
|
||||
]);
|
||||
$li->add($ul);
|
||||
|
||||
$path[] = $differentConfig ? $node->getIdentifier() : $node->getName();
|
||||
foreach ($node->getChildren() as $name => $child) {
|
||||
if ($child instanceof BpNode) {
|
||||
$ul->add($this->renderNode($bp, $child, $path));
|
||||
} else {
|
||||
$ul->add($this->renderChild($bp, $child, $path));
|
||||
}
|
||||
}
|
||||
|
||||
return $li;
|
||||
}
|
||||
|
||||
protected function renderChild($bp, Node $node, $path = null)
|
||||
{
|
||||
$li = Html::tag('li', [
|
||||
'class' => 'movable',
|
||||
'id' => $this->getId($node, $path ?: []),
|
||||
'data-node-name' => $node->getName()
|
||||
]);
|
||||
|
||||
$li->add($this->getNodeIcons($node, $path));
|
||||
|
||||
$link = $node->getLink();
|
||||
$link->getAttributes()->set('data-base-target', '_next');
|
||||
$link->add($this->getNodeIcons($node));
|
||||
$li->add($link);
|
||||
|
||||
if ($node->hasChildren()) {
|
||||
$link->add($this->renderStateBadges($node->getStateSummary()));
|
||||
if (! $this->isLocked() && $node->getBpConfig()->getName() === $this->getBusinessProcess()->getName()) {
|
||||
$li->add($this->getActionIcons($bp, $node));
|
||||
}
|
||||
|
||||
if ($time = $node->getLastStateChange()) {
|
||||
$since = $this->timeSince($time)->prepend(
|
||||
sprintf(' (%s ', $node->getStateName())
|
||||
)->add(')');
|
||||
$link->add($since);
|
||||
}
|
||||
|
||||
$td->add($link);
|
||||
|
||||
foreach ($node->getChildren() as $name => $child) {
|
||||
$tbody->add(Html::tag(
|
||||
'tr',
|
||||
null,
|
||||
Html::tag(
|
||||
'td',
|
||||
null,
|
||||
$this->renderNode($bp, $child, $this->getCurrentPath())
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
if (! $this->isLocked() && $node instanceof BpNode && $bp->getMetadata()->canModify()) {
|
||||
$tbody->add(Html::tag(
|
||||
'tr',
|
||||
null,
|
||||
Html::tag(
|
||||
'td',
|
||||
null,
|
||||
$this->renderAddNewNode($node)
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
return $table;
|
||||
return $li;
|
||||
}
|
||||
|
||||
protected function getActionIcons(BpConfig $bp, Node $node)
|
||||
{
|
||||
if ($node instanceof BpNode) {
|
||||
if ($bp->getMetadata()->canModify()) {
|
||||
return $this->createEditAction($bp, $node);
|
||||
return [$this->createEditAction($bp, $node), $this->renderAddNewNode($node)];
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
|
@ -204,7 +254,7 @@ class TreeRenderer extends Renderer
|
|||
protected function createEditAction(BpConfig $bp, BpNode $node)
|
||||
{
|
||||
return $this->actionIcon(
|
||||
'wrench',
|
||||
'edit',
|
||||
$this->getUrl()->with(array(
|
||||
'action' => 'edit',
|
||||
'editnode' => $node->getName()
|
||||
|
|
@ -243,7 +293,7 @@ class TreeRenderer extends Renderer
|
|||
[
|
||||
'href' => $url,
|
||||
'title' => $title,
|
||||
'style' => 'float: right'
|
||||
'class' => 'action-link'
|
||||
],
|
||||
Html::tag('i', ['class' => 'icon icon-' . $icon])
|
||||
);
|
||||
|
|
@ -251,16 +301,12 @@ class TreeRenderer extends Renderer
|
|||
|
||||
protected function renderAddNewNode($parent)
|
||||
{
|
||||
return Html::tag(
|
||||
'a',
|
||||
[
|
||||
'href' => $this->getUrl()
|
||||
->with('action', 'add')
|
||||
->with('node', $parent->getName()),
|
||||
'title' => mt('businessprocess', 'Add a new business process node'),
|
||||
'class' => 'addnew icon-plus'
|
||||
],
|
||||
mt('businessprocess', 'Add')
|
||||
return $this->actionIcon(
|
||||
'plus',
|
||||
$this->getUrl()
|
||||
->with('action', 'add')
|
||||
->with('node', $parent->getName()),
|
||||
mt('businessprocess', 'Add a new business process node')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,12 +12,13 @@ class ServiceNode extends MonitoredNode
|
|||
|
||||
protected $className = 'service';
|
||||
|
||||
public function __construct(BpConfig $bp, $object)
|
||||
protected $icon = 'service';
|
||||
|
||||
public function __construct($object)
|
||||
{
|
||||
$this->name = $object->hostname . ';' . $object->service;
|
||||
$this->hostname = $object->hostname;
|
||||
$this->service = $object->service;
|
||||
$this->bp = $bp;
|
||||
if (isset($object->state)) {
|
||||
$this->setState($object->state);
|
||||
} else {
|
||||
|
|
@ -47,8 +48,8 @@ class ServiceNode extends MonitoredNode
|
|||
'service' => $this->getServiceDescription()
|
||||
);
|
||||
|
||||
if ($this->bp->hasBackendName()) {
|
||||
$params['backend'] = $this->bp->getBackendName();
|
||||
if ($this->getBpConfig()->hasBackendName()) {
|
||||
$params['backend'] = $this->getBpConfig()->getBackendName();
|
||||
}
|
||||
|
||||
return Url::fromPath('businessprocess/service/show', $params);
|
||||
|
|
|
|||
|
|
@ -93,13 +93,16 @@ class MonitoringState
|
|||
|
||||
Benchmark::measure('Retrieved states for ' . count($serviceStatus) . ' services in ' . $config->getName());
|
||||
|
||||
foreach ($serviceStatus as $row) {
|
||||
$this->handleDbRow($row, $config);
|
||||
$configs = $config->listInvolvedConfigs();
|
||||
foreach ($configs as $cfg) {
|
||||
foreach ($serviceStatus as $row) {
|
||||
$this->handleDbRow($row, $cfg);
|
||||
}
|
||||
foreach ($hostStatus as $row) {
|
||||
$this->handleDbRow($row, $cfg);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($hostStatus as $row) {
|
||||
$this->handleDbRow($row, $config);
|
||||
}
|
||||
// TODO: Union, single query?
|
||||
Benchmark::measure('Got states for business process ' . $config->getName());
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ class LegacyConfigParser
|
|||
/** @var BpConfig */
|
||||
protected $config;
|
||||
|
||||
/** @var array */
|
||||
protected $missingNodes = [];
|
||||
|
||||
/**
|
||||
* LegacyConfigParser constructor
|
||||
*
|
||||
|
|
@ -77,6 +80,8 @@ class LegacyConfigParser
|
|||
$parser->parseLine($line);
|
||||
}
|
||||
|
||||
$parser->resolveMissingNodes();
|
||||
|
||||
Benchmark::measure('Business process ' . $name . ' loaded');
|
||||
return $config;
|
||||
}
|
||||
|
|
@ -99,11 +104,28 @@ class LegacyConfigParser
|
|||
$this->parseLine($line);
|
||||
}
|
||||
|
||||
$this->resolveMissingNodes();
|
||||
|
||||
fclose($fh);
|
||||
unset($this->currentLineNumber);
|
||||
unset($this->currentFilename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve previously missed business process nodes
|
||||
*
|
||||
* @throws ConfigurationError In case a referenced process does not exist
|
||||
*/
|
||||
protected function resolveMissingNodes()
|
||||
{
|
||||
foreach ($this->missingNodes as $name => $parents) {
|
||||
foreach ($parents as $parent) {
|
||||
/** @var BpNode $parent */
|
||||
$parent->addChild($this->config->getNode($name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function readMetadataFromFileHeader($name, $filename)
|
||||
{
|
||||
$metadata = new Metadata($name);
|
||||
|
|
@ -298,47 +320,44 @@ class LegacyConfigParser
|
|||
// New feature: $minWarn = $m[2];
|
||||
$value = $m[3];
|
||||
}
|
||||
$cmps = preg_split('~\s*\\' . $op . '\s*~', $value, -1, PREG_SPLIT_NO_EMPTY);
|
||||
$childNames = array();
|
||||
|
||||
$node = new BpNode((object) array(
|
||||
'name' => $name,
|
||||
'operator' => $op_name,
|
||||
'child_names' => []
|
||||
));
|
||||
$node->setBpConfig($bp);
|
||||
|
||||
$cmps = preg_split('~\s*\\' . $op . '\s*~', $value, -1, PREG_SPLIT_NO_EMPTY);
|
||||
foreach ($cmps as $val) {
|
||||
if (strpos($val, ';') !== false) {
|
||||
if ($bp->hasNode($val)) {
|
||||
$childNames[] = $val;
|
||||
continue;
|
||||
}
|
||||
|
||||
list($host, $service) = preg_split('~;~', $val, 2);
|
||||
if ($service === 'Hoststatus') {
|
||||
$bp->createHost($host);
|
||||
$node->addChild($bp->getNode($val));
|
||||
} else {
|
||||
$bp->createService($host, $service);
|
||||
list($host, $service) = preg_split('~;~', $val, 2);
|
||||
if ($service === 'Hoststatus') {
|
||||
$node->addChild($bp->createHost($host));
|
||||
} else {
|
||||
$node->addChild($bp->createService($host, $service));
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($val[0] === '@') {
|
||||
} elseif ($val[0] === '@') {
|
||||
if (strpos($val, ':') === false) {
|
||||
throw new ConfigurationError(
|
||||
"I'm unable to import full external configs, a node needs to be provided for '%s'",
|
||||
$val
|
||||
);
|
||||
// TODO: this might work:
|
||||
// $node = $bp->createImportedNode(substr($val, 1));
|
||||
} else {
|
||||
list($config, $nodeName) = preg_split('~:\s*~', substr($val, 1), 2);
|
||||
$node = $bp->createImportedNode($config, $nodeName);
|
||||
$node->addChild($bp->createImportedNode($config, $nodeName));
|
||||
}
|
||||
$val = $node->getName();
|
||||
} elseif ($bp->hasNode($val)) {
|
||||
$node->addChild($bp->getNode($val));
|
||||
} else {
|
||||
$this->missingNodes[$val][] = $node;
|
||||
}
|
||||
|
||||
$childNames[] = $val;
|
||||
}
|
||||
|
||||
$node = new BpNode($bp, (object) array(
|
||||
'name' => $name,
|
||||
'operator' => $op_name,
|
||||
'child_names' => $childNames
|
||||
));
|
||||
|
||||
$bp->addNode($name, $node);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ namespace Icinga\Module\Businessprocess\Storage;
|
|||
|
||||
use Icinga\Module\Businessprocess\BpNode;
|
||||
use Icinga\Module\Businessprocess\BpConfig;
|
||||
use Icinga\Module\Businessprocess\ImportedNode;
|
||||
|
||||
class LegacyConfigRenderer
|
||||
{
|
||||
|
|
@ -110,6 +111,10 @@ class LegacyConfigRenderer
|
|||
$cfg = '';
|
||||
|
||||
foreach ($node->getChildBpNodes() as $name => $child) {
|
||||
if ($child instanceof ImportedNode) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$cfg .= $this->requireRenderedBpNode($child) . "\n";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,13 @@ use Icinga\Exception\SystemPermissionException;
|
|||
|
||||
class LegacyStorage extends Storage
|
||||
{
|
||||
/**
|
||||
* All parsed configurations
|
||||
*
|
||||
* @var BpConfig[]
|
||||
*/
|
||||
protected $configs = [];
|
||||
|
||||
/** @var string */
|
||||
protected $configDir;
|
||||
|
||||
|
|
@ -116,10 +123,14 @@ class LegacyStorage extends Storage
|
|||
*/
|
||||
public function loadProcess($name)
|
||||
{
|
||||
return LegacyConfigParser::parseFile(
|
||||
$name,
|
||||
$this->getFilename($name)
|
||||
);
|
||||
if (! isset($this->configs[$name])) {
|
||||
$this->configs[$name] = LegacyConfigParser::parseFile(
|
||||
$name,
|
||||
$this->getFilename($name)
|
||||
);
|
||||
}
|
||||
|
||||
return $this->configs[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -146,6 +157,10 @@ class LegacyStorage extends Storage
|
|||
*/
|
||||
public function loadMetadata($name)
|
||||
{
|
||||
if (isset($this->configs[$name])) {
|
||||
return $this->configs[$name]->getMetadata();
|
||||
}
|
||||
|
||||
return LegacyConfigParser::readMetadataFromFileHeader(
|
||||
$name,
|
||||
$this->getFilename($name)
|
||||
|
|
|
|||
|
|
@ -2,12 +2,18 @@
|
|||
|
||||
namespace Icinga\Module\Businessprocess\Storage;
|
||||
|
||||
use Icinga\Application\Config;
|
||||
use Icinga\Data\ConfigObject;
|
||||
use Icinga\Module\Businessprocess\BpConfig;
|
||||
use Icinga\Module\Businessprocess\Metadata;
|
||||
|
||||
abstract class Storage
|
||||
{
|
||||
/**
|
||||
* @var static
|
||||
*/
|
||||
protected static $instance;
|
||||
|
||||
/**
|
||||
* @var ConfigObject
|
||||
*/
|
||||
|
|
@ -27,6 +33,15 @@ abstract class Storage
|
|||
{
|
||||
}
|
||||
|
||||
public static function getInstance()
|
||||
{
|
||||
if (static::$instance === null) {
|
||||
static::$instance = new static(Config::module('businessprocess')->getSection('global'));
|
||||
}
|
||||
|
||||
return static::$instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* All processes readable by the current user
|
||||
*
|
||||
|
|
|
|||
|
|
@ -16,27 +16,34 @@ class RenderedProcessActionBar extends ActionBar
|
|||
$meta = $config->getMetadata();
|
||||
|
||||
if ($renderer instanceof TreeRenderer) {
|
||||
$this->add(Html::tag(
|
||||
$link = Html::tag(
|
||||
'a',
|
||||
[
|
||||
'href' => $url->with('mode', 'tile'),
|
||||
'title' => mt('businessprocess', 'Switch to Tile view'),
|
||||
'class' => 'icon-dashboard'
|
||||
],
|
||||
mt('businessprocess', 'Tiles')
|
||||
));
|
||||
'title' => mt('businessprocess', 'Switch to Tile view')
|
||||
]
|
||||
);
|
||||
} else {
|
||||
$this->add(Html::tag(
|
||||
$link = Html::tag(
|
||||
'a',
|
||||
[
|
||||
'href' => $url->with('mode', 'tree'),
|
||||
'title' => mt('businessprocess', 'Switch to Tree view'),
|
||||
'class' => 'icon-sitemap'
|
||||
],
|
||||
mt('businessprocess', 'Tree')
|
||||
));
|
||||
'title' => mt('businessprocess', 'Switch to Tree view')
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
$link->add([
|
||||
Html::tag('i', ['class' => 'icon icon-dashboard' . ($renderer instanceof TreeRenderer ? '' : ' active')]),
|
||||
Html::tag('i', ['class' => 'icon icon-sitemap' . ($renderer instanceof TreeRenderer ? ' active' : '')])
|
||||
]);
|
||||
|
||||
$this->add(
|
||||
Html::tag('div', ['class' => 'view-toggle'])
|
||||
->add(Html::tag('span', null, mt('businessprocess', 'View')))
|
||||
->add($link)
|
||||
);
|
||||
|
||||
$this->add(Html::tag(
|
||||
'a',
|
||||
[
|
||||
|
|
@ -51,15 +58,28 @@ class RenderedProcessActionBar extends ActionBar
|
|||
$hasChanges = $config->hasSimulations() || $config->hasBeenChanged();
|
||||
|
||||
if ($renderer->isLocked()) {
|
||||
$this->add(Html::tag(
|
||||
'a',
|
||||
[
|
||||
'href' => $url->with('unlocked', true),
|
||||
'title' => mt('businessprocess', 'Click to unlock editing for this process'),
|
||||
'class' => 'icon-lock'
|
||||
],
|
||||
mt('businessprocess', 'Editing locked')
|
||||
));
|
||||
if (! $renderer->wantsRootNodes() && $renderer->rendersImportedNode()) {
|
||||
$span = Html::tag('span', [
|
||||
'class' => 'disabled',
|
||||
'title' => mt(
|
||||
'businessprocess',
|
||||
'Imported processes can only be changed in their original configuration'
|
||||
)
|
||||
]);
|
||||
$span->add(Html::tag('i', ['class' => 'icon icon-lock']))
|
||||
->add(mt('businessprocess', 'Editing Locked'));
|
||||
$this->add($span);
|
||||
} else {
|
||||
$this->add(Html::tag(
|
||||
'a',
|
||||
[
|
||||
'href' => $url->with('unlocked', true),
|
||||
'title' => mt('businessprocess', 'Click to unlock editing for this process'),
|
||||
'class' => 'icon-lock'
|
||||
],
|
||||
mt('businessprocess', 'Unlock Editing')
|
||||
));
|
||||
}
|
||||
} elseif (! $hasChanges) {
|
||||
$this->add(Html::tag(
|
||||
'a',
|
||||
|
|
@ -68,7 +88,7 @@ class RenderedProcessActionBar extends ActionBar
|
|||
'title' => mt('businessprocess', 'Click to lock editing for this process'),
|
||||
'class' => 'icon-lock-open'
|
||||
],
|
||||
mt('businessprocess', 'Editing unlocked')
|
||||
mt('businessprocess', 'Lock Editing')
|
||||
));
|
||||
}
|
||||
|
||||
|
|
@ -91,9 +111,9 @@ class RenderedProcessActionBar extends ActionBar
|
|||
[
|
||||
'href' => $url->with('action', 'add'),
|
||||
'title' => mt('businessprocess', 'Add a new business process node'),
|
||||
'class' => 'icon-plus'
|
||||
'class' => 'icon-plus button-link'
|
||||
],
|
||||
mt('businessprocess', 'Add')
|
||||
mt('businessprocess', 'Add Node')
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
32
library/Businessprocess/Web/Component/StateBall.php
Normal file
32
library/Businessprocess/Web/Component/StateBall.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Icinga\Module\Businessprocess\Web\Component;
|
||||
|
||||
use ipl\Html\BaseHtmlElement;
|
||||
|
||||
class StateBall extends BaseHtmlElement
|
||||
{
|
||||
const SIZE_TINY = 'xs';
|
||||
const SIZE_SMALL = 's';
|
||||
const SIZE_MEDIUM = 'm';
|
||||
const SIZE_LARGE = 'l';
|
||||
|
||||
protected $tag = 'div';
|
||||
|
||||
public function __construct($state = 'none', $size = self::SIZE_SMALL)
|
||||
{
|
||||
$state = \trim($state);
|
||||
|
||||
if (empty($state)) {
|
||||
$state = 'none';
|
||||
}
|
||||
|
||||
$size = \trim($size);
|
||||
|
||||
if (empty($size)) {
|
||||
$size = self::SIZE_MEDIUM;
|
||||
}
|
||||
|
||||
$this->defaultAttributes = ['class' => "state-ball state-$state size-$size"];
|
||||
}
|
||||
}
|
||||
|
|
@ -262,9 +262,7 @@ class Controller extends ModuleController
|
|||
protected function storage()
|
||||
{
|
||||
if ($this->storage === null) {
|
||||
$this->storage = new LegacyStorage(
|
||||
$this->Config()->getSection('global')
|
||||
);
|
||||
$this->storage = LegacyStorage::getInstance();
|
||||
}
|
||||
|
||||
return $this->storage;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ class CsrfToken
|
|||
return false;
|
||||
}
|
||||
|
||||
list($seed, $token) = explode('|', $elementValue);
|
||||
list($seed, $token) = explode('|', $token);
|
||||
|
||||
if (!is_numeric($seed)) {
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -6,13 +6,74 @@ a:focus {
|
|||
}
|
||||
}
|
||||
|
||||
.action-bar a {
|
||||
.action-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 1.3em;
|
||||
color: @icinga-blue;
|
||||
&:hover::before {
|
||||
text-decoration: none;
|
||||
|
||||
> a {
|
||||
&:hover::before {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
&.button-link {
|
||||
color: white;
|
||||
background: @icinga-blue;
|
||||
|
||||
&:active, &:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-left: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> div.view-toggle {
|
||||
margin-right: 1em;
|
||||
|
||||
span {
|
||||
color: @gray;
|
||||
margin-right: .5em;
|
||||
}
|
||||
|
||||
a {
|
||||
display: inline-block;
|
||||
|
||||
i {
|
||||
padding: .25em .5em;
|
||||
border: 1px solid @icinga-blue;
|
||||
|
||||
&:before {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: white;
|
||||
background-color: @icinga-blue;
|
||||
}
|
||||
|
||||
&:first-of-type {
|
||||
border-top-left-radius: .25em;
|
||||
border-bottom-left-radius: .25em;
|
||||
}
|
||||
&:last-of-type {
|
||||
border-top-right-radius: .25em;
|
||||
border-bottom-right-radius: .25em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
span.disabled {
|
||||
color: @gray;
|
||||
}
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
form a {
|
||||
|
|
@ -23,103 +84,158 @@ div.bp {
|
|||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
div.bp.sortable > .sortable-ghost {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.simulation div.bp {
|
||||
border-right: 1em solid @colorCriticalHandled;
|
||||
padding-right: 1em;
|
||||
background: white;
|
||||
}
|
||||
|
||||
table.bp {
|
||||
/* Business process table styling starts here */
|
||||
width: 100%;
|
||||
|
||||
/* TreeView */
|
||||
|
||||
@vertical-tree-item-gap: .5em;
|
||||
|
||||
ul.bp {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: @text-color;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
box-sizing: border-box;
|
||||
font-size: 1em;
|
||||
font-weight: normal;
|
||||
table-layout: fixed;
|
||||
list-style-type: none;
|
||||
|
||||
/* Reset all paddings and margins, just to be on the safe side */
|
||||
th, td {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
.action-link {
|
||||
font-size: 1.3em;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* Left outer margin on nested BPs */
|
||||
table.bp {
|
||||
// cursors!!!1
|
||||
&:not([data-sortable-disabled="true"]) {
|
||||
.movable {
|
||||
cursor: grab;
|
||||
|
||||
width: 99.6%;
|
||||
margin-left: .4%;
|
||||
margin-top: 4px;
|
||||
&.sortable-chosen {
|
||||
cursor: grabbing;
|
||||
}
|
||||
}
|
||||
|
||||
&.progress .movable {
|
||||
cursor: wait;
|
||||
}
|
||||
}
|
||||
&[data-sortable-disabled="true"] {
|
||||
li.process > div {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.time-since {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
// ghost style
|
||||
&.sortable > li.sortable-ghost {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
max-height: 30em;
|
||||
background-color: @gray-lighter;
|
||||
border: .2em dotted @gray-light;
|
||||
border-left-width: 0;
|
||||
border-right-width: 0;
|
||||
mix-blend-mode: hard-light;
|
||||
|
||||
table.bp th {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* END of font settings */
|
||||
|
||||
/* No focus outline on our links, look ugly */
|
||||
table.bp a:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* No link underlining */
|
||||
table.bp a, table.bp a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* White font for all hovered objects */
|
||||
table.bp.hovered {
|
||||
color: white;
|
||||
|
||||
> tbody > tr > td > a > .time-since {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
table.bp.handled.hovered {
|
||||
color: #0a0a0a;
|
||||
}
|
||||
|
||||
table.bp a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
/* Show a pointer when hovering th, highlighting is JS-triggered */
|
||||
table.bp tr th {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Expand / collapse styling */
|
||||
table.bp.process {
|
||||
|
||||
position: relative;
|
||||
|
||||
> tbody > tr:first-child > td:before {
|
||||
content: '\e81d';
|
||||
font-family: ifont;
|
||||
position: absolute;
|
||||
font-size: 1.5em;
|
||||
margin-left: -0.8em;
|
||||
-webkit-transition: -webkit-transform 0.3s;
|
||||
-moz-transition: -moz-transform 0.3s;
|
||||
-o-transition: -o-transform 0.3s;
|
||||
transition: transform 0.3s;
|
||||
&.process:after {
|
||||
// TODO: Only apply if content overflows?
|
||||
content: " ";
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
height: 50%;
|
||||
background: linear-gradient(transparent, white);
|
||||
}
|
||||
}
|
||||
|
||||
&.collapsed {
|
||||
// header style
|
||||
li.process > div {
|
||||
padding: .291666667em 0;
|
||||
border-bottom: 1px solid @gray-light;
|
||||
|
||||
> tbody > tr:first-child > td:before {
|
||||
> a.toggle {
|
||||
min-width: 1.25em; // So that process icons align with their node's icons
|
||||
color: @gray;
|
||||
}
|
||||
|
||||
> span {
|
||||
font-size: 1.25em;
|
||||
|
||||
&.op {
|
||||
padding: .1em .5em;
|
||||
border-radius: .5em;
|
||||
background-color: @gray-light;
|
||||
font-weight: bold;
|
||||
font-size: 1em;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// subprocess style
|
||||
li.process > ul {
|
||||
padding-left: 2em;
|
||||
list-style-type: none;
|
||||
|
||||
&.sortable {
|
||||
min-height: 1em; // Required to be able to move items back to an otherwise empty list
|
||||
}
|
||||
}
|
||||
|
||||
// vertical layout
|
||||
> li {
|
||||
padding: @vertical-tree-item-gap 0;
|
||||
|
||||
&:first-child {
|
||||
margin-top: @vertical-tree-item-gap;
|
||||
}
|
||||
|
||||
&.process {
|
||||
padding-bottom: 0;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// horizontal layout
|
||||
li.process > div,
|
||||
li:not(.process) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding-left: .25em;
|
||||
|
||||
> * {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// collapse handling
|
||||
li.process {
|
||||
// toggle, default
|
||||
> div > a.toggle > i:before {
|
||||
-webkit-transition: -webkit-transform 0.3s;
|
||||
-moz-transition: -moz-transform 0.3s;
|
||||
-o-transition: -o-transform 0.3s;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
// toggle, collapsed
|
||||
&.collapsed > div > a.toggle > i:before {
|
||||
-moz-transform:rotate(-90deg);
|
||||
-ms-transform:rotate(-90deg);
|
||||
-o-transform:rotate(-90deg);
|
||||
|
|
@ -127,214 +243,31 @@ table.bp.process {
|
|||
transform:rotate(-90deg);
|
||||
}
|
||||
|
||||
table.bp, th span {
|
||||
display: none;
|
||||
&.collapsed {
|
||||
margin-bottom: (@vertical-tree-item-gap * 2);
|
||||
|
||||
> ul.bp {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
table.bp th > a, table.bp td > a, table.bp td > span {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
table.bp span.op {
|
||||
width: 1.5em;
|
||||
min-height: 1.5em;
|
||||
margin-top: 1em;
|
||||
display: block;
|
||||
line-height: 2em;
|
||||
-moz-transform: rotate(-90deg);
|
||||
-ms-transform: rotate(-90deg);
|
||||
-o-transform: rotate(-90deg);
|
||||
-webkit-transform: rotate(-90deg);
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
table.bp .icon {
|
||||
float: left;
|
||||
margin-right: 0.4em;
|
||||
}
|
||||
|
||||
table.bp.node {
|
||||
td:before {
|
||||
font-family: ifont;
|
||||
z-index: 1;
|
||||
font-size: 1.25em;
|
||||
position: absolute;
|
||||
margin-left: 1.25em;
|
||||
margin-top: 0.25em;
|
||||
// hover style
|
||||
li.process:hover > div {
|
||||
background-color: #dae4e6;
|
||||
}
|
||||
}
|
||||
|
||||
table.bp.node.subtree td:before {
|
||||
content: '\e80e';
|
||||
}
|
||||
|
||||
table.bp.node.service td:before {
|
||||
content: '\e840';
|
||||
}
|
||||
|
||||
table.bp.node.host td:before {
|
||||
content: '\e866';
|
||||
}
|
||||
|
||||
/* Border defaults */
|
||||
table.bp {
|
||||
border-width: 0;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
table.bp tr, table.bp tbody, table.bp th, table.bp td, table.bp.node td > a, table.node.missing td > span {
|
||||
border-width: 0;
|
||||
border-style: inherit;
|
||||
border-color: inherit;
|
||||
}
|
||||
|
||||
table.bp td > a, table.node.missing td > span {
|
||||
height: 2.5em;
|
||||
line-height: 2.5em;
|
||||
padding-left: 0.5em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
table.bp.node td > a:last-child, table.node.missing td > span {
|
||||
padding-left: 2.5em;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0.5em 0.5em;
|
||||
border-left-width: 0.8em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
table.bp.node.handled td > a:last-child, table.bp.node.ok td > a:last-child,
|
||||
table.node.missing td > span, table.bp.node.up td > a:last-child
|
||||
{
|
||||
border-left-width: 0.3em;
|
||||
background-position: 1em 0.5em;
|
||||
padding-left: 3em;
|
||||
}
|
||||
|
||||
table.bp th {
|
||||
border-left-width: 0.8em;
|
||||
width: 1.8em;
|
||||
}
|
||||
|
||||
table.process.missing th span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
table.bp.handled > tbody > tr > th, table.bp.ok > tbody > tr > th {
|
||||
border-left-width: 0.3em;
|
||||
width: 2em;
|
||||
}
|
||||
|
||||
/* Operator: upper line */
|
||||
table.bp.operator > tbody > tr:first-child > * {
|
||||
border-top-width: 1px;
|
||||
border-top-style: solid;
|
||||
}
|
||||
|
||||
table.bp.operator.hovered > tbody > tr:first-child > * {
|
||||
border-top-style: solid;
|
||||
}
|
||||
|
||||
/* Set colors based on element state */
|
||||
table.bp {
|
||||
&.ok { border-color: @colorOk; }
|
||||
&.up { border-color: @colorOk; }
|
||||
&.warning { border-color: @colorWarning; }
|
||||
&.warning.handled { border-color: @colorWarningHandled; }
|
||||
&.critical { border-color: @colorCritical; }
|
||||
&.critical.handled { border-color: @colorCriticalHandled; }
|
||||
&.down { border-color: @colorCritical; }
|
||||
&.down.handled { border-color: @colorCriticalHandled; }
|
||||
&.unknown { border-color: @colorUnknown; }
|
||||
&.unknown.handled { border-color: @colorUnknownHandled; }
|
||||
&.unreachable { border-color: @colorUnknown; }
|
||||
&.unreachable.handled { border-color: @colorUnknownHandled; }
|
||||
&.pending { border-color: @colorPending; }
|
||||
&.missing { border-color: #ccc; }
|
||||
&.hovered {
|
||||
&.ok > tbody > tr > {
|
||||
th, td > a { background-color: @colorOk; }
|
||||
}
|
||||
&.up > tbody > tr > {
|
||||
th, td > a { background-color: @colorOk; }
|
||||
}
|
||||
&.warning > tbody > tr > {
|
||||
th, td > a { background-color: @colorWarning; }
|
||||
}
|
||||
&.warning.handled > tbody > tr {
|
||||
> th, > td > a { background-color: @colorWarningHandled; }
|
||||
}
|
||||
&.critical > tbody > tr > {
|
||||
th, td > a { background-color: @colorCritical; }
|
||||
}
|
||||
&.critical.handled > tbody > tr > {
|
||||
th, td > a { background-color: @colorCriticalHandled; }
|
||||
}
|
||||
&.down > tbody > tr > {
|
||||
th, td > a { background-color: @colorCritical; }
|
||||
}
|
||||
&.down.handled > tbody > tr > {
|
||||
th, td > a { background-color: @colorCriticalHandled; }
|
||||
}
|
||||
&.unknown > tbody > tr > {
|
||||
th, td > a { background-color: @colorUnknown; }
|
||||
}
|
||||
&.unknown.handled > tbody > tr > {
|
||||
th, td > a { background-color: @colorUnknownHandled; }
|
||||
}
|
||||
&.unreachable > tbody > tr > {
|
||||
th, td > a { background-color: @colorUnknown; }
|
||||
}
|
||||
&.unreachable.handled > tbody > tr > {
|
||||
th, td > a { background-color: @colorUnreachableHandled; }
|
||||
}
|
||||
&.pending > tbody > tr > {
|
||||
th, td > a { background-color: @colorPending; }
|
||||
}
|
||||
&.missing > tbody > tr > {
|
||||
th, td > a, td > span { background-color: #ccc; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Reduce font size after the 3rd level... */
|
||||
table.bp table.bp table.bp table.bp {
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
/* ...and keep it constant afterwards */
|
||||
table.bp table.bp table.bp table.bp table.bp {
|
||||
font-size: 1em;
|
||||
}
|
||||
/* Transitions */
|
||||
div.knightrider table.bp {
|
||||
|
||||
// That's ugly, I know
|
||||
.transition(@val1, @val2) {
|
||||
transition: @val1, @val2;
|
||||
-moz-transition: @val1, @val2;
|
||||
-o-transition: @val1, @val2;
|
||||
-webkit-transition: @val1, @val2;
|
||||
}
|
||||
> tbody > tr > td > a:last-child, > tbody > tr > td > span, > tbody > tr > th {
|
||||
// Fade out
|
||||
.transition(color 0.5s 0.1s step-start,
|
||||
background-color 0.5s 0.1s ease-out
|
||||
);
|
||||
li:not(.process):hover {
|
||||
background-color: #dae4e6;
|
||||
}
|
||||
|
||||
&.hovered > tbody > tr {
|
||||
> td > a:last-child, > td > span, > th {
|
||||
// Fade in
|
||||
.transition(color 0.0s 0.0s step-start,
|
||||
background-color 0.0s 0.0s ease-out
|
||||
);
|
||||
li.process > div > .state-ball,
|
||||
li:not(.process) > .state-ball {
|
||||
border: .15em solid white;
|
||||
|
||||
&.size-s {
|
||||
width: 7em/6em;
|
||||
height: 7em/6em;
|
||||
line-height: 7em/6em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -458,6 +391,11 @@ td > a > .badges {
|
|||
clear: both;
|
||||
}
|
||||
|
||||
.tiles.sortable > .sortable-ghost {
|
||||
opacity: 0.5;
|
||||
border: .2em dashed black;
|
||||
}
|
||||
|
||||
.tiles > div {
|
||||
color: white;
|
||||
width: 12em;
|
||||
|
|
@ -627,38 +565,6 @@ td > a > .badges {
|
|||
list-style: none;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
|
||||
.badges {
|
||||
background-color: transparent;
|
||||
border-radius: 0;
|
||||
display: inline-block;
|
||||
padding: 0 0 0 0.5em;
|
||||
.badge {
|
||||
line-height: 1.25em;
|
||||
font-size: 0.8em;
|
||||
border: 1px solid white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.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 {
|
||||
|
|
@ -676,22 +582,50 @@ td > a > .badges {
|
|||
|
||||
}
|
||||
.breadcrumb li a {
|
||||
color: white;
|
||||
color: @icinga-blue;
|
||||
margin: 0;
|
||||
font-size: 1.2em;
|
||||
text-decoration: none;
|
||||
padding-left: 2em;
|
||||
line-height: 2.5em;
|
||||
background: @icinga-blue;
|
||||
position: relative;
|
||||
display: block;
|
||||
float: left;
|
||||
&:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
> .state-ball {
|
||||
margin-right: .5em;
|
||||
border: .15em solid white;
|
||||
|
||||
&.size-s {
|
||||
width: 7em/6em;
|
||||
height: 7em/6em;
|
||||
line-height: 7em/6em;
|
||||
}
|
||||
}
|
||||
}
|
||||
.breadcrumb li {
|
||||
border: 1px solid @gray-lighter;
|
||||
|
||||
&:first-of-type {
|
||||
border-radius: .25em;
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
border-radius: .25em;
|
||||
border: 1px solid @icinga-blue;
|
||||
background: @icinga-blue;
|
||||
padding-right: 1.2em;
|
||||
|
||||
a {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.breadcrumb li a:before, .breadcrumb li a:after {
|
||||
.breadcrumb li:not(:last-of-type) a:before, .breadcrumb li:not(:last-of-type) a:after {
|
||||
content: " ";
|
||||
display: block;
|
||||
width: 0;
|
||||
|
|
@ -704,30 +638,29 @@ td > a > .badges {
|
|||
left: 100%;
|
||||
}
|
||||
|
||||
.breadcrumb li a:before {
|
||||
border-left: 1.2em solid white;
|
||||
.breadcrumb li:not(:last-of-type) a:before {
|
||||
border-left: 1.2em solid @gray-lighter;
|
||||
margin-left: 1px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.breadcrumb li a:after {
|
||||
border-left: 1.2em solid @icinga-blue;
|
||||
.breadcrumb li:not(:last-of-type) a:after {
|
||||
border-left: 1.2em solid white;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.tabs > .dropdown-nav-item > ul {
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.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:not(:last-child) a:hover { background: @icinga-blue; color: white; }
|
||||
.breadcrumb li:not(:last-child) a:hover:after { border-left-color: @icinga-blue !important; }
|
||||
.breadcrumb li:last-child:hover, .breadcrumb li:last-child a:hover { background: @icinga-blue; border-color: @icinga-blue !important; }
|
||||
|
||||
.breadcrumb li a:focus {
|
||||
text-decoration: underline;
|
||||
|
|
|
|||
58
public/css/state-ball.less
Normal file
58
public/css/state-ball.less
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
.state-ball {
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
|
||||
&.state-critical,
|
||||
&.state-down {
|
||||
background-color: @color-critical;
|
||||
}
|
||||
|
||||
&.state-unknown {
|
||||
background-color: @color-unknown;
|
||||
}
|
||||
|
||||
&.state-warning {
|
||||
background-color: @color-warning;
|
||||
}
|
||||
|
||||
&.state-ok,
|
||||
&.state-up {
|
||||
background-color: @color-ok;
|
||||
}
|
||||
|
||||
&.state-pending {
|
||||
background-color: @color-pending;
|
||||
}
|
||||
|
||||
&.size-xs {
|
||||
line-height: 0.75em;
|
||||
height: 0.75em;
|
||||
width: 0.75em;
|
||||
}
|
||||
|
||||
&.size-s {
|
||||
line-height: 1em;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
}
|
||||
|
||||
&.size-m {
|
||||
line-height: 2em;
|
||||
height: 2em;
|
||||
width: 2em;
|
||||
}
|
||||
|
||||
&.size-l {
|
||||
line-height: 2.5em;
|
||||
height: 2.5em;
|
||||
width: 2.5em;
|
||||
}
|
||||
|
||||
> i {
|
||||
color: white;
|
||||
font-style: normal;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
}
|
||||
47
public/js/behavior/sortable.js
Normal file
47
public/js/behavior/sortable.js
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*! Icinga Web 2 | (c) 2018 Icinga Development Team | GPLv2+ */
|
||||
|
||||
(function(Icinga, $) {
|
||||
|
||||
'use strict';
|
||||
|
||||
Icinga.Behaviors = Icinga.Behaviors || {};
|
||||
|
||||
var Sortable = function (icinga) {
|
||||
Icinga.EventListener.call(this, icinga);
|
||||
this.on('rendered', this.onRendered, this);
|
||||
};
|
||||
|
||||
Sortable.prototype = new Icinga.EventListener();
|
||||
|
||||
Sortable.prototype.onRendered = function(e) {
|
||||
$(e.target).find('.sortable').each(function() {
|
||||
var $el = $(this);
|
||||
var options = {
|
||||
scroll: $el.closest('.container')[0],
|
||||
onMove: function (/**Event*/ event, /**Event*/ originalEvent) {
|
||||
if (typeof this.options['filter'] !== 'undefined' && $(event.related).is(this.options['filter'])) {
|
||||
// Assumes the filtered item is either at the very start or end of the list and prevents the
|
||||
// user from dropping other items before (if at the very start) or after it.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$.each($el.data(), function (i, v) {
|
||||
if (i.length > 8 && i.startsWith('sortable')) {
|
||||
options[i.charAt(8).toLowerCase() + i.substr(9)] = v;
|
||||
}
|
||||
});
|
||||
|
||||
if (typeof options.group !== 'undefined' && typeof options.group.put === 'string' && options.group.put.startsWith('function:')) {
|
||||
var module = icinga.module($el.closest('.icinga-module').data('icingaModule'));
|
||||
options.group.put = module.object[options.group.put.substr(9)];
|
||||
}
|
||||
|
||||
$(this).sortable(options);
|
||||
});
|
||||
};
|
||||
|
||||
Icinga.Behaviors.Sortable = Sortable;
|
||||
|
||||
})(Icinga, jQuery);
|
||||
|
|
@ -21,20 +21,20 @@
|
|||
/**
|
||||
* Tell Icinga about our event handlers
|
||||
*/
|
||||
this.module.on('beforerender', this.rememberOpenedBps);
|
||||
this.module.on('rendered', this.onRendered);
|
||||
this.module.on('rendered', this.onRendered);
|
||||
|
||||
this.module.on('click', 'table.bp.process > tbody > tr:first-child > td > a:last-child', this.processTitleClick);
|
||||
this.module.on('click', 'table.bp > tbody > tr:first-child > th', this.processOperatorClick);
|
||||
this.module.on('focus', 'form input, form textarea, form select', this.formElementFocus);
|
||||
|
||||
this.module.on('mouseenter', 'table.bp > tbody > tr > td > a', this.procMouseOver);
|
||||
this.module.on('mouseenter', 'table.bp > tbody > tr > th', this.procMouseOver);
|
||||
this.module.on('mouseenter', 'table.node.missing > tbody > tr > td > span', this.procMouseOver);
|
||||
this.module.on('mouseleave', 'div.bp', this.procMouseOut);
|
||||
this.module.on('click', 'li.process a.toggle', this.processToggleClick);
|
||||
this.module.on('click', 'li.process > div', this.processHeaderClick);
|
||||
this.module.on('end', 'ul.sortable', this.rowDropped);
|
||||
|
||||
this.module.on('click', 'div.tiles > div', this.tileClick);
|
||||
this.module.on('click', '.dashboard-tile', this.dashboardTileClick);
|
||||
this.module.on('end', 'div.tiles.sortable', this.tileDropped);
|
||||
|
||||
this.module.on('choose', '.sortable', this.suspendAutoRefresh);
|
||||
this.module.on('unchoose', '.sortable', this.resumeAutoRefresh);
|
||||
|
||||
this.module.icinga.logger.debug('BP module initialized');
|
||||
},
|
||||
|
|
@ -42,39 +42,40 @@
|
|||
onRendered: function (event) {
|
||||
var $container = $(event.currentTarget);
|
||||
this.fixFullscreen($container);
|
||||
this.fixOpenedBps($container);
|
||||
this.restoreCollapsedBps($container);
|
||||
this.highlightFormErrors($container);
|
||||
this.hideInactiveFormDescriptions($container);
|
||||
this.fixTileLinksOnDashboard($container);
|
||||
},
|
||||
|
||||
processTitleClick: function (event) {
|
||||
processToggleClick: function (event) {
|
||||
event.stopPropagation();
|
||||
var $el = $(event.currentTarget).closest('table.bp');
|
||||
$el.toggleClass('collapsed');
|
||||
},
|
||||
|
||||
processOperatorClick: function (event) {
|
||||
event.stopPropagation();
|
||||
var $el = $(event.currentTarget).closest('table.bp');
|
||||
var $li = $(event.currentTarget).closest('li.process');
|
||||
$li.toggleClass('collapsed');
|
||||
|
||||
// Click on arrow
|
||||
$el.removeClass('collapsed');
|
||||
|
||||
var children = $el.find('> tbody > tr > td > table.bp.process');
|
||||
if (children.length === 0) {
|
||||
$el.toggleClass('collapsed');
|
||||
var $bpUl = $(event.currentTarget).closest('.content > ul.bp');
|
||||
if (! $bpUl.length || !$bpUl.data('isRootConfig')) {
|
||||
return;
|
||||
}
|
||||
if (children.filter('.collapsed').length) {
|
||||
children.removeClass('collapsed');
|
||||
} else {
|
||||
children.each(function(idx, el) {
|
||||
var $el = $(el);
|
||||
$el.addClass('collapsed');
|
||||
$el.find('table.bp.process').addClass('collapsed');
|
||||
});
|
||||
|
||||
var bpName = $bpUl.attr('id');
|
||||
if (typeof this.idCache[bpName] === 'undefined') {
|
||||
this.idCache[bpName] = [];
|
||||
}
|
||||
|
||||
var index = this.idCache[bpName].indexOf($li.attr('id'));
|
||||
if ($li.is('.collapsed')) {
|
||||
if (index === -1) {
|
||||
this.idCache[bpName].push($li.attr('id'));
|
||||
}
|
||||
} else if (index !== -1) {
|
||||
this.idCache[bpName].splice(index, 1);
|
||||
}
|
||||
},
|
||||
|
||||
processHeaderClick: function (event) {
|
||||
this.processToggleClick(event);
|
||||
},
|
||||
|
||||
hideInactiveFormDescriptions: function($container) {
|
||||
|
|
@ -89,73 +90,110 @@
|
|||
$(event.currentTarget).find('> .bp-link > a').first().trigger('click');
|
||||
},
|
||||
|
||||
/**
|
||||
* Add 'hovered' class to hovered title elements
|
||||
*
|
||||
* TODO: Skip on tablets
|
||||
*/
|
||||
procMouseOver: function (event) {
|
||||
suspendAutoRefresh: function(event) {
|
||||
// TODO: If there is a better approach some time, let me know
|
||||
$(event.originalEvent.from).closest('.container').data('lastUpdate', (new Date()).getTime() + 3600 * 1000);
|
||||
event.stopPropagation();
|
||||
var $hovered = $(event.currentTarget);
|
||||
var $el = $hovered.closest('table.bp');
|
||||
},
|
||||
|
||||
if ($el.is('.operator')) {
|
||||
if (!$hovered.closest('tr').is('tr:first-child')) {
|
||||
// Skip hovered space between cols
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// return;
|
||||
resumeAutoRefresh: function(event) {
|
||||
var $container = $(event.originalEvent.from).closest('.container');
|
||||
$container.data('lastUpdate', (new Date()).getTime() - ($container.data('icingaRefresh') || 10) * 1000);
|
||||
event.stopPropagation();
|
||||
},
|
||||
|
||||
tileDropped: function(event) {
|
||||
var evt = event.originalEvent;
|
||||
if (evt.oldIndex !== evt.newIndex) {
|
||||
var $source = $(evt.from);
|
||||
var actionUrl = [
|
||||
$source.data('actionUrl'),
|
||||
'action=move',
|
||||
'movenode=' + $(evt.item).data('nodeName')
|
||||
].join('&');
|
||||
|
||||
var data = {
|
||||
csrfToken: $source.data('csrfToken'),
|
||||
movenode: 'movenode', // That's the submit button..
|
||||
parent: $(evt.to).data('nodeName') || '',
|
||||
from: evt.oldIndex,
|
||||
to: evt.newIndex
|
||||
};
|
||||
|
||||
var $container = $source.closest('.container');
|
||||
var req = icinga.loader.loadUrl(actionUrl, $container, data, 'POST');
|
||||
req.complete(function (req, textStatus) {
|
||||
icinga.loader.loadUrl(
|
||||
$container.data('icingaUrl'), $container, undefined, undefined, undefined, true);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
$('table.bp.hovered').not($el.parents('table.bp')).removeClass('hovered'); // not self & parents
|
||||
$el.addClass('hovered');
|
||||
$el.parents('table.bp').addClass('hovered');
|
||||
rowDropped: function(event) {
|
||||
var evt = event.originalEvent,
|
||||
$source = $(evt.from),
|
||||
$target = $(evt.to);
|
||||
|
||||
if (evt.oldIndex !== evt.newIndex || !$target.is($source)) {
|
||||
var $root = $target.closest('.content > ul.bp');
|
||||
$root.addClass('progress')
|
||||
.find('ul.bp')
|
||||
.add($root)
|
||||
.each(function() {
|
||||
$(this).data('sortable').option('disabled', true);
|
||||
});
|
||||
|
||||
var data = {
|
||||
csrfToken: $target.data('csrfToken'),
|
||||
movenode: 'movenode', // That's the submit button..
|
||||
parent: $target.closest('.process').data('nodeName') || '',
|
||||
from: evt.oldIndex,
|
||||
to: evt.newIndex
|
||||
};
|
||||
|
||||
var actionUrl = [
|
||||
$source.data('actionUrl'),
|
||||
'action=move',
|
||||
'movenode=' + $(evt.item).data('nodeName')
|
||||
].join('&');
|
||||
|
||||
var $container = $target.closest('.container');
|
||||
var req = icinga.loader.loadUrl(actionUrl, $container, data, 'POST');
|
||||
req.complete(function (req, textStatus) {
|
||||
icinga.loader.loadUrl(
|
||||
$container.data('icingaUrl'), $container, undefined, undefined, undefined, true);
|
||||
});
|
||||
event.stopPropagation();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove 'hovered' class from hovered title elements
|
||||
* Called by Sortable.js while in Tree-View
|
||||
*
|
||||
* TODO: Skip on tablets
|
||||
*/
|
||||
procMouseOut: function (event) {
|
||||
$('table.bp.hovered').removeClass('hovered');
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle clicks on operator or title element
|
||||
* See group option on the sortable elements.
|
||||
*
|
||||
* Title shows subelement, operator unfolds all subelements
|
||||
* @param to
|
||||
* @param from
|
||||
* @param item
|
||||
* @param event
|
||||
* @returns boolean
|
||||
*/
|
||||
titleClicked: function (event) {
|
||||
var self = this;
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
var $el = $(event.currentTarget),
|
||||
affected = []
|
||||
$container = $el.closest('.container');
|
||||
if ($el.hasClass('operator')) {
|
||||
$affected = $el.closest('table').children('tbody')
|
||||
.children('tr.children').children('td').children('table');
|
||||
|
||||
// Only if there are child BPs
|
||||
if ($affected.find('th.operator').length < 1) {
|
||||
$affected = $el.closest('table');
|
||||
}
|
||||
} else {
|
||||
$affected = $el.closest('table');
|
||||
rowPutAllowed: function(to, from, item, event) {
|
||||
if (to.options.group.name === 'root') {
|
||||
return $(item).is('.process');
|
||||
}
|
||||
$affected.each(function (key, el) {
|
||||
var $bptable = $(el).closest('table');
|
||||
$bptable.toggleClass('collapsed');
|
||||
if ($bptable.hasClass('collapsed')) {
|
||||
$bptable.find('table').addClass('collapsed');
|
||||
}
|
||||
|
||||
// Otherwise we're facing a nesting error next
|
||||
var $item = $(item),
|
||||
childrenNames = $item.find('.process').map(function () {
|
||||
return $(this).data('nodeName');
|
||||
}).get();
|
||||
childrenNames.push($item.data('nodeName'));
|
||||
var loopDetected = $(to.el).parents('.process').toArray().some(function (parent) {
|
||||
return childrenNames.indexOf($(parent).data('nodeName')) !== -1;
|
||||
});
|
||||
|
||||
/*$container.data('refreshParams', {
|
||||
opened: self.listOpenedBps($container)
|
||||
});*/
|
||||
return !loopDetected;
|
||||
},
|
||||
|
||||
fixTileLinksOnDashboard: function($container) {
|
||||
|
|
@ -185,48 +223,23 @@
|
|||
}
|
||||
},
|
||||
|
||||
fixOpenedBps: function($container) {
|
||||
var $bpDiv = $container.find('div.bp');
|
||||
var bpName = $bpDiv.attr('id');
|
||||
|
||||
if (typeof this.idCache[bpName] === 'undefined') {
|
||||
return;
|
||||
}
|
||||
var $procs = $bpDiv.find('table.process');
|
||||
|
||||
$.each(this.idCache[bpName], function(idx, id) {
|
||||
var $el = $('#' + id);
|
||||
$procs = $procs.not($el);
|
||||
|
||||
$el.parents('table.process').each(function (idx, el) {
|
||||
$procs = $procs.not($(el));
|
||||
});
|
||||
});
|
||||
|
||||
$procs.addClass('collapsed');
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a list of all currently opened BPs.
|
||||
*
|
||||
* Only get the deepest nodes to keep requests as small as possible
|
||||
*/
|
||||
rememberOpenedBps: function (event) {
|
||||
var ids = [];
|
||||
var $bpDiv = $(event.currentTarget).find('div.bp');
|
||||
var $bpName = $bpDiv.attr('id');
|
||||
|
||||
$bpDiv.find('table.process')
|
||||
.not('table.process.collapsed')
|
||||
.not('table.process.collapsed table.process')
|
||||
.each(function (key, el) {
|
||||
ids.push($(el).attr('id'));
|
||||
});
|
||||
if (ids.length === 0) {
|
||||
restoreCollapsedBps: function($container) {
|
||||
var $bpUl = $container.find('.content > ul.bp');
|
||||
if (! $bpUl.length || !$bpUl.data('isRootConfig')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.idCache[$bpName] = ids;
|
||||
var bpName = $bpUl.attr('id');
|
||||
if (typeof this.idCache[bpName] === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
var _this = this;
|
||||
$bpUl.find('li.process')
|
||||
.filter(function () {
|
||||
return _this.idCache[bpName].indexOf(this.id) !== -1;
|
||||
})
|
||||
.addClass('collapsed');
|
||||
},
|
||||
|
||||
/** BEGIN Form handling, borrowed from Director **/
|
||||
|
|
|
|||
2349
public/js/vendor/Sortable.js
vendored
Normal file
2349
public/js/vendor/Sortable.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
76
public/js/vendor/jquery.fn.sortable.js
vendored
Normal file
76
public/js/vendor/jquery.fn.sortable.js
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
(function (factory) {
|
||||
"use strict";
|
||||
var sortable,
|
||||
jq,
|
||||
_this = this
|
||||
;
|
||||
|
||||
if (typeof define === "function" && define.amd) {
|
||||
try {
|
||||
define(["sortablejs", "jquery"], function(Sortable, $) {
|
||||
sortable = Sortable;
|
||||
jq = $;
|
||||
checkErrors();
|
||||
factory(Sortable, $);
|
||||
});
|
||||
} catch(err) {
|
||||
checkErrors();
|
||||
}
|
||||
return;
|
||||
} else if (typeof exports === 'object') {
|
||||
try {
|
||||
sortable = require('sortablejs');
|
||||
jq = require('jquery');
|
||||
} catch(err) { }
|
||||
}
|
||||
|
||||
if (typeof jQuery === 'function' || typeof $ === 'function') {
|
||||
jq = jQuery || $;
|
||||
}
|
||||
|
||||
if (typeof Sortable !== 'undefined') {
|
||||
sortable = Sortable;
|
||||
}
|
||||
|
||||
function checkErrors() {
|
||||
if (!jq) {
|
||||
throw new Error('jQuery is required for jquery-sortablejs');
|
||||
}
|
||||
|
||||
if (!sortable) {
|
||||
throw new Error('SortableJS is required for jquery-sortablejs (https://github.com/SortableJS/Sortable)');
|
||||
}
|
||||
}
|
||||
checkErrors();
|
||||
factory(sortable, jq);
|
||||
})(function (Sortable, $) {
|
||||
"use strict";
|
||||
|
||||
$.fn.sortable = function (options) {
|
||||
var retVal,
|
||||
args = arguments;
|
||||
|
||||
this.each(function () {
|
||||
var $el = $(this),
|
||||
sortable = $el.data('sortable');
|
||||
|
||||
if (!sortable && (options instanceof Object || !options)) {
|
||||
sortable = new Sortable(this, options);
|
||||
$el.data('sortable', sortable);
|
||||
} else if (sortable) {
|
||||
if (options === 'destroy') {
|
||||
sortable.destroy();
|
||||
$el.removeData('sortable');
|
||||
} else if (options === 'widget') {
|
||||
retVal = sortable;
|
||||
} else if (typeof sortable[options] === 'function') {
|
||||
retVal = sortable[options].apply(sortable, [].slice.call(args, 1));
|
||||
} else if (options in sortable.options) {
|
||||
retVal = sortable.option.apply(sortable, args);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return (retVal === void 0) ? this : retVal;
|
||||
};
|
||||
});
|
||||
|
|
@ -20,7 +20,7 @@ class HostNodeTest extends BaseTestCase
|
|||
{
|
||||
$this->assertEquals(
|
||||
'localhost;Hoststatus',
|
||||
(string) $this->localhost()
|
||||
$this->localhost()->getName()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -57,9 +57,9 @@ class HostNodeTest extends BaseTestCase
|
|||
protected function localhost()
|
||||
{
|
||||
$bp = new BpConfig();
|
||||
return new HostNode($bp, (object) array(
|
||||
return (new HostNode((object) array(
|
||||
'hostname' => 'localhost',
|
||||
'state' => 0,
|
||||
));
|
||||
)))->setBpConfig($bp);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ class AndOperatorTest extends BaseTestCase
|
|||
{
|
||||
$storage = new LegacyStorage($this->emptyConfigSection());
|
||||
$expressions = array(
|
||||
'a = b',
|
||||
'a = b & c & d',
|
||||
'a = b;c',
|
||||
'a = b;c & c;d & d;e',
|
||||
);
|
||||
|
||||
foreach ($expressions as $expression) {
|
||||
|
|
@ -27,9 +27,9 @@ class AndOperatorTest extends BaseTestCase
|
|||
public function testThreeTimesCriticalIsCritical()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 2);
|
||||
$bp->setNodeState('c', 2);
|
||||
$bp->setNodeState('d', 2);
|
||||
$bp->setNodeState('b;c', 2);
|
||||
$bp->setNodeState('c;d', 2);
|
||||
$bp->setNodeState('d;e', 2);
|
||||
|
||||
$this->assertEquals(
|
||||
'CRITICAL',
|
||||
|
|
@ -40,9 +40,9 @@ class AndOperatorTest extends BaseTestCase
|
|||
public function testTwoTimesCriticalAndOkIsCritical()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 2);
|
||||
$bp->setNodeState('c', 0);
|
||||
$bp->setNodeState('d', 2);
|
||||
$bp->setNodeState('b;c', 2);
|
||||
$bp->setNodeState('c;d', 0);
|
||||
$bp->setNodeState('d;e', 2);
|
||||
|
||||
$this->assertEquals(
|
||||
'CRITICAL',
|
||||
|
|
@ -53,9 +53,9 @@ class AndOperatorTest extends BaseTestCase
|
|||
public function testCriticalAndWarningAndOkIsCritical()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 2);
|
||||
$bp->setNodeState('c', 1);
|
||||
$bp->setNodeState('d', 0);
|
||||
$bp->setNodeState('b;c', 2);
|
||||
$bp->setNodeState('c;d', 1);
|
||||
$bp->setNodeState('d;e', 0);
|
||||
|
||||
$this->assertEquals(
|
||||
'CRITICAL',
|
||||
|
|
@ -66,9 +66,9 @@ class AndOperatorTest extends BaseTestCase
|
|||
public function testUnknownAndWarningAndOkIsUnknown()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 0);
|
||||
$bp->setNodeState('c', 1);
|
||||
$bp->setNodeState('d', 3);
|
||||
$bp->setNodeState('b;c', 0);
|
||||
$bp->setNodeState('c;d', 1);
|
||||
$bp->setNodeState('d;e', 3);
|
||||
|
||||
$this->assertEquals(
|
||||
'UNKNOWN',
|
||||
|
|
@ -79,9 +79,9 @@ class AndOperatorTest extends BaseTestCase
|
|||
public function testTwoTimesWarningAndOkIsWarning()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 0);
|
||||
$bp->setNodeState('c', 1);
|
||||
$bp->setNodeState('d', 1);
|
||||
$bp->setNodeState('b;c', 0);
|
||||
$bp->setNodeState('c;d', 1);
|
||||
$bp->setNodeState('d;e', 1);
|
||||
|
||||
$this->assertEquals(
|
||||
'WARNING',
|
||||
|
|
@ -92,9 +92,9 @@ class AndOperatorTest extends BaseTestCase
|
|||
public function testThreeTimesOkIsOk()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 0);
|
||||
$bp->setNodeState('c', 0);
|
||||
$bp->setNodeState('d', 0);
|
||||
$bp->setNodeState('b;c', 0);
|
||||
$bp->setNodeState('c;d', 0);
|
||||
$bp->setNodeState('d;e', 0);
|
||||
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
|
|
@ -203,7 +203,7 @@ class AndOperatorTest extends BaseTestCase
|
|||
protected function getBp()
|
||||
{
|
||||
$storage = new LegacyStorage($this->emptyConfigSection());
|
||||
$expression = 'a = b & c & d';
|
||||
$expression = 'a = b;c & c;d & d;e';
|
||||
$bp = $storage->loadFromString('dummy', $expression);
|
||||
$bp->createBp('b');
|
||||
$bp->createBp('c');
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ class MinOperatorTest extends BaseTestCase
|
|||
{
|
||||
$storage = new LegacyStorage($this->emptyConfigSection());
|
||||
$expressions = array(
|
||||
'a = 1 of: b',
|
||||
'a = 2 of: b + c + d',
|
||||
'a = 1 of: b;c',
|
||||
'a = 2 of: b;c + c;d + d;e',
|
||||
);
|
||||
$this->getName();
|
||||
foreach ($expressions as $expression) {
|
||||
|
|
@ -26,9 +26,9 @@ class MinOperatorTest extends BaseTestCase
|
|||
public function testTwoOfThreeTimesCriticalAreAtLeastCritical()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 2);
|
||||
$bp->setNodeState('c', 2);
|
||||
$bp->setNodeState('d', 2);
|
||||
$bp->setNodeState('b;c', 2);
|
||||
$bp->setNodeState('c;d', 2);
|
||||
$bp->setNodeState('d;e', 2);
|
||||
|
||||
$this->assertEquals(
|
||||
'CRITICAL',
|
||||
|
|
@ -39,9 +39,9 @@ class MinOperatorTest extends BaseTestCase
|
|||
public function testTwoOfTwoTimesCriticalAndUnknownAreAtLeastCritical()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 2);
|
||||
$bp->setNodeState('c', 3);
|
||||
$bp->setNodeState('d', 2);
|
||||
$bp->setNodeState('b;c', 2);
|
||||
$bp->setNodeState('c;d', 3);
|
||||
$bp->setNodeState('d;e', 2);
|
||||
|
||||
$this->assertEquals(
|
||||
'CRITICAL',
|
||||
|
|
@ -52,9 +52,9 @@ class MinOperatorTest extends BaseTestCase
|
|||
public function testTwoOfCriticalAndWarningAndOkAreAtLeastCritical()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 2);
|
||||
$bp->setNodeState('c', 1);
|
||||
$bp->setNodeState('d', 0);
|
||||
$bp->setNodeState('b;c', 2);
|
||||
$bp->setNodeState('c;d', 1);
|
||||
$bp->setNodeState('d;e', 0);
|
||||
|
||||
$this->assertEquals(
|
||||
'CRITICAL',
|
||||
|
|
@ -65,9 +65,9 @@ class MinOperatorTest extends BaseTestCase
|
|||
public function testTwoOfUnknownAndWarningAndCriticalAreAtLeastCritical()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 2);
|
||||
$bp->setNodeState('c', 1);
|
||||
$bp->setNodeState('d', 3);
|
||||
$bp->setNodeState('b;c', 2);
|
||||
$bp->setNodeState('c;d', 1);
|
||||
$bp->setNodeState('d;e', 3);
|
||||
|
||||
$this->assertEquals(
|
||||
'CRITICAL',
|
||||
|
|
@ -78,9 +78,9 @@ class MinOperatorTest extends BaseTestCase
|
|||
public function testTwoOfTwoTimesWarningAndUnknownAreAtLeastUnknown()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 3);
|
||||
$bp->setNodeState('c', 1);
|
||||
$bp->setNodeState('d', 1);
|
||||
$bp->setNodeState('b;c', 3);
|
||||
$bp->setNodeState('c;d', 1);
|
||||
$bp->setNodeState('d;e', 1);
|
||||
|
||||
$this->assertEquals(
|
||||
'UNKNOWN',
|
||||
|
|
@ -91,9 +91,9 @@ class MinOperatorTest extends BaseTestCase
|
|||
public function testTwoOfThreeTimesOkAreAtLeastOk()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 0);
|
||||
$bp->setNodeState('c', 0);
|
||||
$bp->setNodeState('d', 0);
|
||||
$bp->setNodeState('b;c', 0);
|
||||
$bp->setNodeState('c;d', 0);
|
||||
$bp->setNodeState('d;e', 0);
|
||||
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
|
|
@ -114,8 +114,8 @@ class MinOperatorTest extends BaseTestCase
|
|||
public function testTenWithOnlyTwoCritical()
|
||||
{
|
||||
$bp = $this->getBp(10, 8, 0);
|
||||
$bp->setNodeState('b', 2);
|
||||
$bp->setNodeState('c', 2);
|
||||
$bp->setNodeState('b;c', 2);
|
||||
$bp->setNodeState('c;d', 2);
|
||||
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
|
|
@ -126,9 +126,9 @@ class MinOperatorTest extends BaseTestCase
|
|||
public function testTenWithThreeCritical()
|
||||
{
|
||||
$bp = $this->getBp(10, 8, 0);
|
||||
$bp->setNodeState('b', 2);
|
||||
$bp->setNodeState('c', 2);
|
||||
$bp->setNodeState('d', 2);
|
||||
$bp->setNodeState('b;c', 2);
|
||||
$bp->setNodeState('c;d', 2);
|
||||
$bp->setNodeState('d;e', 2);
|
||||
|
||||
$this->assertEquals(
|
||||
'CRITICAL',
|
||||
|
|
@ -139,9 +139,9 @@ class MinOperatorTest extends BaseTestCase
|
|||
public function testTenWithThreeWarning()
|
||||
{
|
||||
$bp = $this->getBp(10, 8, 0);
|
||||
$bp->setNodeState('b', 1);
|
||||
$bp->setNodeState('c', 1);
|
||||
$bp->setNodeState('d', 1);
|
||||
$bp->setNodeState('b;c', 1);
|
||||
$bp->setNodeState('c;d', 1);
|
||||
$bp->setNodeState('d;e', 1);
|
||||
|
||||
$this->assertEquals(
|
||||
'WARNING',
|
||||
|
|
@ -157,14 +157,13 @@ class MinOperatorTest extends BaseTestCase
|
|||
$names = array();
|
||||
$a = 97;
|
||||
for ($i = 1; $i <= $count; $i++) {
|
||||
$names[] = chr($a + $i);
|
||||
$names[] = chr($a + $i) . ';' . chr($a + $i + 1);
|
||||
}
|
||||
|
||||
$storage = new LegacyStorage($this->emptyConfigSection());
|
||||
$expression = sprintf('a = %d of: %s', $min, join(' + ', $names));
|
||||
$bp = $storage->loadFromString('dummy', $expression);
|
||||
foreach ($names as $n) {
|
||||
$bp->createBp($n);
|
||||
if ($defaultState !== null) {
|
||||
$bp->setNodeState($n, $defaultState);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,10 +12,10 @@ class NotOperatorTest extends BaseTestCase
|
|||
{
|
||||
$storage = new LegacyStorage($this->emptyConfigSection());
|
||||
$expressions = array(
|
||||
'a = !b',
|
||||
'a = ! b',
|
||||
'a = b ! c ! d',
|
||||
'a = ! b ! c ! d !',
|
||||
'a = !b;c',
|
||||
'a = ! b;c',
|
||||
'a = b;c ! c;d ! d;e',
|
||||
'a = ! b;c ! c;d ! d;e !',
|
||||
);
|
||||
|
||||
foreach ($expressions as $expression) {
|
||||
|
|
@ -29,10 +29,10 @@ class NotOperatorTest extends BaseTestCase
|
|||
public function testASimpleNegationGivesTheCorrectResult()
|
||||
{
|
||||
$storage = new LegacyStorage($this->emptyConfigSection());
|
||||
$expression = 'a = !b';
|
||||
$expression = 'a = !b;c';
|
||||
$bp = $storage->loadFromString('dummy', $expression);
|
||||
$a = $bp->getNode('a');
|
||||
$b = $bp->createBp('b')->setState(3);
|
||||
$b = $bp->getNode('b;c')->setState(3);
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
$a->getStateName()
|
||||
|
|
@ -49,9 +49,9 @@ class NotOperatorTest extends BaseTestCase
|
|||
public function testThreeTimesCriticalIsOk()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 2);
|
||||
$bp->setNodeState('c', 2);
|
||||
$bp->setNodeState('d', 2);
|
||||
$bp->setNodeState('b;c', 2);
|
||||
$bp->setNodeState('c;d', 2);
|
||||
$bp->setNodeState('d;e', 2);
|
||||
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
|
|
@ -62,9 +62,9 @@ class NotOperatorTest extends BaseTestCase
|
|||
public function testThreeTimesUnknownIsOk()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 3);
|
||||
$bp->setNodeState('c', 3);
|
||||
$bp->setNodeState('d', 3);
|
||||
$bp->setNodeState('b;c', 3);
|
||||
$bp->setNodeState('c;d', 3);
|
||||
$bp->setNodeState('d;e', 3);
|
||||
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
|
|
@ -75,9 +75,9 @@ class NotOperatorTest extends BaseTestCase
|
|||
public function testThreeTimesWarningIsWarning()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 1);
|
||||
$bp->setNodeState('c', 1);
|
||||
$bp->setNodeState('d', 1);
|
||||
$bp->setNodeState('b;c', 1);
|
||||
$bp->setNodeState('c;d', 1);
|
||||
$bp->setNodeState('d;e', 1);
|
||||
|
||||
$this->assertEquals(
|
||||
'WARNING',
|
||||
|
|
@ -88,9 +88,9 @@ class NotOperatorTest extends BaseTestCase
|
|||
public function testThreeTimesOkIsCritical()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 0);
|
||||
$bp->setNodeState('c', 0);
|
||||
$bp->setNodeState('d', 0);
|
||||
$bp->setNodeState('b;c', 0);
|
||||
$bp->setNodeState('c;d', 0);
|
||||
$bp->setNodeState('d;e', 0);
|
||||
|
||||
$this->assertEquals(
|
||||
'CRITICAL',
|
||||
|
|
@ -101,9 +101,9 @@ class NotOperatorTest extends BaseTestCase
|
|||
public function testNotOkAndWarningAndCriticalIsOk()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 0);
|
||||
$bp->setNodeState('c', 1);
|
||||
$bp->setNodeState('d', 2);
|
||||
$bp->setNodeState('b;c', 0);
|
||||
$bp->setNodeState('c;d', 1);
|
||||
$bp->setNodeState('d;e', 2);
|
||||
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
|
|
@ -114,9 +114,9 @@ class NotOperatorTest extends BaseTestCase
|
|||
public function testNotWarningAndUnknownAndCriticalIsOk()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 3);
|
||||
$bp->setNodeState('c', 2);
|
||||
$bp->setNodeState('d', 1);
|
||||
$bp->setNodeState('b;c', 3);
|
||||
$bp->setNodeState('c;d', 2);
|
||||
$bp->setNodeState('d;e', 1);
|
||||
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
|
|
@ -127,9 +127,9 @@ class NotOperatorTest extends BaseTestCase
|
|||
public function testNotTwoTimesWarningAndOkIsWarning()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 0);
|
||||
$bp->setNodeState('c', 1);
|
||||
$bp->setNodeState('d', 1);
|
||||
$bp->setNodeState('b;c', 0);
|
||||
$bp->setNodeState('c;d', 1);
|
||||
$bp->setNodeState('d;e', 1);
|
||||
|
||||
$this->assertEquals(
|
||||
'WARNING',
|
||||
|
|
@ -143,11 +143,8 @@ class NotOperatorTest extends BaseTestCase
|
|||
protected function getBp()
|
||||
{
|
||||
$storage = new LegacyStorage($this->emptyConfigSection());
|
||||
$expression = 'a = ! b ! c ! d';
|
||||
$expression = 'a = ! b;c ! c;d ! d;e';
|
||||
$bp = $storage->loadFromString('dummy', $expression);
|
||||
$bp->createBp('b');
|
||||
$bp->createBp('c');
|
||||
$bp->createBp('d');
|
||||
|
||||
return $bp;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ class OrOperatorTest extends BaseTestCase
|
|||
{
|
||||
$storage = new LegacyStorage($this->emptyConfigSection());
|
||||
$expressions = array(
|
||||
'a = b',
|
||||
'a = b | c | d',
|
||||
'a = b;c',
|
||||
'a = b;c | c;d | d;e',
|
||||
);
|
||||
|
||||
foreach ($expressions as $expression) {
|
||||
|
|
@ -27,9 +27,9 @@ class OrOperatorTest extends BaseTestCase
|
|||
public function testThreeTimesCriticalIsCritical()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 2);
|
||||
$bp->setNodeState('c', 2);
|
||||
$bp->setNodeState('d', 2);
|
||||
$bp->setNodeState('b;c', 2);
|
||||
$bp->setNodeState('c;d', 2);
|
||||
$bp->setNodeState('d;e', 2);
|
||||
|
||||
$this->assertEquals(
|
||||
'CRITICAL',
|
||||
|
|
@ -40,9 +40,9 @@ class OrOperatorTest extends BaseTestCase
|
|||
public function testTwoTimesCriticalOrUnknownIsUnknown()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 2);
|
||||
$bp->setNodeState('c', 3);
|
||||
$bp->setNodeState('d', 2);
|
||||
$bp->setNodeState('b;c', 2);
|
||||
$bp->setNodeState('c;d', 3);
|
||||
$bp->setNodeState('d;e', 2);
|
||||
|
||||
$this->assertEquals(
|
||||
'UNKNOWN',
|
||||
|
|
@ -53,9 +53,9 @@ class OrOperatorTest extends BaseTestCase
|
|||
public function testCriticalOrWarningOrOkIsOk()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 2);
|
||||
$bp->setNodeState('c', 1);
|
||||
$bp->setNodeState('d', 0);
|
||||
$bp->setNodeState('b;c', 2);
|
||||
$bp->setNodeState('c;d', 1);
|
||||
$bp->setNodeState('d;e', 0);
|
||||
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
|
|
@ -66,9 +66,9 @@ class OrOperatorTest extends BaseTestCase
|
|||
public function testUnknownOrWarningOrCriticalIsWarning()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 2);
|
||||
$bp->setNodeState('c', 1);
|
||||
$bp->setNodeState('d', 3);
|
||||
$bp->setNodeState('b;c', 2);
|
||||
$bp->setNodeState('c;d', 1);
|
||||
$bp->setNodeState('d;e', 3);
|
||||
|
||||
$this->assertEquals(
|
||||
'WARNING',
|
||||
|
|
@ -79,9 +79,9 @@ class OrOperatorTest extends BaseTestCase
|
|||
public function testTwoTimesWarningAndOkIsOk()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 0);
|
||||
$bp->setNodeState('c', 1);
|
||||
$bp->setNodeState('d', 1);
|
||||
$bp->setNodeState('b;c', 0);
|
||||
$bp->setNodeState('c;d', 1);
|
||||
$bp->setNodeState('d;e', 1);
|
||||
|
||||
$this->assertEquals(
|
||||
'OK',
|
||||
|
|
@ -92,9 +92,9 @@ class OrOperatorTest extends BaseTestCase
|
|||
public function testThreeTimesWarningIsWarning()
|
||||
{
|
||||
$bp = $this->getBp();
|
||||
$bp->setNodeState('b', 1);
|
||||
$bp->setNodeState('c', 1);
|
||||
$bp->setNodeState('d', 1);
|
||||
$bp->setNodeState('b;c', 1);
|
||||
$bp->setNodeState('c;d', 1);
|
||||
$bp->setNodeState('d;e', 1);
|
||||
|
||||
$this->assertEquals(
|
||||
'WARNING',
|
||||
|
|
@ -108,11 +108,8 @@ class OrOperatorTest extends BaseTestCase
|
|||
protected function getBp()
|
||||
{
|
||||
$storage = new LegacyStorage($this->emptyConfigSection());
|
||||
$expression = 'a = b | c | d';
|
||||
$expression = 'a = b;c | c;d | d;e';
|
||||
$bp = $storage->loadFromString('dummy', $expression);
|
||||
$bp->createBp('b');
|
||||
$bp->createBp('c');
|
||||
$bp->createBp('d');
|
||||
|
||||
return $bp;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,10 +47,10 @@ class ServiceNodeTest extends BaseTestCase
|
|||
protected function pingOnLocalhost()
|
||||
{
|
||||
$bp = new BpConfig();
|
||||
return new ServiceNode($bp, (object) array(
|
||||
return (new ServiceNode((object) array(
|
||||
'hostname' => 'localhost',
|
||||
'service' => 'ping <> pong',
|
||||
'state' => 0,
|
||||
));
|
||||
)))->setBpConfig($bp);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue