Skip to content

Commit 11b706d

Browse files
wip
1 parent c2e06ac commit 11b706d

File tree

6 files changed

+284
-10
lines changed

6 files changed

+284
-10
lines changed

application/controllers/NodeController.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
namespace Icinga\Module\Businessprocess\Controllers;
44

5+
use GuzzleHttp\Psr7\ServerRequest;
56
use Icinga\Application\Modules\Module;
7+
use Icinga\Module\Businessprocess\Forms\AddNodeToProcessForm;
68
use Icinga\Module\Businessprocess\ProvidedHook\Icingadb\IcingadbSupport;
79
use Icinga\Module\Businessprocess\Renderer\Breadcrumb;
810
use Icinga\Module\Businessprocess\Renderer\TileRenderer;
@@ -11,6 +13,8 @@
1113
use Icinga\Module\Businessprocess\State\MonitoringState;
1214
use Icinga\Module\Businessprocess\Web\Controller;
1315
use Icinga\Module\Businessprocess\Web\Url;
16+
use ipl\Web\Url as iplUrl;
17+
use ipl\Web\Widget\ButtonLink;
1418

1519
class NodeController extends Controller
1620
{
@@ -108,5 +112,30 @@ public function impactAction()
108112
if ($content->isEmpty()) {
109113
$content->add($this->translate('No impact detected. Is this node part of a business process?'));
110114
}
115+
116+
$content->add(
117+
(new ButtonLink(t('Add to process'), iplUrl::fromPath('businessprocess/node/add', ['name' => $name])))
118+
->setAttribute('data-base-target', '_self')
119+
);
120+
}
121+
122+
public function addAction(): void
123+
{
124+
$this->controls()->add(
125+
$this->singleTab($this->translate('Add Node'))
126+
);
127+
128+
$objectName = $this->params->getRequired('name');
129+
130+
$this->addTitle(sprintf(t('Add %s to process'), $objectName));
131+
132+
$form = (new AddNodeToProcessForm())
133+
->populate(['config' => $this->params->get('config')])
134+
->setStorage($this->storage())
135+
->setNodeName($objectName)
136+
->setSession($this->session())
137+
->handleRequest(ServerRequest::fromGlobals());
138+
139+
$this->content()->add($form);
111140
}
112141
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
<?php
2+
3+
namespace Icinga\Module\Businessprocess\Forms;
4+
5+
use Exception;
6+
use Icinga\Application\Icinga;
7+
use Icinga\Exception\ConfigurationError;
8+
use Icinga\Module\Businessprocess\BpConfig;
9+
use Icinga\Module\Businessprocess\Exception\ModificationError;
10+
use Icinga\Module\Businessprocess\Modification\ProcessChanges;
11+
use Icinga\Module\Businessprocess\Renderer\TreeRenderer;
12+
use Icinga\Module\Businessprocess\Storage\Storage;
13+
use Icinga\Module\Businessprocess\Web\Url;
14+
use Icinga\Web\Session;
15+
use ipl\Html\Form;
16+
use ipl\Html\FormDecorator\DdDtDecorator;
17+
use ipl\I18n\Translation;
18+
use ipl\Web\Common\CsrfCounterMeasure;
19+
20+
class AddNodeToProcessForm extends Form
21+
{
22+
use Translation;
23+
use CsrfCounterMeasure;
24+
25+
/** @var ?Storage */
26+
protected $storage;
27+
28+
/** @var ?Session\SessionNamespace */
29+
protected $session;
30+
31+
/** @var ?string */
32+
protected $nodeName;
33+
34+
/** @var BpConfig */
35+
protected $bpConfig;
36+
37+
protected $changes;
38+
39+
/**
40+
* Set the storage
41+
*
42+
* @param Storage $storage
43+
*
44+
* @return $this
45+
*/
46+
public function setStorage(Storage $storage): self
47+
{
48+
$this->storage = $storage;
49+
50+
return $this;
51+
}
52+
53+
/**
54+
* Set the session
55+
*
56+
* @param Session\SessionNamespace $session
57+
*
58+
* @return $this
59+
*/
60+
public function setSession(Session\SessionNamespace $session): self
61+
{
62+
$this->session = $session;
63+
64+
return $this;
65+
}
66+
67+
public function setBpConfig(BpConfig $config)
68+
{
69+
$this->bpConfig = $config;
70+
71+
return $this;
72+
}
73+
74+
public function setNodeName(string $nodeName): self
75+
{
76+
$this->nodeName = $nodeName;
77+
78+
return $this;
79+
}
80+
81+
public function getNodeName(): ?string
82+
{
83+
return $this->nodeName;
84+
}
85+
86+
protected function assemble()
87+
{
88+
$this->createCsrfCounterMeasure(Session::getSession()->getId());
89+
$this->setDefaultElementDecorator(new DdDtDecorator());
90+
91+
$this->addElement('select', 'config', [
92+
'label' => $this->translate('File Name'),
93+
'required' => true,
94+
'class' => 'autosubmit',
95+
'options' => array_merge(
96+
['' => $this->translate('Please choose')],
97+
$this->storage->listProcesses()
98+
),
99+
'disabledOptions' => [''],
100+
'description' => $this->translate('Choose a configuration file')
101+
]);
102+
103+
$newParentNode = null;
104+
$configName = $this->getValue('config');
105+
if ($configName !== null) {
106+
try {
107+
$this->setBpConfig($this->storage->loadProcess($configName));
108+
$parent = $this->bpConfig->createBp('Unbound');
109+
$changes = ProcessChanges::construct($this->bpConfig, $this->session);
110+
$changes->addChildrenToNode($this->getNodeName(), $parent);
111+
$newParentNode = $this->getPopulatedValue('parent');
112+
$newParentNode = $newParentNode ? $this->bpConfig->getNode($newParentNode) : null;
113+
if ($newParentNode && ! $this->bpConfig->hasNode($this->getNodeName())) {
114+
$changes->addChildrenToNode($this->getNodeName(), $newParentNode);
115+
}
116+
117+
if ($this->getPopulatedValue('from') !== null) {
118+
if (! $this->bpConfig->getMetadata()->isManuallyOrdered()) {
119+
$changes->applyManualOrder();
120+
}
121+
122+
123+
try {
124+
$changes->moveNode(
125+
$this->bpConfig->getNode($this->getNodeName()),
126+
$this->getPopulatedValue('from'),
127+
$this->getPopulatedValue('to'),
128+
$this->getPopulatedValue('parent')
129+
);
130+
} catch (Exception $e) {
131+
throw new Exception($e->getMessage());
132+
/*$this->notifyError($e->getMessage());
133+
Icinga::app()->getResponse()
134+
// Web 2's JS forces a content update for non-200s. Our own JS
135+
// can't prevent this, hence we're not making this a 400 :(
136+
//->setHttpResponseCode(400)
137+
->setHeader('X-Icinga-Container', 'ignore')
138+
->sendResponse();
139+
exit;*/
140+
}
141+
}
142+
// Trigger session destruction to make sure it get's stored.
143+
unset($changes);
144+
} catch (Exception $e) {
145+
throw new ConfigurationError(
146+
'Config file %s.conf is invalid, please choose another one',
147+
$configName
148+
);
149+
}
150+
151+
$tree = (new TreeRenderer($this->bpConfig))
152+
->setUrl(Url::fromRequest())
153+
->setExtraChild(! $newParentNode ? $this->getNodeName() : null)
154+
->unlock();
155+
156+
$this->add($tree);
157+
158+
$this->addElement('submit', 'submit');
159+
}
160+
}
161+
162+
protected function onSuccess()
163+
{
164+
165+
}
166+
}

library/Businessprocess/Renderer/TreeRenderer.php

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ class TreeRenderer extends Renderer
2020
{
2121
const NEW_COLLAPSIBLE_IMPLEMENTATION_SINCE = '2.11.2';
2222

23+
/** @var ?string */
24+
protected $extraChild;
25+
2326
public function assemble()
2427
{
2528
$bp = $this->config;
@@ -32,9 +35,11 @@ public function assemble()
3235
'data-sortable-disabled' => $this->isLocked() ? 'true' : 'false',
3336
'data-sortable-data-id-attr' => 'id',
3437
'data-sortable-direction' => 'vertical',
38+
'data-sortable-sort' => $this->hasExtraChild() ? 'false' : 'true',
3539
'data-sortable-group' => json_encode([
3640
'name' => $this->wantsRootNodes() ? 'root' : $htmlId,
37-
'put' => 'function:rowPutAllowed'
41+
'put' => 'function:rowPutAllowed',
42+
'pull' => ! $this->hasExtraChild()
3843
]),
3944
'data-sortable-invert-swap' => 'true',
4045
'data-csrf-token' => CsrfToken::generate()
@@ -76,6 +81,20 @@ public function renderBp(BpConfig $bp)
7681
$html = array();
7782
if ($this->wantsRootNodes()) {
7883
$nodes = $bp->getChildren();
84+
if ($this->hasExtraChild()) {
85+
$objectToAdd = $this->getExtraChild();
86+
$parts = explode(';', $objectToAdd);
87+
if ($parts[1] === 'Hoststatus') {
88+
$bp->createHost($parts[0]);
89+
} else {
90+
$bp->createService($parts[0], $parts[1]);
91+
}
92+
93+
$parent = $bp->createBp('Unbound');
94+
$parent->addChild($bp->getNode($objectToAdd));
95+
96+
$html[] = $this->renderNode($bp, $parent, true);
97+
}
7998
} else {
8099
$nodes = $this->parent->getChildren();
81100
}
@@ -172,11 +191,12 @@ public function getOverriddenState($fakeState, Node $node)
172191
/**
173192
* @param BpConfig $bp
174193
* @param Node $node
194+
* @param bool $isFakeNode
175195
* @param array $path
176196
*
177197
* @return string
178198
*/
179-
public function renderNode(BpConfig $bp, Node $node, $path = array())
199+
public function renderNode(BpConfig $bp, Node $node, bool $isFakeNode = false, $path = array())
180200
{
181201
$htmlId = $this->getId($node, $path);
182202
$li = Html::tag(
@@ -216,7 +236,7 @@ public function renderNode(BpConfig $bp, Node $node, $path = array())
216236

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

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

@@ -225,7 +245,7 @@ public function renderNode(BpConfig $bp, Node $node, $path = array())
225245
}
226246

227247
$differentConfig = $node->getBpConfig()->getName() !== $this->getBusinessProcess()->getName();
228-
if (! $this->isLocked() && !$differentConfig) {
248+
if (! $this->isLocked() && ! $this->hasExtraChild() && ! $differentConfig) {
229249
$summary->add($this->getActionIcons($bp, $node));
230250
} elseif ($differentConfig) {
231251
$summary->add($this->actionIcon(
@@ -242,9 +262,11 @@ public function renderNode(BpConfig $bp, Node $node, $path = array())
242262
'data-sortable-data-id-attr' => 'id',
243263
'data-sortable-draggable' => '.movable',
244264
'data-sortable-direction' => 'vertical',
265+
'data-sortable-sort' => $this->hasExtraChild() ? 'false' : 'true',
245266
'data-sortable-group' => json_encode([
246267
'name' => $htmlId, // Unique, so that the function below is the only deciding factor
247-
'put' => 'function:rowPutAllowed'
268+
'put' => ! $isFakeNode ? 'function:rowPutAllowed' : false,
269+
'pull' => $this->hasExtraChild() ? 'function:rowPullAllowed' : true,
248270
]),
249271
'data-csrf-token' => CsrfToken::generate(),
250272
'data-action-url' => $this->getUrl()
@@ -260,7 +282,7 @@ public function renderNode(BpConfig $bp, Node $node, $path = array())
260282
$path[] = $differentConfig ? $node->getIdentifier() : $node->getName();
261283
foreach ($node->getChildren() as $name => $child) {
262284
if ($child instanceof BpNode) {
263-
$ul->add($this->renderNode($bp, $child, $path));
285+
$ul->add($this->renderNode($bp, $child, false, $path));
264286
} else {
265287
$ul->add($this->renderChild($bp, $node, $child, $path));
266288
}
@@ -281,6 +303,14 @@ protected function renderChild($bp, BpNode $parent, Node $node, $path = null)
281303
'data-node-name' => $node->getName()
282304
]);
283305

306+
if ($this->hasExtraChild()) {
307+
if ($node->getName() === $this->getExtraChild()) {
308+
$li->addAttributes(['class' => 'new-child']);
309+
} else {
310+
$li->addAttributes(['class' => 'unhighlight']);
311+
}
312+
}
313+
284314
$li->add($this->getNodeIcons($node, $path, $parent));
285315

286316
$link = $node->getLink();
@@ -291,7 +321,11 @@ protected function renderChild($bp, BpNode $parent, Node $node, $path = null)
291321
$li->add($this->getOverriddenState($overriddenState, $node));
292322
}
293323

294-
if (! $this->isLocked() && $node->getBpConfig()->getName() === $this->getBusinessProcess()->getName()) {
324+
if (
325+
! $this->isLocked()
326+
&& ! $this->hasExtraChild()
327+
&& $node->getBpConfig()->getName() === $this->getBusinessProcess()->getName()
328+
) {
295329
$li->add($this->getActionIcons($bp, $node));
296330
}
297331

@@ -369,4 +403,21 @@ protected function renderAddNewNode($parent)
369403
mt('businessprocess', 'Add a new business process node')
370404
);
371405
}
406+
407+
public function setExtraChild(?string $extraChild): self
408+
{
409+
$this->extraChild = $extraChild;
410+
411+
return $this;
412+
}
413+
414+
public function getExtraChild(): ?string
415+
{
416+
return $this->extraChild;
417+
}
418+
419+
public function hasExtraChild(): bool
420+
{
421+
return $this->extraChild !== null;
422+
}
372423
}

public/css/module.less

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ form a {
8080
color: @icinga-blue;
8181
}
8282

83+
form li.unhighlight a {
84+
color: @gray;
85+
}
86+
8387
div.bp {
8488
margin-bottom: 4px;
8589
}

public/js/behavior/sortable.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,15 @@
3333
}
3434
});
3535

36-
if (typeof options.group !== 'undefined' && typeof options.group.put === 'string' && options.group.put.substring(0, 9) === 'function:') {
37-
var module = icinga.module($el.closest('.icinga-module').data('icingaModule'));
38-
options.group.put = module.object[options.group.put.substr(9)];
36+
if (typeof options.group !== 'undefined') {
37+
let module = icinga.module($el.closest('.icinga-module').data('icingaModule'));
38+
if (typeof options.group.put === 'string' && options.group.put.substring(0, 9) === 'function:') {
39+
options.group.put = module.object[options.group.put.substr(9)];
40+
}
41+
42+
if (typeof options.group.pull === 'string' && options.group.pull.substring(0, 9) === 'function:') {
43+
options.group.pull = module.object[options.group.pull.substr(9)];
44+
}
3945
}
4046

4147
$(this).sortable(options);

0 commit comments

Comments
 (0)