From 87b1d961bcd512630682766aef1730444db2493c Mon Sep 17 00:00:00 2001 From: Jennifer Mourek Date: Thu, 28 Jun 2018 09:01:26 +0200 Subject: [PATCH 1/6] Add functionality to edit node --- application/controllers/ProcessController.php | 21 +- application/forms/EditNodeForm.php | 444 ++++++++++++++++++ .../Renderer/TileRenderer/NodeTile.php | 7 + 3 files changed, 465 insertions(+), 7 deletions(-) create mode 100644 application/forms/EditNodeForm.php diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 32c42ff..52bb7a2 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -217,15 +217,22 @@ class ProcessController extends Controller ->setParentNode($node) ->setSession($this->session()) ->handleRequest(); + } elseif ($action === 'editmonitored' && $canEdit) { + $form = $this->loadForm('EditNode') + ->setProcess($bp) + ->setNode($bp->getNode($this->params->get('editmonitorednode'))) + ->setParentNode($node) + ->setSession($this->session()) + ->handleRequest(); } elseif ($action === 'delete' && $canEdit) { - $form =$this->loadForm('DeleteNode') - ->setProcess($bp) - ->setNode($bp->getNode($this->params->get('deletenode'))) - ->setParentNode($node) - ->setSession($this->session()) - ->handleRequest(); + $form = $this->loadForm('DeleteNode') + ->setProcess($bp) + ->setNode($bp->getNode($this->params->get('deletenode'))) + ->setParentNode($node) + ->setSession($this->session()) + ->handleRequest(); } elseif ($action === 'edit' && $canEdit) { - $form =$this->loadForm('Process') + $form = $this->loadForm('Process') ->setProcess($bp) ->setNode($bp->getNode($this->params->get('editnode'))) ->setSession($this->session()) diff --git a/application/forms/EditNodeForm.php b/application/forms/EditNodeForm.php new file mode 100644 index 0000000..5be971a --- /dev/null +++ b/application/forms/EditNodeForm.php @@ -0,0 +1,444 @@ +node; + + $view = $this->getView(); + $this->addHtml( + '

' . $view->escape( + sprintf($this->translate('Modify "%s"'), $node->getAlias()) + ) . '

' + ); + + $type = $this->selectNodeType(); + switch ($type) { + case 'host': + $this->selectHost(); + break; + case 'service': + $this->selectService(); + break; + case 'process': + $this->selectProcess(); + break; + case 'new-process': + $this->addNewProcess(); + break; + case null: + $this->setSubmitLabel($this->translate('Next')); + return; + } + } + + protected function addNewProcess() + { + $this->addElement('text', 'name', array( + 'label' => $this->translate('Name'), + 'required' => true, + 'disabled' => true, + 'description' => $this->translate( + 'This is the unique identifier of this process' + ), + )); + + $this->addElement('text', 'alias', array( + 'label' => $this->translate('Title'), + 'description' => $this->translate( + 'Usually this title will be shown for this node. Equals name' + . ' if not given' + ), + )); + + $this->addElement('select', 'operator', array( + 'label' => $this->translate('Operator'), + 'required' => true, + 'multiOptions' => array( + '&' => $this->translate('AND'), + '|' => $this->translate('OR'), + '!' => $this->translate('NOT'), + '<' => $this->translate('DEG'), + '1' => $this->translate('MIN 1'), + '2' => $this->translate('MIN 2'), + '3' => $this->translate('MIN 3'), + '4' => $this->translate('MIN 4'), + '5' => $this->translate('MIN 5'), + '6' => $this->translate('MIN 6'), + '7' => $this->translate('MIN 7'), + '8' => $this->translate('MIN 8'), + '9' => $this->translate('MIN 9'), + ) + )); + + $this->addElement('select', 'display', array( + 'label' => $this->translate('Visualization'), + 'required' => true, + 'description' => $this->translate( + 'Where to show this process' + ), + 'value' => $this->hasParentNode() ? '0' : '1', + 'multiOptions' => array( + '1' => $this->translate('Toplevel Process'), + '0' => $this->translate('Subprocess only'), + ) + )); + + $this->addElement('text', 'infoUrl', array( + 'label' => $this->translate('Info URL'), + 'description' => $this->translate( + 'URL pointing to more information about this node' + ) + )); + + + $node = $this->node; + $this->getElement('name')->setValue($node->getName()); + if ($node->hasAlias()) { + $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()); + } + } + + /** + * @return string|null + */ + protected function selectNodeType() + { + $types = array(); + if ($this->hasParentNode()) { + $types['host'] = $this->translate('Host'); + $types['service'] = $this->translate('Service'); + } elseif (! $this->hasProcesses()) { + $this->addElement('hidden', 'node_type', array( + 'ignore' => true, + 'decorators' => array('ViewHelper'), + 'value' => 'new-process' + )); + + return 'new-process'; + } + + if ($this->hasProcesses()) { + $types['process'] = $this->translate('Existing Process'); + } + + $types['new-process'] = $this->translate('New Process Node'); + + $this->addElement('select', 'node_type', array( + 'label' => $this->translate('Node type'), + 'required' => true, + 'description' => $this->translate( + 'The node type you want to add' + ), + 'ignore' => true, + 'class' => 'autosubmit', + 'multiOptions' => $this->optionalEnum($types) + )); + + return $this->getSentValue('node_type'); + } + + protected function selectHost() + { + $this->addElement('multiselect', 'children', array( + 'label' => $this->translate('Hosts'), + 'required' => true, + 'size' => 8, + 'style' => 'width: 25em', + 'multiOptions' => $this->enumHostList(), + 'description' => $this->translate( + 'Hosts that should be part of this business process node' + ) + )); + } + + protected function selectService() + { + $this->addHostElement(); + if ($host = $this->getSentValue('host')) { + $this->addServicesElement($host); + } else { + $this->setSubmitLabel($this->translate('Next')); + } + } + + protected function addHostElement() + { + $this->addElement('select', 'host', array( + 'label' => $this->translate('Host'), + 'required' => true, + 'ignore' => true, + 'class' => 'autosubmit', + 'multiOptions' => $this->optionalEnum($this->enumHostForServiceList()), + )); + } + + protected function addServicesElement($host) + { + $this->addElement('multiselect', 'children', array( + 'label' => $this->translate('Services'), + 'required' => true, + 'size' => 8, + 'style' => 'width: 25em', + 'multiOptions' => $this->enumServiceList($host), + 'description' => $this->translate( + 'Services that should be part of this business process node' + ) + )); + } + + protected function selectProcess() + { + $this->addElement('multiselect', 'children', array( + 'label' => $this->translate('Process nodes'), + 'required' => true, + 'size' => 8, + 'style' => 'width: 25em', + 'multiOptions' => $this->enumProcesses(), + 'description' => $this->translate( + 'Other processes that should be part of this business process node' + ) + )); + } + + /** + * @param MonitoringBackend $backend + * @return $this + */ + public function setBackend(MonitoringBackend $backend) + { + $this->backend = $backend; + return $this; + } + + /** + * @param BpConfig $process + * @return $this + */ + public function setProcess(BpConfig $process) + { + $this->bp = $process; + $this->setBackend($process->getBackend()); + return $this; + } + + /** + * @param BpNode|null $node + * @return $this + */ + public function setParentNode(BpNode $node = null) + { + $this->parent = $node; + return $this; + } + + /** + * @return bool + */ + public function hasParentNode() + { + return $this->parent !== null; + } + + /** + * @param SessionNamespace $session + * @return $this + */ + public function setSession(SessionNamespace $session) + { + $this->session = $session; + return $this; + } + + protected function enumHostForServiceList() + { + $names = $this->backend->select()->from('hostStatus', array( + 'hostname' => 'host_name', + ))->order('host_name')->getQuery()->fetchColumn(); + + // fetchPairs doesn't seem to work when using the same column with + // different aliases twice + + return array_combine((array) $names, (array) $names); + } + + protected function enumHostList() + { + $names = $this->backend->select()->from('hostStatus', array( + 'hostname' => 'host_name', + ))->order('host_name')->getQuery()->fetchColumn(); + + // fetchPairs doesn't seem to work when using the same column with + // different aliases twice + $res = array(); + $suffix = ';Hoststatus'; + foreach ($names as $name) { + $res[$name . $suffix] = $name; + } + + return $res; + } + + protected function enumServiceList($host) + { + $query = $this->backend->select()->from( + 'serviceStatus', + array('service' => 'service_description') + )->where('host_name', $host); + $query->order('service_description'); + $names = $query->getQuery()->fetchColumn(); + + $services = array(); + foreach ($names as $name) { + $services[$host . ';' . $name] = $name; + } + + return $services; + } + + protected function hasProcesses() + { + return count($this->enumProcesses()) > 0; + } + + protected function enumProcesses() + { + $list = array(); + + $parents = array(); + + if ($this->hasParentNode()) { + $this->collectAllParents($this->parent, $parents); + $parents[$this->parent->getName()] = $this->parent; + } + + foreach ($this->bp->getNodes() as $node) { + if ($node instanceof BpNode && ! isset($parents[$node->getName()])) { + $list[(string) $node] = (string) $node; // display name? + } + } + + natsort($list); + return $list; + } + + /** + * Collect the given node's parents recursively into the given array by their names + * + * @param BpNode $node + * @param BpNode[] $parents + */ + protected function collectAllParents(BpNode $node, array & $parents) + { + foreach ($node->getParents() as $parent) { + $parents[$parent->getName()] = $parent; + $this->collectAllParents($parent, $parents); + } + } + + protected function fetchObjectList() + { + $this->objectList = array(); + $hosts = $this->backend->select()->from('hostStatus', array( + 'hostname' => 'host_name', + 'in_downtime' => 'host_in_downtime', + 'ack' => 'host_acknowledged', + 'state' => 'host_state' + ))->order('host_name')->getQuery()->fetchAll(); + + $services = $this->backend->select()->from('serviceStatus', array( + 'hostname' => 'host_name', + 'service' => 'service_description', + 'in_downtime' => 'service_in_downtime', + 'ack' => 'service_acknowledged', + 'state' => 'service_state' + ))->order('host_name')->order('service_description')->getQuery()->fetchAll(); + + foreach ($hosts as $host) { + $this->objectList[$host->hostname] = array( + $host->hostname . ';Hoststatus' => 'Host Status' + ); + } + + foreach ($services as $service) { + $this->objectList[$service->hostname][ + $service->hostname . ';' . $service->service + ] = $service->service; + } + + return $this; + } + + /** + * @param Node $node + * @return $this + */ + public function setNode(Node $node) + { + $this->node = $node; + return $this; + } + + public function onSuccess() + { + $changes = ProcessChanges::construct($this->bp, $this->session); + switch ($this->getValue('node_type')) { + case 'host': + case 'service': + case 'process': + $changes->addChildrenToNode($this->getValue('children'), $this->parent); + break; + case 'new-process': + $properties = $this->getValues(); + unset($properties['name']); + if ($this->hasParentNode()) { + $properties['parentName'] = $this->parent->getName(); + } + $changes->createNode($this->getValue('name'), $properties); + break; + } + + $changes->deleteNode($this->node, $this->parent->getName()); + + // Trigger session destruction to make sure it get's stored. + // TODO: figure out why this is necessary, might be an unclean shutdown on redirect + unset($changes); + + parent::onSuccess(); + } +} diff --git a/library/Businessprocess/Renderer/TileRenderer/NodeTile.php b/library/Businessprocess/Renderer/TileRenderer/NodeTile.php index cc4a7be..b16f6a9 100644 --- a/library/Businessprocess/Renderer/TileRenderer/NodeTile.php +++ b/library/Businessprocess/Renderer/TileRenderer/NodeTile.php @@ -269,6 +269,13 @@ class NodeTile extends BaseElement 'Show the business impact of this node by simulating a specific state' )) )); + + $this->actions()->add(Link::create( + Icon::create('edit'), + $renderer->getUrl()->with('action', 'editmonitored')->with('editmonitorednode', $node->getName()), + null, + array('title' => $this->translate('Modify this monitored node')) + )); } if (! $this->renderer->getBusinessProcess()->getMetadata()->canModify()) { From c1cb7612c027f0d670671c08ae272f4c6351d5b7 Mon Sep 17 00:00:00 2001 From: Jennifer Mourek Date: Thu, 28 Jun 2018 11:18:58 +0200 Subject: [PATCH 2/6] Add preselection when editing --- application/forms/EditNodeForm.php | 73 ++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 18 deletions(-) diff --git a/application/forms/EditNodeForm.php b/application/forms/EditNodeForm.php index 5be971a..149361e 100644 --- a/application/forms/EditNodeForm.php +++ b/application/forms/EditNodeForm.php @@ -28,21 +28,35 @@ class EditNodeForm extends QuickForm protected $processList = array(); + protected $service; + + protected $host; + /** @var SessionNamespace */ protected $session; public function setup() { - $node = $this->node; + $this->host = substr($this->getNode()->getName(), 0, strpos($this->getNode()->getName(), ';')); + if ($this->isService()) { + $this->service = substr($this->getNode()->getName(), strpos($this->getNode()->getName(), ';') + 1); + } $view = $this->getView(); $this->addHtml( '

' . $view->escape( - sprintf($this->translate('Modify "%s"'), $node->getAlias()) + sprintf($this->translate('Modify "%s"'), $this->getNode()->getAlias()) ) . '

' ); - $type = $this->selectNodeType(); + $monitoredNodeType = null; + if ($this->isService()) { + $monitoredNodeType = 'service'; + } else { + $monitoredNodeType = 'host'; + } + + $type = $this->selectNodeType($monitoredNodeType); switch ($type) { case 'host': $this->selectHost(); @@ -60,6 +74,24 @@ class EditNodeForm extends QuickForm $this->setSubmitLabel($this->translate('Next')); return; } +/* + $this->getElement('name')->setValue($this->getNode()->getName()); + if ($node->hasAlias()) { + $this->getElement('alias')->setValue($this->getNode()->getAlias()); + } + $this->getElement('operator')->setValue($this->getNode()->getOperator()); + $this->getElement('display')->setValue($this->getNode()->getDisplay()); + if ($node->hasInfoUrl()) { + $this->getElement('url')->setValue($this->getNode()->getInfoUrl()); + } +*/ + } + + protected function isService() { + if (strpos($this->getNode()->getName(), ';Hoststatus')) { + return false; + } + return true; } protected function addNewProcess() @@ -120,24 +152,12 @@ class EditNodeForm extends QuickForm 'URL pointing to more information about this node' ) )); - - - $node = $this->node; - $this->getElement('name')->setValue($node->getName()); - if ($node->hasAlias()) { - $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()); - } } /** * @return string|null */ - protected function selectNodeType() + protected function selectNodeType($monitoredNodeType = null) { $types = array(); if ($this->hasParentNode()) { @@ -170,6 +190,13 @@ class EditNodeForm extends QuickForm 'multiOptions' => $this->optionalEnum($types) )); + if ($monitoredNodeType !== null) { + $this->getElement('node_type')->setValue($monitoredNodeType); + if ($this->getSentValue('node_type') === null) { + return $monitoredNodeType; + } + } + return $this->getSentValue('node_type'); } @@ -190,7 +217,10 @@ class EditNodeForm extends QuickForm protected function selectService() { $this->addHostElement(); - if ($host = $this->getSentValue('host')) { + + if ($this->getSentValue('hosts') === null) { + $this->addServicesElement($this->host); + } elseif ($host = $this->getSentValue('hosts')) { $this->addServicesElement($host); } else { $this->setSubmitLabel($this->translate('Next')); @@ -199,13 +229,15 @@ class EditNodeForm extends QuickForm protected function addHostElement() { - $this->addElement('select', 'host', array( + $this->addElement('select', 'hosts', array( 'label' => $this->translate('Host'), 'required' => true, 'ignore' => true, 'class' => 'autosubmit', 'multiOptions' => $this->optionalEnum($this->enumHostForServiceList()), )); + + $this->getElement('hosts')->setValue($this->host); } protected function addServicesElement($host) @@ -414,6 +446,11 @@ class EditNodeForm extends QuickForm return $this; } + public function getNode() + { + return $this->node; + } + public function onSuccess() { $changes = ProcessChanges::construct($this->bp, $this->session); From d9808ae9b719d905be5f9495c11cc2f3c178f00a Mon Sep 17 00:00:00 2001 From: Jennifer Mourek Date: Thu, 28 Jun 2018 11:45:06 +0200 Subject: [PATCH 3/6] Remove comment and fix codestyle --- application/forms/EditNodeForm.php | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/application/forms/EditNodeForm.php b/application/forms/EditNodeForm.php index 149361e..ba126b5 100644 --- a/application/forms/EditNodeForm.php +++ b/application/forms/EditNodeForm.php @@ -53,7 +53,7 @@ class EditNodeForm extends QuickForm if ($this->isService()) { $monitoredNodeType = 'service'; } else { - $monitoredNodeType = 'host'; + $monitoredNodeType = 'host'; } $type = $this->selectNodeType($monitoredNodeType); @@ -74,20 +74,10 @@ class EditNodeForm extends QuickForm $this->setSubmitLabel($this->translate('Next')); return; } -/* - $this->getElement('name')->setValue($this->getNode()->getName()); - if ($node->hasAlias()) { - $this->getElement('alias')->setValue($this->getNode()->getAlias()); - } - $this->getElement('operator')->setValue($this->getNode()->getOperator()); - $this->getElement('display')->setValue($this->getNode()->getDisplay()); - if ($node->hasInfoUrl()) { - $this->getElement('url')->setValue($this->getNode()->getInfoUrl()); - } -*/ } - protected function isService() { + protected function isService() + { if (strpos($this->getNode()->getName(), ';Hoststatus')) { return false; } From 9cfe95df60d604087153a76f719d4da9a8553974 Mon Sep 17 00:00:00 2001 From: Jennifer Mourek Date: Mon, 2 Jul 2018 12:50:29 +0200 Subject: [PATCH 4/6] Remove option 'DEG' --- application/forms/EditNodeForm.php | 1 - 1 file changed, 1 deletion(-) diff --git a/application/forms/EditNodeForm.php b/application/forms/EditNodeForm.php index ba126b5..41e71c9 100644 --- a/application/forms/EditNodeForm.php +++ b/application/forms/EditNodeForm.php @@ -110,7 +110,6 @@ class EditNodeForm extends QuickForm '&' => $this->translate('AND'), '|' => $this->translate('OR'), '!' => $this->translate('NOT'), - '<' => $this->translate('DEG'), '1' => $this->translate('MIN 1'), '2' => $this->translate('MIN 2'), '3' => $this->translate('MIN 3'), From f4a3fcc81b17fe99bd63b476579826845d102e12 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 31 Jul 2018 12:49:13 +0200 Subject: [PATCH 5/6] Close action form after successful submission --- application/controllers/ProcessController.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 52bb7a2..1c61743 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -213,12 +213,14 @@ class ProcessController extends Controller if ($action === 'add' && $canEdit) { $form = $this->loadForm('AddNode') + ->setSuccessUrl(Url::fromRequest()->without('action')) ->setProcess($bp) ->setParentNode($node) ->setSession($this->session()) ->handleRequest(); } elseif ($action === 'editmonitored' && $canEdit) { $form = $this->loadForm('EditNode') + ->setSuccessUrl(Url::fromRequest()->without('action')) ->setProcess($bp) ->setNode($bp->getNode($this->params->get('editmonitorednode'))) ->setParentNode($node) @@ -226,6 +228,7 @@ class ProcessController extends Controller ->handleRequest(); } elseif ($action === 'delete' && $canEdit) { $form = $this->loadForm('DeleteNode') + ->setSuccessUrl(Url::fromRequest()->without('action')) ->setProcess($bp) ->setNode($bp->getNode($this->params->get('deletenode'))) ->setParentNode($node) @@ -233,12 +236,14 @@ class ProcessController extends Controller ->handleRequest(); } elseif ($action === 'edit' && $canEdit) { $form = $this->loadForm('Process') + ->setSuccessUrl(Url::fromRequest()->without('action')) ->setProcess($bp) ->setNode($bp->getNode($this->params->get('editnode'))) ->setSession($this->session()) ->handleRequest(); } elseif ($action === 'simulation') { $form = $this->loadForm('simulation') + ->setSuccessUrl(Url::fromRequest()->without('action')) ->setNode($bp->getNode($this->params->get('simulationnode'))) ->setSimulation(Simulation::fromSession($this->session())) ->handleRequest(); From 99a66a4b4bb71ea97b061197083ca5b94d0ed2a7 Mon Sep 17 00:00:00 2001 From: Eric Lippmann Date: Tue, 31 Jul 2018 12:59:49 +0200 Subject: [PATCH 6/6] Remove node before recreating it upon edit Else you may run into errors that objects have been defined twice. --- application/forms/EditNodeForm.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/application/forms/EditNodeForm.php b/application/forms/EditNodeForm.php index 41e71c9..8ac4a52 100644 --- a/application/forms/EditNodeForm.php +++ b/application/forms/EditNodeForm.php @@ -443,6 +443,9 @@ class EditNodeForm extends QuickForm public function onSuccess() { $changes = ProcessChanges::construct($this->bp, $this->session); + + $changes->deleteNode($this->node, $this->parent->getName()); + switch ($this->getValue('node_type')) { case 'host': case 'service': @@ -459,8 +462,6 @@ class EditNodeForm extends QuickForm break; } - $changes->deleteNode($this->node, $this->parent->getName()); - // Trigger session destruction to make sure it get's stored. // TODO: figure out why this is necessary, might be an unclean shutdown on redirect unset($changes);