Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to add a new child using Business Impact action in the Host/Service Details view #386

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions application/controllers/NodeController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@

namespace Icinga\Module\Businessprocess\Controllers;


use Exception;

use GuzzleHttp\Psr7\ServerRequest;

use Icinga\Application\Modules\Module;
use Icinga\Module\Businessprocess\Forms\AddNodeToProcessForm;
use Icinga\Module\Businessprocess\ProvidedHook\Icingadb\IcingadbSupport;
use Icinga\Module\Businessprocess\Renderer\Breadcrumb;
use Icinga\Module\Businessprocess\Renderer\TileRenderer;
Expand All @@ -12,9 +17,14 @@
use Icinga\Module\Businessprocess\State\MonitoringState;
use Icinga\Module\Businessprocess\Web\Controller;
use Icinga\Module\Businessprocess\Web\Url;

use ipl\Html\Html;
use ipl\Web\Widget\Link;

use ipl\Web\Url as iplUrl;
use ipl\Web\Widget\ButtonLink;


class NodeController extends Controller
{
public function impactAction()
Expand Down Expand Up @@ -144,5 +154,33 @@ public function impactAction()

$content->addHtml($elem);
}

$content->add(
(new ButtonLink(t('Add to process'), iplUrl::fromPath('businessprocess/node/add', ['name' => $name])))
->setAttribute('data-base-target', '_self')
);
}

public function addAction(): void
{
$this->controls()->add(
$this->singleTab($this->translate('Add Node'))
);

$objectName = $this->params->getRequired('name');

$this->addTitle(sprintf(t('Add %s to process'), $objectName));

$form = (new AddNodeToProcessForm())
->populate(['config' => $this->params->get('config')])
->setStorage($this->storage())
->setNodeName($objectName)
->setSession($this->session())
->on(AddNodeToProcessForm::ON_SUCCESS, function($form) use ($objectName) {
$this->redirectNow(iplUrl::fromPath('businessprocess/node/impact', ['name' => $objectName]));
})
->handleRequest(ServerRequest::fromGlobals());

$this->content()->add($form);
}
}
176 changes: 176 additions & 0 deletions application/forms/AddNodeToProcessForm.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
<?php

namespace Icinga\Module\Businessprocess\Forms;

use Exception;
use Icinga\Exception\ConfigurationError;
use Icinga\Module\Businessprocess\BpConfig;
use Icinga\Module\Businessprocess\BpNode;
use Icinga\Module\Businessprocess\Modification\ProcessChanges;
use Icinga\Module\Businessprocess\Renderer\TreeRenderer;
use Icinga\Module\Businessprocess\Storage\Storage;
use Icinga\Module\Businessprocess\Web\Url;
use Icinga\Web\Session;
use ipl\Html\Form;
use ipl\Html\FormDecorator\DdDtDecorator;
use ipl\I18n\Translation;
use ipl\Web\Common\CsrfCounterMeasure;

class AddNodeToProcessForm extends Form
{
use Translation;
use CsrfCounterMeasure;

/** @var ?Storage */
protected $storage;

/** @var ?Session\SessionNamespace */
protected $session;

/** @var ?string */
protected $nodeName;

/** @var BpConfig */
protected $bpConfig;

protected $changes;

protected $fakeNodeName = '$_Unbound_$';

/**
* Set the storage
*
* @param Storage $storage
*
* @return $this
*/
public function setStorage(Storage $storage): self
{
$this->storage = $storage;

return $this;
}

/**
* Set the session
*
* @param Session\SessionNamespace $session
*
* @return $this
*/
public function setSession(Session\SessionNamespace $session): self
{
$this->session = $session;

return $this;
}

public function setBpConfig(BpConfig $config)
{
$this->bpConfig = $config;

return $this;
}

public function setNodeName(string $nodeName): self
{
$this->nodeName = $nodeName;

return $this;
}

public function getNodeName(): ?string
{
return $this->nodeName;
}

protected function getProcessChanges(BpConfig $bpConfig)
{
return ProcessChanges::construct($bpConfig, $this->session);
}

protected function assemble()
{
$this->createCsrfCounterMeasure(Session::getSession()->getId());
$this->setDefaultElementDecorator(new DdDtDecorator());

$this->addElement('select', 'config', [
'label' => $this->translate('File Name'),
'required' => true,
'class' => 'autosubmit',
'options' => array_merge(
['' => $this->translate('Please choose')],
$this->storage->listProcesses()
),
'disabledOptions' => [''],
'description' => $this->translate('Choose a configuration file')
]);

$newParentNode = null;
$configName = $this->getValue('config');
if ($configName !== null) {
try {
$this->setBpConfig($this->storage->loadProcess($configName));
} catch (Exception $e) {
throw new ConfigurationError(
'Config file %s.conf is invalid, please choose another one',
$configName
);
}

$changes = $this->getProcessChanges($this->bpConfig);

/* if ($changes->count() > 2) {// moves again
$changes->pop(); // remove last change
}*/

if ($changes->isEmpty()) {
$changes->createNode($this->fakeNodeName, ['operator' => '&', 'childNames' => [$this->getNodeName()]]);
} else {
$this->bpConfig->applyChanges($changes);
}

if ($this->getPopulatedValue('from') !== null) {
if (! $this->bpConfig->getMetadata()->isManuallyOrdered()) {
$changes->applyManualOrder();
}

try {
$changes->moveNode(
$this->bpConfig->getNode($this->getNodeName()),
0, // $this->getPopulatedValue('from'),
$this->getPopulatedValue('to'),
$this->getPopulatedValue('parent'),
$this->fakeNodeName
);
} catch (Exception $e) {
throw new Exception($e->getMessage());
}
}

// Trigger session destruction to make sure it get's stored.
unset($changes);

$tree = (new TreeRenderer($this->bpConfig))
->setUrl(Url::fromRequest())
->setExtraChild(! $newParentNode ? $this->getNodeName() : null)
->unlock();

$tree->setSort($tree->getDefaultSort());

$this->add($tree);

$this->addElement('submit', 'submit');
}
}

protected function onSuccess()
{
//$this->bpConfig->removeNode('Unbound');

$changes = $this->getProcessChanges($this->bpConfig);
$this->bpConfig->applyChanges($changes);

$this->storage->storeProcess($this->bpConfig);
}
}
65 changes: 58 additions & 7 deletions library/Businessprocess/Renderer/TreeRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ class TreeRenderer extends Renderer
{
const NEW_COLLAPSIBLE_IMPLEMENTATION_SINCE = '2.11.2';

/** @var ?string */
protected $extraChild;

public function assemble()
{
$bp = $this->config;
Expand All @@ -34,9 +37,11 @@ public function assemble()
: 'false',
'data-sortable-data-id-attr' => 'id',
'data-sortable-direction' => 'vertical',
'data-sortable-sort' => $this->hasExtraChild() ? 'false' : 'true',
'data-sortable-group' => json_encode([
'name' => $this->wantsRootNodes() ? 'root' : $htmlId,
'put' => 'function:rowPutAllowed'
'put' => 'function:rowPutAllowed',
'pull' => ! $this->hasExtraChild()
]),
'data-sortable-invert-swap' => 'true',
'data-csrf-token' => CsrfToken::generate()
Expand Down Expand Up @@ -78,6 +83,20 @@ public function renderBp(BpConfig $bp)
$html = [];
if ($this->wantsRootNodes()) {
$nodes = $bp->getRootNodes();
if ($this->hasExtraChild()) {
$objectToAdd = $this->getExtraChild();
$parts = explode(';', $objectToAdd);
if ($parts[1] === 'Hoststatus') {
$bp->createHost($parts[0]);
} else {
$bp->createService($parts[0], $parts[1]);
}

$parent = $bp->createBp('Unbound');
$parent->addChild($bp->getNode($objectToAdd));

$html[] = $this->renderNode($bp, $parent, true);
}
} else {
$nodes = $this->parent->getChildren();
}
Expand Down Expand Up @@ -175,11 +194,12 @@ public function getOverriddenState($fakeState, Node $node)
/**
* @param BpConfig $bp
* @param Node $node
* @param bool $isFakeNode
* @param array $path
*
* @return string
*/
public function renderNode(BpConfig $bp, Node $node, $path = array())
public function renderNode(BpConfig $bp, Node $node, bool $isFakeNode = false, $path = array())
{
$htmlId = $this->getId($node, $path);
$li = Html::tag(
Expand Down Expand Up @@ -219,7 +239,7 @@ public function renderNode(BpConfig $bp, Node $node, $path = array())

$summary->add(Html::tag('span', null, $node->getAlias()));

if ($node instanceof BpNode) {
if ( ! $isFakeNode && $node instanceof BpNode) {
$summary->add(Html::tag('span', ['class' => 'op'], $node->operatorHtml()));
}

Expand All @@ -228,7 +248,7 @@ public function renderNode(BpConfig $bp, Node $node, $path = array())
}

$differentConfig = $node->getBpConfig()->getName() !== $this->getBusinessProcess()->getName();
if (! $this->isLocked() && !$differentConfig) {
if (! $this->isLocked() && ! $this->hasExtraChild() && ! $differentConfig) {
$summary->add($this->getActionIcons($bp, $node));
} elseif ($differentConfig) {
$summary->add($this->actionIcon(
Expand All @@ -247,9 +267,11 @@ public function renderNode(BpConfig $bp, Node $node, $path = array())
'data-sortable-data-id-attr' => 'id',
'data-sortable-draggable' => '.movable',
'data-sortable-direction' => 'vertical',
'data-sortable-sort' => $this->hasExtraChild() ? 'false' : 'true',
'data-sortable-group' => json_encode([
'name' => $htmlId, // Unique, so that the function below is the only deciding factor
'put' => 'function:rowPutAllowed'
'put' => ! $isFakeNode ? 'function:rowPutAllowed' : false,
'pull' => $this->hasExtraChild() ? 'function:rowPullAllowed' : true,
]),
'data-csrf-token' => CsrfToken::generate(),
'data-action-url' => $this->getUrl()
Expand All @@ -265,7 +287,7 @@ public function renderNode(BpConfig $bp, Node $node, $path = array())
$path[] = $differentConfig ? $node->getIdentifier() : $node->getName();
foreach ($this->sort($node->getChildren()) as $name => $child) {
if ($child instanceof BpNode) {
$ul->add($this->renderNode($bp, $child, $path));
$ul->add($this->renderNode($bp, $child, false, $path));
} else {
$ul->add($this->renderChild($bp, $node, $child, $path));
}
Expand All @@ -286,6 +308,14 @@ protected function renderChild($bp, BpNode $parent, Node $node, $path = null)
'data-node-name' => $node->getName()
]);

if ($this->hasExtraChild()) {
if ($node->getName() === $this->getExtraChild()) {
$li->addAttributes(['class' => 'new-child']);
} else {
$li->addAttributes(['class' => 'unhighlight']);
}
}

$li->add($this->getNodeIcons($node, $path, $parent));

$link = $node->getLink();
Expand All @@ -296,7 +326,11 @@ protected function renderChild($bp, BpNode $parent, Node $node, $path = null)
$li->add($this->getOverriddenState($overriddenState, $node));
}

if (! $this->isLocked() && $node->getBpConfig()->getName() === $this->getBusinessProcess()->getName()) {
if (
! $this->isLocked()
&& ! $this->hasExtraChild()
&& $node->getBpConfig()->getName() === $this->getBusinessProcess()->getName()
) {
$li->add($this->getActionIcons($bp, $node));
}

Expand Down Expand Up @@ -374,4 +408,21 @@ protected function renderAddNewNode($parent)
mt('businessprocess', 'Add a new business process node')
);
}

public function setExtraChild(?string $extraChild): self
{
$this->extraChild = $extraChild;

return $this;
}

public function getExtraChild(): ?string
{
return $this->extraChild;
}

public function hasExtraChild(): bool
{
return $this->extraChild !== null;
}
}
4 changes: 4 additions & 0 deletions public/css/module.less
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ form a {
color: @icinga-blue;
}

form li.unhighlight a {
color: @gray;
}

div.bp {
margin-bottom: 4px;
}
Expand Down
Loading