From 5ce4346cbdb4d255183be96f81b1e34313060495 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 17 Nov 2015 13:10:30 +0100 Subject: [PATCH 001/256] Revert "Implement overlay as a link target" This reverts commit ef949e3adb4cb9e7dd654ab88961ea3502de9df2. --- application/controllers/NodeController.php | 26 +++++++--- public/css/module.less | 57 ---------------------- public/js/module.js | 50 ++----------------- 3 files changed, 22 insertions(+), 111 deletions(-) diff --git a/application/controllers/NodeController.php b/application/controllers/NodeController.php index 31596f8..a00944b 100644 --- a/application/controllers/NodeController.php +++ b/application/controllers/NodeController.php @@ -3,6 +3,7 @@ use Icinga\Module\Businessprocess\Controller; use Icinga\Module\Businessprocess\Simulation; use Icinga\Module\Businessprocess\Forms\ProcessForm; +use Icinga\Module\Businessprocess\Forms\SimulationForm; use Icinga\Web\Url; /* @@ -45,16 +46,27 @@ class Businessprocess_NodeController extends Controller { $bp = $this->loadBpConfig(); $nodename = $this->getParam('node'); - $node = $this->view->node = $bp->getNode($nodename); + $node = $bp->getNode($nodename); + $details = Url::fromPath( + 'businessprocess/node/simulate', + array( + 'config' => $this->view->configName, + 'node' => $nodename + ) + ); + $url = sprintf( + 'businessprocess/process/show?unlocked&config=%s#!%s', + $bp->getName(), + $details->getAbsoluteUrl() + ); - $this->view->form = $this->loadForm('simulation') - ->setNode($node) + $this->view->form = SimulationForm::construct() ->setSimulation(new Simulation($bp, $this->session())) + ->setNode($node) + // TODO: find a better way to handle redirects + ->setRedirectUrl($url) ->handleRequest(); - - if ($this->view->form->succeeded()) { - $this->render('empty'); - } + $this->view->node = $node; } public function addAction() diff --git a/public/css/module.less b/public/css/module.less index 1abba0d..4c052a7 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -524,60 +524,3 @@ table.sourcecode { margin-right: 1px; } } - -#bp-overlay-container { - display: none; - position: absolute; - background-color: rgba(250, 250, 255, 0.98); - border: 0.25em solid @icinga-blue; - z-index: 3000; - top: 10%; - left: 20%; - right: 20%; - height: 40em; - max-width: 60em; - - .bp-overlay-controls { - background-color: @icinga-blue; - color: white; - height: 2em; - - a { - float: right; - line-height: 2em; - margin-right: 1em; - } - } -} - -#layout.minimal-layout #bp-overlay-container { - position: fixed; - left: 0; - right: 0; - top: 0; - bottom: 0; - width: auto; - height: auto; - border: none; - overflow: auto; - - #bp-overlay { - height: auto; - } -} - -#layout.poor-layout #bp-overlay-container { - left: 2%; - right: 2%; -} - -#layout.compact-layout #bp-overlay-container { - left: 5%; - right: 5%; -} - -#bp-overlay { - padding: 1em; - overflow: auto; - height: 37.5em; -} diff --git a/public/js/module.js b/public/js/module.js index 0fe40cd..a612aa3 100644 --- a/public/js/module.js +++ b/public/js/module.js @@ -22,44 +22,19 @@ * Tell Icinga about our event handlers */ this.module.on('beforerender', this.rememberOpenedBps); - this.module.on('rendered', this.rendered); + this.module.on('rendered', this.fixOpenedBps); 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('click', '.bp-overlay-controls a', this.closeOverlay); - this.module.on('click', 'a', this.checkOverlay); 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); - if ($('#bp-overlay').length < 1) { - $('#layout').append('
'); - } this.module.icinga.logger.debug('BP module loaded'); }, - closeOverlay: function(event) { - $('#bp-overlay-container').hide(); - $('#bp-overlay').html(''); - }, - - checkOverlay: function(event) { - $el = $(event.currentTarget); - $sourceContainer = $el.closest('.container'); - $target = $el.closest('[data-base-target]'); - if ($target.length < 1) { return; } - - targetId = $target.data('baseTarget'); - if ($target.data('baseTarget') !== 'bp-overlay') { return; } - - $('#bp-overlay').data('sourceContainer', $sourceContainer.attr('id')); - $('#bp-overlay-container').css({ - 'display': 'block' - }); - }, - processTitleClick: function (event) { event.stopPropagation(); var $el = $(event.currentTarget).closest('table.bp'); @@ -158,22 +133,15 @@ });*/ }, - rendered: function(event) { + fixOpenedBps: function(event) { var $container = $(event.currentTarget); - if ($container.attr('id') === 'bp-overlay') { - this.onOverlayRendered(); - } - this.fixOpenedBps($container); - }, - - fixOpenedBps: function($container) { var container_id = $container.attr('id'); if (typeof this.idCache[container_id] === 'undefined') { return; } var $procs = $('table.process', $container); - $.each(this.idCache[container_id], function(idx, id) { + $.each(this.idCache[$container.attr('id')], function(idx, id) { var $el = $('#' + id); $procs = $procs.not($el); @@ -186,18 +154,6 @@ $procs.addClass('collapsed'); }, - onOverlayRendered: function() - { - // close overlay if required: - $overlay = $('#bp-overlay'); - $overlayContainer = $('#bp-overlay-container'); - if ($overlayContainer.css('display') === 'block' && $overlay.html().match(/^__CLOSEME__/)) { - $source = $('#' + $overlay.data('sourceContainer')); - $overlayContainer.hide(); - self.icinga.loader.loadUrl($source.data('icingaUrl'), $source, undefined, undefined, undefined, true); - } - }, - /** * Get a list of all currently opened BPs. * From 6fac82b3f94fdeecebff07c795a4826bdfc6a79a Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 17 Nov 2015 14:00:02 +0100 Subject: [PATCH 002/256] ProcessForm: create top level procs per default This is less confusing for beginners --- application/forms/ProcessForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/forms/ProcessForm.php b/application/forms/ProcessForm.php index 2fda9fd..a238de9 100644 --- a/application/forms/ProcessForm.php +++ b/application/forms/ProcessForm.php @@ -67,8 +67,8 @@ class ProcessForm extends Form 'Where to show this process' ), 'multiOptions' => array( - '0' => $this->translate('Subprocess only'), '1' => $this->translate('Toplevel Process'), + '0' => $this->translate('Subprocess only'), ) )); From c8e29575bde29254323ccd4e7d91137f9f7a0212 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 17 Nov 2015 14:20:26 +0100 Subject: [PATCH 003/256] ProcessController: rename method --- application/controllers/ProcessController.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index d1da782..1a24491 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -41,7 +41,7 @@ class Businessprocess_ProcessController extends Controller */ public function showAction() { - $this->redirectIfConfigChosen(); + $this->redirectOnConfigSwitch(); if ($this->params->get('unlocked')) { $bp = $this->loadModifiedBpConfig(); @@ -109,7 +109,7 @@ class Businessprocess_ProcessController extends Controller */ public function toplevelAction() { - $this->redirectIfConfigChosen(); + $this->redirectOnConfigSwitch(); } /** @@ -196,7 +196,7 @@ class Businessprocess_ProcessController extends Controller * Redirect to our URL plus the chosen config if someone switched the * config in the appropriate dropdown list */ - protected function redirectIfConfigChosen() + protected function redirectOnConfigSwitch() { $request = $this->getRequest(); if ($request->isPost()) { From ba5e0964bb4061807d845b7ed04987c6732e206f Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 17 Nov 2015 14:21:59 +0100 Subject: [PATCH 004/256] controllers: use namespaces --- application/controllers/IndexController.php | 4 +++- application/controllers/NodeController.php | 4 +++- application/controllers/ProcessController.php | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/application/controllers/IndexController.php b/application/controllers/IndexController.php index 9db9841..e8d92ab 100644 --- a/application/controllers/IndexController.php +++ b/application/controllers/IndexController.php @@ -1,8 +1,10 @@ process = */ -class Businessprocess_NodeController extends Controller +class NodeController extends Controller { // rename to config public function editAction() diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 1a24491..2c26892 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -1,5 +1,7 @@ Date: Tue, 17 Nov 2015 14:25:19 +0100 Subject: [PATCH 005/256] process/toplevel: remove legacy action --- application/controllers/ProcessController.php | 8 -------- 1 file changed, 8 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 2c26892..dd56855 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -106,14 +106,6 @@ class ProcessController extends Controller } } - /** - * Show a business process from a toplevel perspective - */ - public function toplevelAction() - { - $this->redirectOnConfigSwitch(); - } - /** * Show the source code for a process */ From 7fcef19c6ebc22e9e91c6ddd23ad5c833eb581f3 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 17 Nov 2015 14:46:50 +0100 Subject: [PATCH 006/256] permissions: provide a few --- application/controllers/ProcessController.php | 2 ++ application/views/scripts/process/show.phtml | 2 -- configuration.php | 2 ++ library/Businessprocess/Controller.php | 13 ++++++++++++- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index dd56855..dbbd82d 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -20,6 +20,8 @@ class ProcessController extends Controller */ public function createAction() { + $this->assertPermission('businessprocess/create'); + $this->setTitle($this->translate('Create a new business process')); $this->tabsForCreate()->activate('create'); diff --git a/application/views/scripts/process/show.phtml b/application/views/scripts/process/show.phtml index c3bf5eb..4ee8336 100644 --- a/application/views/scripts/process/show.phtml +++ b/application/views/scripts/process/show.phtml @@ -19,8 +19,6 @@ use Icinga\Module\Businessprocess\BusinessProcess; icon('wrench') ?> -icon('plus') ?> - diff --git a/configuration.php b/configuration.php index a94fedc..6d4b89a 100644 --- a/configuration.php +++ b/configuration.php @@ -5,3 +5,5 @@ $section = $this->menuSection(N_('Overview')) ->setPriority(45) ->setUrl('businessprocess'); +$this->providePermission('businessprocess/create', 'Allow to create new configs'); +$this->providePermission('businessprocess/modify', 'Allow to modify processes'); diff --git a/library/Businessprocess/Controller.php b/library/Businessprocess/Controller.php index d961dd9..5994f7c 100644 --- a/library/Businessprocess/Controller.php +++ b/library/Businessprocess/Controller.php @@ -85,7 +85,14 @@ class Controller extends ModuleController protected function loadBpConfig() { $storage = $this->storage(); - $this->view->processList = $storage->listProcesses(); + if ($this->hasPermission('businessprocess/create')) { + $this->view->processList = array_merge( + $storage->listProcesses(), + array('.new' => $this->translate('Create new configuration')) + ); + } else { + $this->view->processList = $storage->listProcesses(); + } // No process found? Go to welcome page if (empty($this->view->processList)) { @@ -97,6 +104,10 @@ class Controller extends ModuleController key($this->view->processList) ); + if ($name === '.new') { + $this->redirectNow('businessprocess/process/create'); + } + $modifications = $this->session()->get('modifications', array()); if (array_key_exists($name, $modifications)) { $bp = $storage->loadFromString($name, $modifications[$name]); From 98241dd9cde8f0b04a4b69d739d0082ce7a3566d Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 17 Nov 2015 15:44:29 +0100 Subject: [PATCH 007/256] QuickForm: a couple of small changes --- .../Businessprocess/Web/Form/QuickForm.php | 72 ++++++++++++------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/library/Businessprocess/Web/Form/QuickForm.php b/library/Businessprocess/Web/Form/QuickForm.php index 0a5cd19..861a4db 100644 --- a/library/Businessprocess/Web/Form/QuickForm.php +++ b/library/Businessprocess/Web/Form/QuickForm.php @@ -8,6 +8,7 @@ use Icinga\Exception\ProgrammingError; use Icinga\Web\Notification; use Icinga\Web\Request; use Icinga\Web\Url; +use Exception; use Zend_Form; /** @@ -46,8 +47,6 @@ abstract class QuickForm extends Zend_Form protected $successUrl; - protected $succeeded; - protected $successMessage; protected $submitLabel; @@ -76,6 +75,14 @@ abstract class QuickForm extends Zend_Form $this->setAction(Url::fromRequest()); $this->createIdElement(); $this->regenerateCsrfToken(); + $this->setDecorators( + array( + 'Description', + array('FormErrors', array('onlyCustomFormErrors' => true)), + 'FormElements', + 'Form' + ) + ); } protected function handleOptions($options = null) @@ -96,20 +103,22 @@ abstract class QuickForm extends Zend_Form protected function addSubmitButtonIfSet() { if (false !== ($label = $this->getSubmitLabel())) { - $el = $this->createElement('submit', $label)->setLabel($label)->removeDecorator('Label'); + $el = $this->createElement('submit', $label)->setLabel($label)->setDecorators(array('ViewHelper')); $this->submitButtonName = $el->getName(); $this->addElement($el); } - } - // TODO: This is ugly, we need to defer button creation - protected function moveSubmitToBottom() - { - $name = $this->submitButtonName; - if ($name && ($submit = $this->getElement($name))) { - $this->removeElement($name); - $this->addElement($submit); - } + $grp = array( + $this->submitButtonName, + $this->deleteButtonName + ); + $this->addDisplayGroup($grp, 'buttons', array( + 'decorators' => array( + 'FormElements', + 'DtDdWrapper', + ), + 'order' => 1000, + )); } protected function createIdElement() @@ -119,11 +128,10 @@ abstract class QuickForm extends Zend_Form $this->getElement(self::ID)->setIgnore(true); } - protected function getSentValue($name, $default = null) + public function getSentValue($name, $default = null) { $request = $this->getRequest(); - - if ($request->isPost()) { + if ($request->isPost() && $this->hasBeenSent()) { return $request->getPost($name); } else { return $default; @@ -208,17 +216,28 @@ abstract class QuickForm extends Zend_Form ) + $enum; } - public function succeeded() - { - return $this->succeeded; - } - - public function setSuccessUrl($url) + public function setSuccessUrl($url, $params = null) { + if (! $url instanceof Url) { + $url = Url::fromPath($url); + } + if ($params !== null) { + $url->setParams($params); + } $this->successUrl = $url; return $this; } + public function getSuccessUrl() + { + $url = $this->successUrl ?: $this->getAction(); + if (! $url instanceof Url) { + $url = Url::fromPath($url); + } + + return $url; + } + public function setup() { } @@ -292,11 +311,14 @@ abstract class QuickForm extends Zend_Form if ($this->hasBeenSubmitted()) { $this->beforeValidation($post); if ($this->isValid($post)) { - $this->onSuccess(); - $this->succeeded = true; + try { + $this->onSuccess(); + } catch (Exception $e) { + $this->addError($e->getMessage()); + $this->onFailure(); + } } else { $this->onFailure(); - $this->succeeded = false; } } else { $this->setDefaults($post); @@ -341,7 +363,7 @@ abstract class QuickForm extends Zend_Form public function redirectOnSuccess($message = null) { - $url = $this->successUrl ?: $this->getAction(); + $url = $this->getSuccessUrl(); $this->notifySuccess($this->getSuccessMessage($message)); $this->redirectAndExit($url); } From 4f2ae8a53297d248bdaf525e82a5176aa93b72cb Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 17 Nov 2015 15:45:13 +0100 Subject: [PATCH 008/256] BpConfigForm: refactor based on QuickForm --- application/forms/BpConfigForm.php | 101 ++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 29 deletions(-) diff --git a/application/forms/BpConfigForm.php b/application/forms/BpConfigForm.php index 2e06262..b22bd23 100644 --- a/application/forms/BpConfigForm.php +++ b/application/forms/BpConfigForm.php @@ -4,12 +4,12 @@ namespace Icinga\Module\Businessprocess\Forms; use Icinga\Application\Config; use Icinga\Module\Businessprocess\BusinessProcess; -use Icinga\Module\Businessprocess\Form; +use Icinga\Module\Businessprocess\Web\Form\QuickForm; use Icinga\Web\Notification; use Icinga\Web\Request; use Icinga\Web\Url; -class BpConfigForm extends Form +class BpConfigForm extends QuickForm { protected $storage; @@ -23,8 +23,11 @@ class BpConfigForm extends Form protected $processList = array(); + protected $deleteButtonName; + public function setup() { + $this->addElement('text', 'name', array( 'label' => $this->translate('Name'), 'required' => true, @@ -63,7 +66,43 @@ class BpConfigForm extends Form 'soft' => $this->translate('Use SOFT states'), ) )); - $this->addElement('submit', $this->translate('Store')); + + if ($this->config === null) { + $this->setSubmitLabel( + $this->translate('Add') + ); + } else { + $config = $this->config; + + $this->getElement('name') + ->setValue($config->getName()) + ->setAttrib('readonly', true); + + if ($config->hasTitle()) { + $this->getElement('title')->setValue($config->getTitle()); + } + + if ($config->hasBackend()) { + $this->getElement('backend_name')->setValue( + $config->getBackend()->getName() + ); + } + if ($config->usesSoftStates()) { + $this->getElement('state_type')->setValue('soft'); + } else { + $this->getElement('state_type')->setValue('hard'); + } + + $this->setSubmitLabel( + $this->translate('Store') + ); + $label = $this->translate('Delete'); + $el = $this->createElement('submit', $label) + ->setLabel($label) + ->setDecorators(array('ViewHelper')); + $this->deleteButtonName = $el->getName(); + $this->addElement($el); + } } protected function listAvailableBackends() @@ -81,26 +120,20 @@ class BpConfigForm extends Form public function setProcessConfig($config) { $this->config = $config; - $this->getElement('name')->setValue($config->getName()); - $this->getElement('name')->setAttrib('readonly', true); - - if ($config->hasTitle()) { - $this->getElement('title')->setValue($config->getTitle()); - } - - if ($config->hasBackend()) { - $this->getElement('backend_name')->setValue( - $config->getBackend()->getName() - ); - } - - if ($config->usesSoftStates()) { - $this->getElement('state_type')->setValue('soft'); - } else { - $this->getElement('state_type')->setValue('hard'); - } - return $this; + return $this; + } + + protected function onRequest() + { + $name = $this->getValue('name'); + + if ($this->shouldBeDeleted()) { + $this->config->clearAppliedChanges(); + $this->storage->deleteProcess($name); + $this->setSuccessUrl('businessprocess'); + $this->redirectOnSuccess(sprintf('Process %s has been deleted', $name)); + } } public function onSuccess() @@ -126,17 +159,14 @@ class BpConfigForm extends Form } $this->storage->storeProcess($config); $config->clearAppliedChanges(); - $this->setRedirectUrl( - $this->getRedirectUrl()->setParams( + $this->setSuccessUrl( + $this->getSuccessUrl()->setParams( array('config' => $name, 'unlocked' => true) ) ); - Notification::success(sprintf('Process %s has been created', $name)); + $this->redirectOnSuccess(sprintf('Process %s has been created', $name)); } else { - // Existing config - $config = $this->config; - if ($title) { $config->setTitle($title); } @@ -151,8 +181,21 @@ class BpConfigForm extends Form $this->storage->storeProcess($config); $config->clearAppliedChanges(); - $this->getRedirectUrl()->setParam('config', $name); + $this->getSuccessUrl()->setParam('config', $name); Notification::success(sprintf('Process %s has been stored', $name)); } } + + public function hasDeleteButton() + { + return $this->deleteButtonName !== null; + } + + public function shouldBeDeleted() + { + if (! $this->hasDeleteButton()) return false; + + $name = $this->deleteButtonName; + return $this->getSentValue($name) === $this->getElement($name)->getLabel(); + } } From 4add34aca6ef72ab8a709d3af31507a25d77dfa1 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 17 Nov 2015 15:46:55 +0100 Subject: [PATCH 009/256] ProcessController: simplify form-related code --- application/controllers/ProcessController.php | 22 +++++++------------ 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index dbbd82d..3f62c34 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -25,9 +25,9 @@ class ProcessController extends Controller $this->setTitle($this->translate('Create a new business process')); $this->tabsForCreate()->activate('create'); - $this->view->form = BpConfigForm::construct() + $this->view->form = $this->loadForm('bpConfig') ->setStorage($this->storage()) - ->setRedirectUrl('businessprocess/process/show') + ->setSuccessUrl('businessprocess/process/show') ->handleRequest(); } @@ -170,21 +170,15 @@ class ProcessController extends Controller $bp->getTitle() ); - $url = sprintf( - 'businessprocess/process/show?config=%s&unlocked#!%s', - $bp->getName(), - $this->getRequest()->getUrl() + $url = Url::fromPath( + 'businessprocess/process/show?unlocked', + array('config' => $bp->getName()) ); - $this->view->form = BpConfigForm::construct() + + $this->view->form = $this->loadForm('bpConfig') ->setProcessConfig($bp) ->setStorage($this->storage()) - ->setRedirectUrl($url) - ->handleRequest(); - - $this->view->deleteForm = DeleteConfigForm::construct() - ->setStorage($this->storage()) - ->setController($this) - ->setBpConfig($bp) + ->setSuccessUrl($url) ->handleRequest(); } From d057567247fc11db4af375ecc641bbbc0ef3ba4a Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 17 Nov 2015 15:47:29 +0100 Subject: [PATCH 010/256] Controller: provide $bp if loaded --- library/Businessprocess/Controller.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/Businessprocess/Controller.php b/library/Businessprocess/Controller.php index 5994f7c..cbff42b 100644 --- a/library/Businessprocess/Controller.php +++ b/library/Businessprocess/Controller.php @@ -21,6 +21,8 @@ class Controller extends ModuleController protected $backend; + protected $bp; + private $storage; private $url; @@ -125,7 +127,7 @@ class Controller extends ModuleController } } - $this->view->bpconfig = $bp; + $this->view->bpconfig = $this->bp = $bp; $this->view->configName = $bp->getName(); return $bp; From 1c54d8be2ca829783be63f8aaef65183538b07ef Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 17 Nov 2015 15:49:24 +0100 Subject: [PATCH 011/256] CreateConfigForm: remove obsolete file --- application/forms/_CreateConfigForm.php | 101 ------------------------ 1 file changed, 101 deletions(-) delete mode 100644 application/forms/_CreateConfigForm.php diff --git a/application/forms/_CreateConfigForm.php b/application/forms/_CreateConfigForm.php deleted file mode 100644 index 4182833..0000000 --- a/application/forms/_CreateConfigForm.php +++ /dev/null @@ -1,101 +0,0 @@ -addElement('text', 'name', array( - 'label' => $this->translate('Name'), - 'required' => true, - 'description' => $this->translate('This is the unique identifier of this process'), - )); - - $this->addElement('text', 'title', array( - 'label' => $this->translate('Title'), - 'description' => $this->translate('Usually this title will be shown for this process. Equals name if not given'), - )); - - $this->addElement('select', 'backend_name', array( - 'label' => $this->translate('Backend'), - 'required' => true, - 'description' => $this->translate('Icinga Web Monitoring Backend where current object states for this process should be retrieved from'), - 'multiOptions' => array( - null => $this->translate('Use current default backend'), - ) + $this->listAvailableBackends() - )); - - $this->addElement('select', 'state_type', array( - 'label' => $this->translate('State Type'), - 'required' => true, - 'description' => $this->translate('Whether this process should be based on Icinga hard or soft states'), - 'multiOptions' => array( - 'hard' => $this->translate('Use HARD states'), - 'soft' => $this->translate('Use SOFT states'), - ) - )); - $this->addElement('submit', $this->translate('Store')); - } - - protected function listAvailableBackends() - { - $keys = array_keys(Config::module('monitoring', 'backends')->toArray()); - return array_combine($keys, $keys); - } - - public function setBackend($backend) - { - $this->backend = $backend; - return $this; - } - - public function setProcessConfig($config) - { - $this->process = $config; - $this->getElement('name')->setValue($config->getName()); - - if ($config->hasTitle()) { - $this->getElement('title')->setValue($config->getTitle()); - } - - if ($config->hasBackend()) { - $this->getElement('backend_name')->setValue($config->getBackend()->getName()); - } - - if ($config->usesSoftStates()) { - $this->getElement('state_type')->setValue('soft'); - } else { - $this->getElement('state_type')->setValue('hard'); - } - - return $this; - } - - public function setSession($session) - { - $this->session = $session; - return $this; - } - - public function onSuccess() - { -/* - $storage->storeProcess($bp); - $modifications = $this->session->get('modifications', array()); - $node = $this->process->getNode($this->getValue('name')); - $node->setChildNames($this->getValue('children')); - $node->setOperator($this->getValue('operator')); - $modifications[$this->process->getName()] = $this->process->toLegacyConfigString(); - $this->session->set('modifications', $modifications); - $message = 'Process %s has been modified'; - Notification::success(sprintf($message, $this->process->getName())); -*/ - } -} From 450cfc0403abeba3428520242bae2caac97b24ce Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 17 Nov 2015 15:49:47 +0100 Subject: [PATCH 012/256] DeleteConfigForm: this is no longer needed --- application/forms/DeleteConfigForm.php | 46 -------------------------- 1 file changed, 46 deletions(-) delete mode 100644 application/forms/DeleteConfigForm.php diff --git a/application/forms/DeleteConfigForm.php b/application/forms/DeleteConfigForm.php deleted file mode 100644 index 02f5dc4..0000000 --- a/application/forms/DeleteConfigForm.php +++ /dev/null @@ -1,46 +0,0 @@ -addHidden('name'); - $this->addElement('submit', $this->translate('Delete this process')); - } - - public function setStorage($storage) - { - $this->storage = $storage; - return $this; - } - - public function setController($controller) - { - $this->controller = $controller; - return $this; - } - - public function setBpConfig($bp) - { - $this->getElement('name')->setValue($bp->getName()); - return $this; - } - - public function onSuccess() - { - $name = $this->getValue('name'); - $this->storage->deleteProcess($name); - $this->setRedirectUrl('businessprocess'); - Notification::success(sprintf('Process %s has been deleted', $name)); - } -} From fd3c577cb4ed35f0e299dde71045e74c9b37cde6 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 17 Nov 2015 15:52:19 +0100 Subject: [PATCH 013/256] NodeController: benefit from simpler forms --- application/controllers/NodeController.php | 47 +++++++--------------- 1 file changed, 14 insertions(+), 33 deletions(-) diff --git a/application/controllers/NodeController.php b/application/controllers/NodeController.php index 54944e1..54d619d 100644 --- a/application/controllers/NodeController.php +++ b/application/controllers/NodeController.php @@ -4,8 +4,6 @@ namespace Icinga\Module\Businessprocess\Controllers; use Icinga\Module\Businessprocess\Controller; use Icinga\Module\Businessprocess\Simulation; -use Icinga\Module\Businessprocess\Forms\ProcessForm; -use Icinga\Module\Businessprocess\Forms\SimulationForm; use Icinga\Web\Url; /* @@ -20,25 +18,16 @@ class NodeController extends Controller { $bp = $this->loadModifiedBpConfig(); $node = $bp->getNode($this->getParam('node')); - $detail = Url::fromPath( - 'businessprocess/node/edit', - array( - 'config' => $this->view->configName, - 'node' => $node - ) + $url = Url::fromPath( + 'businessprocess/process/show?unlocked', + array('config' => $bp->getName()) ); - $this->view->form = ProcessForm::construct() + $this->view->form = $this->loadForm('process') ->setProcess($bp) ->setSession($this->session()) ->setNode($node) - ->setRedirectUrl( - sprintf( - 'businessprocess/process/show?config=%s&unlocked#!%s', - $bp->getName(), - $detail->getAbsoluteUrl() - ) - ) + ->setSuccessUrl($url) ->handleRequest(); $this->view->node = $node; @@ -49,25 +38,17 @@ class NodeController extends Controller $bp = $this->loadBpConfig(); $nodename = $this->getParam('node'); $node = $bp->getNode($nodename); - $details = Url::fromPath( - 'businessprocess/node/simulate', - array( - 'config' => $this->view->configName, - 'node' => $nodename - ) - ); - $url = sprintf( - 'businessprocess/process/show?unlocked&config=%s#!%s', - $bp->getName(), - $details->getAbsoluteUrl() + $url = Url::fromPath( + 'businessprocess/process/show?unlocked', + array('config' => $bp->getName()) ); - $this->view->form = SimulationForm::construct() + $this->view->form = $this->loadForm('simulation') ->setSimulation(new Simulation($bp, $this->session())) ->setNode($node) - // TODO: find a better way to handle redirects - ->setRedirectUrl($url) + ->setSuccessUrl($url) ->handleRequest(); + $this->view->node = $node; } @@ -75,15 +56,15 @@ class NodeController extends Controller { $bp = $this->loadBpConfig(); - $redirectUrl = Url::fromPath( + $url = Url::fromPath( 'businessprocess/process/show', array('config' => $bp->getName()) ); - $this->view->form = ProcessForm::construct() + $this->view->form = $this->loadForm('process') ->setProcess($bp) ->setSession($this->session()) - ->setRedirectUrl($redirectUrl) + ->setRedirectUrl($url) ->handleRequest(); } From 91fca695662bf1a07ac618b99cbae2c04d79805c Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 20 Nov 2015 12:35:09 +0100 Subject: [PATCH 014/256] LegacyStorage: permission check for process lists --- .../Businessprocess/Storage/LegacyStorage.php | 76 +++++++++++++++++-- 1 file changed, 71 insertions(+), 5 deletions(-) diff --git a/library/Businessprocess/Storage/LegacyStorage.php b/library/Businessprocess/Storage/LegacyStorage.php index 0198e40..288537b 100644 --- a/library/Businessprocess/Storage/LegacyStorage.php +++ b/library/Businessprocess/Storage/LegacyStorage.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Businessprocess\Storage; use Icinga\Application\Icinga; +use Icinga\Authentication\Auth; use Icinga\Data\ConfigObject; use Icinga\Exception\ConfigurationError; use Icinga\Module\Businessprocess\BusinessProcess; @@ -56,12 +57,17 @@ class LegacyStorage extends Storage public function listProcesses() { $files = array(); + foreach (new DirectoryIterator($this->getConfigDir()) as $file) { if($file->isDot()) continue; $filename = $file->getFilename(); if (substr($filename, -5) === '.conf') { $name = substr($filename, 0, -5); $header = $this->readHeader($file->getPathname(), $name); + if (! $this->headerPermissionsAreSatisfied($header)) { + continue; + } + if ($header['Title'] === null) { $files[$name] = $name; } else { @@ -74,16 +80,76 @@ class LegacyStorage extends Storage return $files; } + protected function headerPermissionsAreSatisfied($header) + { + if (Icinga::app()->isCli()) { + return true; + } + + if ( + $header['Allowed users'] === null + && $header['Allowed groups'] === null + && $header['Allowed roles'] === null + ) { + return true; + } + + $auth = Auth::getInstance(); + if (! $auth->isAuthenticated()) { + return false; + } + + $user = $auth->getUser(); + $username = $user->getUsername(); + + if ($header['Owner'] === $username) { + return true; + } + + if ($header['Allowed users'] !== null) { + $users = $this->splitCommaSeparated($header['Allowed users']); + foreach ($users as $allowedUser) { + if ($username === $allowedUser) { + return true; + } + } + } + + if ($header['Allowed groups'] !== null) { + $groups = $this->splitCommaSeparated($header['Allowed groups']); + foreach ($groups as $group) { + if ($user->isMemberOf($group)) { + return true; + } + } + } + + if ($header['Allowed roles'] !== null) { + // TODO: not implemented yet + return false; + } + + return false; + } + + protected function splitCommaSeparated($string) + { + return preg_split('/\s*,\s*/', $string, -1, PREG_SPLIT_NO_EMPTY); + } + protected function readHeader($file, $name) { $fh = fopen($file, 'r'); $cnt = 0; $header = array( - 'Title' => null, - 'Owner' => null, - 'Backend' => null, - 'Statetype' => 'soft', - 'SLA Hosts' => null + 'Title' => null, + 'Owner' => null, + 'Allowed users' => null, + 'Allowed groups' => null, + 'Allowed roles' => null, + 'Backend' => null, + 'Statetype' => 'soft', + 'SLA Hosts' => null ); while ($cnt < 15 && false !== ($line = fgets($fh))) { $cnt++; From 3efd53b97a5ccf7e5644abec5a9c4921e04a0853 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 23 Nov 2015 00:42:22 +0100 Subject: [PATCH 015/256] LegacyStorage: add missing fclose --- library/Businessprocess/Storage/LegacyStorage.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/Businessprocess/Storage/LegacyStorage.php b/library/Businessprocess/Storage/LegacyStorage.php index 288537b..fc2bf18 100644 --- a/library/Businessprocess/Storage/LegacyStorage.php +++ b/library/Businessprocess/Storage/LegacyStorage.php @@ -159,6 +159,8 @@ class LegacyStorage extends Storage } } } + + fclose($fh); return $header; } From 3332a06792c7310463c7bf6ad504782c967784e7 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 23 Nov 2015 00:43:17 +0100 Subject: [PATCH 016/256] LegacyStorage: add hasProcess, respecting perms --- library/Businessprocess/Storage/LegacyStorage.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/library/Businessprocess/Storage/LegacyStorage.php b/library/Businessprocess/Storage/LegacyStorage.php index fc2bf18..146bdb4 100644 --- a/library/Businessprocess/Storage/LegacyStorage.php +++ b/library/Businessprocess/Storage/LegacyStorage.php @@ -214,6 +214,18 @@ class LegacyStorage extends Storage return $bp; } + public function hasProcess($name) + { + $file = $this->getFilename($name); + if (! is_file($file)) { + return false; + } + + $name = substr($file, 0, -5); + $header = $this->readHeader($file, $name); + return $this->headerPermissionsAreSatisfied($header); + } + protected function loadHeader($name, $bp) { // TODO: do not open twice, this is quick and dirty based on existing code From 311fd54552ac77a90b6af5b3203c2a4f4b31b41f Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 23 Nov 2015 00:44:14 +0100 Subject: [PATCH 017/256] Controller: 404 on missing process or perms --- library/Businessprocess/Controller.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/Businessprocess/Controller.php b/library/Businessprocess/Controller.php index cbff42b..8c0023c 100644 --- a/library/Businessprocess/Controller.php +++ b/library/Businessprocess/Controller.php @@ -110,6 +110,13 @@ class Controller extends ModuleController $this->redirectNow('businessprocess/process/create'); } + if (! $storage->hasProcess($name)) { + $this->httpNotFound( + $this->translate('No such process config: "%s"'), + $name + ); + } + $modifications = $this->session()->get('modifications', array()); if (array_key_exists($name, $modifications)) { $bp = $storage->loadFromString($name, $modifications[$name]); From f25f1418200bd0ca0709b39a238512faaeba7c10 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 23 Nov 2015 00:46:18 +0100 Subject: [PATCH 018/256] LegacyStorage: be more conservative on pointers --- library/Businessprocess/Storage/LegacyStorage.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/library/Businessprocess/Storage/LegacyStorage.php b/library/Businessprocess/Storage/LegacyStorage.php index 146bdb4..8d9761b 100644 --- a/library/Businessprocess/Storage/LegacyStorage.php +++ b/library/Businessprocess/Storage/LegacyStorage.php @@ -332,8 +332,9 @@ class LegacyStorage extends Storage $value = $m[2]; } $cmps = preg_split('~\s*\\' . $op . '\s*~', $value, -1, PREG_SPLIT_NO_EMPTY); + $childNames = array(); - foreach ($cmps as & $val) { + foreach ($cmps as $val) { if (strpos($val, ';') !== false) { if ($bp->hasNode($val)) continue; @@ -349,13 +350,16 @@ class LegacyStorage extends Storage $bp->createImportedNode($config, $nodeName); $val = $nodeName; } + + $childNames[] = $val; } $node = new BpNode($bp, (object) array( 'name' => $name, 'operator' => $op_name, - 'child_names' => $cmps + 'child_names' => $childNames )); + $bp->addNode($name, $node); } From 1e145f127f76f12e5aa75c0121038510d9be32e1 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 23 Nov 2015 12:59:20 +0100 Subject: [PATCH 019/256] css/module: do not reduce font size, web2 does so NB: font size after third level will still be slightly reduced --- public/css/module.less | 5 ----- 1 file changed, 5 deletions(-) diff --git a/public/css/module.less b/public/css/module.less index 4c052a7..3ef8fc1 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -1,7 +1,3 @@ -/* Default font settings */ -h1 { - font-size: 1em; -} /* Normalize CSS */ @@ -37,7 +33,6 @@ h1 a:focus { } div.bp { - font-size: 0.82em; margin-bottom: 4px; } From 93fff13209ff38531afad6a802ef2e4826c9e9ce Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 23 Nov 2015 13:05:41 +0100 Subject: [PATCH 020/256] (Bp)Node: add get/hasMissingChildren method --- library/Businessprocess/BpNode.php | 20 ++++++++++++++++++++ library/Businessprocess/Node.php | 16 +++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/library/Businessprocess/BpNode.php b/library/Businessprocess/BpNode.php index d9e91c4..f413c96 100644 --- a/library/Businessprocess/BpNode.php +++ b/library/Businessprocess/BpNode.php @@ -19,6 +19,7 @@ class BpNode extends Node protected $alias; protected $counters; protected $missing = null; + protected $missingChildren; protected static $emptyStateSummary = array( 'OK' => 0, @@ -135,6 +136,25 @@ class BpNode extends Node return $this->missing; } + public function getMissingChildren() + { + if ($this->missingChildren === null) { + foreach ($this->getChildren() as $child) { + if ($child->isMissing()) { + $missing[(string) $child] = $child; + } + + foreach ($child->getMissingChildren() as $m) { + $missing[(string) $m] = $m; + } + } + + $this->missingChildren = $missing; + } + + return $this->missingChildren; + } + public function getOperator() { return $this->operator; diff --git a/library/Businessprocess/Node.php b/library/Businessprocess/Node.php index 4133b1c..3652454 100644 --- a/library/Businessprocess/Node.php +++ b/library/Businessprocess/Node.php @@ -127,6 +127,16 @@ abstract class Node return $this->missing; } + public function hasMissingChildren() + { + return count($this->getMissingChildren() > 0); + } + + public function getMissingChildren() + { + return array(); + } + public function hasInfoUrl() { return false; @@ -334,7 +344,11 @@ abstract class Node if ($this->isMissing()) { return array('missing'); } elseif ($state === 'ok') { - return array('ok'); + if ($this->hasMissingChildren()) { + return array('ok', 'missing-children'); + } else { + return array('ok'); + } } else { return array('problem', $state); } From ac82187f5b0c57a1ca98cbb8e653104be538bce4 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 7 Apr 2016 15:48:31 +0200 Subject: [PATCH 021/256] BpNode: fix error for missing children --- library/Businessprocess/BpNode.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/Businessprocess/BpNode.php b/library/Businessprocess/BpNode.php index f413c96..f71f531 100644 --- a/library/Businessprocess/BpNode.php +++ b/library/Businessprocess/BpNode.php @@ -139,6 +139,8 @@ class BpNode extends Node public function getMissingChildren() { if ($this->missingChildren === null) { + $missing = array(); + foreach ($this->getChildren() as $child) { if ($child->isMissing()) { $missing[(string) $child] = $child; From 6da0c7c722713716c7e0144d5db69655ebda5975 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 21 Jun 2016 23:53:50 +0200 Subject: [PATCH 022/256] ShipConfigFiles: stop using legacy hook --- library/Businessprocess/Director/ShipConfigFiles.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Businessprocess/Director/ShipConfigFiles.php b/library/Businessprocess/Director/ShipConfigFiles.php index 8ebf194..35019d9 100644 --- a/library/Businessprocess/Director/ShipConfigFiles.php +++ b/library/Businessprocess/Director/ShipConfigFiles.php @@ -3,7 +3,7 @@ namespace Icinga\Module\Businessprocess\Director; use Icinga\Application\Config; -use Icinga\Module\Director\Web\Hook\ShipConfigFilesHook; +use Icinga\Module\Director\Hook\ShipConfigFilesHook; use Icinga\Module\Businessprocess\Storage\LegacyStorage; class ShipConfigFiles extends ShipConfigFilesHook From 8151769592a24d4cd65bdc790332015c8cd51373 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 22 Nov 2016 15:00:09 +0100 Subject: [PATCH 023/256] Web\Component: introduce a couple of web components --- .../Web/Component/ActionBar.php | 10 ++ .../Web/Component/Attribute.php | 98 +++++++++++ .../Web/Component/Attributes.php | 158 ++++++++++++++++++ .../Web/Component/Component.php | 134 +++++++++++++++ .../Web/Component/Container.php | 144 ++++++++++++++++ .../Businessprocess/Web/Component/Link.php | 76 +++++++++ 6 files changed, 620 insertions(+) create mode 100644 library/Businessprocess/Web/Component/ActionBar.php create mode 100644 library/Businessprocess/Web/Component/Attribute.php create mode 100644 library/Businessprocess/Web/Component/Attributes.php create mode 100644 library/Businessprocess/Web/Component/Component.php create mode 100644 library/Businessprocess/Web/Component/Container.php create mode 100644 library/Businessprocess/Web/Component/Link.php diff --git a/library/Businessprocess/Web/Component/ActionBar.php b/library/Businessprocess/Web/Component/ActionBar.php new file mode 100644 index 0000000..0a71df0 --- /dev/null +++ b/library/Businessprocess/Web/Component/ActionBar.php @@ -0,0 +1,10 @@ + 'action-bar'); +} \ No newline at end of file diff --git a/library/Businessprocess/Web/Component/Attribute.php b/library/Businessprocess/Web/Component/Attribute.php new file mode 100644 index 0000000..dfabd00 --- /dev/null +++ b/library/Businessprocess/Web/Component/Attribute.php @@ -0,0 +1,98 @@ +name = $name; + $this->value = $value; + } + + /** + * @param $name + * @param $value + * @return static + */ + public static function create($name, $value) + { + return new static($name, $value); + } + + /** + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * @return string + */ + public function render() + { + return sprintf( + '%s="%s"', + $this->renderName(), + $this->renderValue() + ); + } + + /** + * @return string + */ + public function renderName() + { + return static::escapeName($this->name); + } + + /** + * @return string + */ + public function renderValue() + { + return static::escapeValue($this->value); + } + + /** + * @param $name + * @return string + */ + public static function escapeName($name) + { + // TODO: escape + return (string) $name; + } + + /** + * @param $value + * @return string + */ + public static function escapeValue($value) + { + // TODO: escape + return (string) $value; + } +} diff --git a/library/Businessprocess/Web/Component/Attributes.php b/library/Businessprocess/Web/Component/Attributes.php new file mode 100644 index 0000000..1c073b7 --- /dev/null +++ b/library/Businessprocess/Web/Component/Attributes.php @@ -0,0 +1,158 @@ +attributes = array(); + if (empty($attributes)) { + return; + } + + foreach ($attributes as $key => $value) { + if ($value instanceof Attribute) { + $this->addAttribute($value); + } elseif (is_string($key)) { + $this->add($key, $value); + } elseif (is_array($value) && count($value) === 2) { + $this->add(array_shift($value), array_shift($value)); + } + } + } + + /** + * @param Attribute[] $attributes + * @return static + */ + public static function create(array $attributes = null) + { + return new static($attributes); + } + + /** + * @param Attributes|array|null $attributes + * @return static + */ + public static function wantAttributes($attributes) + { + if ($attributes instanceof Attributes) { + return $attributes; + } else { + $self = new static(); + if (is_array($attributes)) { + foreach ($attributes as $k => $v) { + $self->add($k, $v); + } + + return $self; + + } elseif ($attributes !== null) { + throw new IcingaException( + 'Attributes, Array or Null expected, got %s', + $self->getPhpTypeName($attributes) + ); + } + return $self; + } + } + + /** + * @return Attribute[] + */ + public function attributes() + { + return $this->attributes; + } + + /** + * @param Attribute|string $attribute + * @param string|array $value + * @return $this + */ + public function add($attribute, $value = null) + { + if ($attribute instanceof static) { + foreach ($attribute as $a) { + $this->add($a); + } + + return $this; + } elseif ($attribute instanceof Attribute) { + return $this->addAttribute($attribute); + } else { + return $this->addAttribute(Attribute::create($attribute, $value)); + } + } + + /** + * @param Attribute|string $attribute + * @param string|array $value + * @return $this + */ + public function set($attribute, $value = null) + { + if ($attribute instanceof static) { + foreach ($attribute as $a) { + $this->setAttribute($a); + } + + return $this; + } else if ($attribute instanceof Attribute) { + return $this->setAttribute($attribute); + } else { + return $this->setAttribute(new Attribute($attribute, $value)); + } + } + + /** + * @param Attribute $attribute + * @return $this + */ + public function addAttribute(Attribute $attribute) + { + $name = $attribute->getName(); + if (array_key_exists($name, $this->attributes)) { + $this->attributes[$name]->addValue($attribute->getValue()); + } else { + $this->attributes[$name] = $attribute; + } + + return $this; + } + + /** + * @param Attribute $attribute + * @return $this + */ + public function setAttribute(Attribute $attribute) + { + $name = $attribute->getName(); + $this->attributes[$name] = $attribute; + return $this; + } + + /** + * @inheritdoc + */ + public function render() + { + if (empty($this->attributes)) { + return ''; + } + + return ' ' . implode(' ', $this->attributes); + } +} \ No newline at end of file diff --git a/library/Businessprocess/Web/Component/Component.php b/library/Businessprocess/Web/Component/Component.php new file mode 100644 index 0000000..fb7d873 --- /dev/null +++ b/library/Businessprocess/Web/Component/Component.php @@ -0,0 +1,134 @@ +view = $view; + return $this; + } + + /** + * @return View + */ + public function view() + { + if ($this->view === null) { + $this->view = $this->discoveredView(); + } + return $this->view; + } + + /** + * @return View + */ + protected function discoveredView() + { + if (self::$discoveredView === null) { + $viewRenderer = Icinga::app()->getViewRenderer(); + if ($viewRenderer->view === null) { + $viewRenderer->initView(); + } + + self::$discoveredView = $viewRenderer->view; + } + + return self::$discoveredView; + } + + /** + * @return string + */ + abstract function render(); + + public function wantHtml($any, $separator = '') + { + if ($any instanceof Component) { + return $any; + } elseif (is_string($any)) { + return $this->view()->escape($any); + } elseif (is_array($any)) { + $safe = array(); + foreach ($any as $el) { + $safe .= $this->wantHtml($el); + } + + return implode($separator, $safe); + } else { + // TODO: Should we add a dedicated Exception class? + throw new IcingaException( + 'String, Web Component or Array of such expected, got "%s"', + $this->getPhpTypeName($any) + ); + } + } + + public function getPhpTypeName($any) + { + if (is_object($any)) { + return get_class($any); + } else { + return gettype($any); + } + } + + /** + * @param Exception|string $error + * @return string + */ + protected function renderError($error) + { + if ($error instanceof Exception) { + $file = preg_split('/[\/\\\]/', $error->getFile(), -1, PREG_SPLIT_NO_EMPTY); + $file = array_pop($file); + $msg = sprintf( + '%s (%s:%d)', + $error->getMessage(), + $file, + $error->getLine() + ); + } elseif (is_string($error)) { + $msg = $error; + } else { + $msg = 'Got an invalid error'; + } + + + + + $view = $this->view(); + return sprintf( + $view->translate('ERROR: %s'), + $view->escape($msg) + ); + } + + /** + * @return string + */ + public function __toString() + { + try { + return $this->render(); + } catch (Exception $e) { + return $this->renderError($e); + } + } +} \ No newline at end of file diff --git a/library/Businessprocess/Web/Component/Container.php b/library/Businessprocess/Web/Component/Container.php new file mode 100644 index 0000000..b4d90d4 --- /dev/null +++ b/library/Businessprocess/Web/Component/Container.php @@ -0,0 +1,144 @@ +addContent($content); + + if ($this->attributes === null) { + $this->attributes = Attributes::wantAttributes($attributes); + } else { + $this->attributes = Attributes::wantAttributes($this->attributes); + } + if ($tag !== null) { + $this->tag = $tag; + } + } + + /** + * @param Component|array|string $content + * @param Attributes|array $attributes + * @param string $tag + * + * @return static + */ + public static function create($content = array(), $attributes = null, $tag = null) + { + return new static($content, $attributes, $tag); + } + + /** + * @return Attributes + */ + public function attributes() + { + return $this->attributes; + } + + /** + * @see addContent() + * @param Component|array|string $content + * @return $this + */ + public function add($content) + { + return $this->addContent($content); + } + + /** + * @param Component|array|string $content + * @return $this + */ + public function addContent($content) + { + if (is_array($content)) { + foreach ($content as $c) { + $this->addContent($c); + } + } + + $htmlOrComponent = $this->wantHtml($content); + if (strlen($htmlOrComponent)) { + $this->content[] = $htmlOrComponent; + } + + return $this; + } + + /** + * @param Component|array|string $content + * @return $this + */ + public function setContent($content) + { + $this->content = array(); + $this->addContent($content); + + return $this; + } + + /** + * @return string + */ + public function renderContent() + { + return implode($this->separator, $this->content); + } + + /** + * @inheritdoc + */ + public function render() + { + return $this->renderContainerFor($this->renderContent()); + } + + /** + * @inheritdoc + */ + public function renderError($error) + { + // TODO: eventually add class="error" + return $this->renderContainerFor( + parent::renderError($error) + ); + } + + /** + * @param $content + * @return string + */ + protected function renderContainerFor($content) + { + return sprintf( + '<%s%s>%s', + $this->tag, + $this->attributes->render(), + $content, + $this->tag + ); + } +} \ No newline at end of file diff --git a/library/Businessprocess/Web/Component/Link.php b/library/Businessprocess/Web/Component/Link.php new file mode 100644 index 0000000..fa9e2fc --- /dev/null +++ b/library/Businessprocess/Web/Component/Link.php @@ -0,0 +1,76 @@ +text = $text; + if ($url instanceof Url) { + if ($urlParams !== null) { + $url->addParams($urlParams); + } + + $link->url = $url; + } else { + $link->url = Url::fromPath($url, $urlParams); + } + $link->attributes = new Attributes($attributes); + + return $link; + } + + /** + * @return string + */ + public function getRenderedText() + { + return $this->view()->escape((string) $this->text); + } + + /** + * @return Url + */ + public function getUrl() + { + return $this->url; + } + + /** + * @inheritdoc + */ + public function render() + { + return sprintf( + '%s', + $this->getUrl(), + $this->attributes->render(), + $this->getRenderedText() + ); + } +} \ No newline at end of file From e93d626d09a73af9e3fdbbc7f7f2cf952f2c7d9d Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 22 Nov 2016 15:28:37 +0100 Subject: [PATCH 024/256] Controller, css: add action bar --- library/Businessprocess/Controller.php | 14 ++++++++++---- public/css/module.less | 8 ++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/library/Businessprocess/Controller.php b/library/Businessprocess/Controller.php index 8c0023c..070adbb 100644 --- a/library/Businessprocess/Controller.php +++ b/library/Businessprocess/Controller.php @@ -2,17 +2,14 @@ namespace Icinga\Module\Businessprocess; -use Exception; use Icinga\Application\Icinga; use Icinga\Module\Businessprocess\BusinessProcess; -use Icinga\Module\Businessprocess\Form\ProcessForm; -use Icinga\Module\Businessprocess\Form\SimulationForm; use Icinga\Module\Businessprocess\Storage\LegacyStorage; +use Icinga\Module\Businessprocess\Web\Component\ActionBar; use Icinga\Module\Monitoring\Backend; use Icinga\Web\Controller as ModuleController; use Icinga\Web\Notification; use Icinga\Module\Businessprocess\Web\Form\FormLoader; -use Icinga\Web\Url; use Icinga\Web\Widget; class Controller extends ModuleController @@ -46,6 +43,15 @@ class Controller extends ModuleController return $this->url; } + protected function actions() + { + if ($this->view->actions === null) { + $this->view->actions = ActionBar::create(); + } + + return $this->view->actions; + } + protected function tabs() { if ($this->view->tabs === null) { diff --git a/public/css/module.less b/public/css/module.less index 3ef8fc1..a49ecdc 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -28,6 +28,14 @@ display: inline; } +.action-bar a { + color: @icinga-blue; + &:hover::before { + text-decoration: none; + } + margin-right: 1em; +} + h1 a:focus { outline: none; } From 0b6cf208f02fc6e661ebf7d4c3c68e4ad22e02fc Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 22 Nov 2016 16:24:34 +0100 Subject: [PATCH 025/256] Node: fix hasMissingChildren() --- library/Businessprocess/Node.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Businessprocess/Node.php b/library/Businessprocess/Node.php index 3652454..6223983 100644 --- a/library/Businessprocess/Node.php +++ b/library/Businessprocess/Node.php @@ -129,7 +129,7 @@ abstract class Node public function hasMissingChildren() { - return count($this->getMissingChildren() > 0); + return count($this->getMissingChildren()) > 0; } public function getMissingChildren() From 0251bc5edb22b3208f96fa8c07d83fc4e0b43c7f Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 22 Nov 2016 17:57:46 +0100 Subject: [PATCH 026/256] Host/ServiceNode: introduce common base class --- library/Businessprocess/HostNode.php | 2 +- library/Businessprocess/MonitoredNode.php | 24 +++++++++++++++++++++++ library/Businessprocess/ServiceNode.php | 4 ++-- 3 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 library/Businessprocess/MonitoredNode.php diff --git a/library/Businessprocess/HostNode.php b/library/Businessprocess/HostNode.php index 6715e00..4c2496e 100644 --- a/library/Businessprocess/HostNode.php +++ b/library/Businessprocess/HostNode.php @@ -4,7 +4,7 @@ namespace Icinga\Module\Businessprocess; use Icinga\Web\Url; -class HostNode extends Node +class HostNode extends MonitoredNode { protected static $sortStateToStateMap = array( 4 => self::ICINGA_DOWN, diff --git a/library/Businessprocess/MonitoredNode.php b/library/Businessprocess/MonitoredNode.php new file mode 100644 index 0000000..85ec5fe --- /dev/null +++ b/library/Businessprocess/MonitoredNode.php @@ -0,0 +1,24 @@ +add( + $url = Link::create( + $actions->view()->translate('Simulate a specific state'), + 'businessprocess/process/show?addSimulation&unlocked', + array( + 'config' => $this->bp->getName(), + 'simulationNode' => $this->name + ), + array('class' => 'icon-magic') + ) + ); + } +} \ No newline at end of file diff --git a/library/Businessprocess/ServiceNode.php b/library/Businessprocess/ServiceNode.php index 8360ad6..b169e24 100644 --- a/library/Businessprocess/ServiceNode.php +++ b/library/Businessprocess/ServiceNode.php @@ -4,7 +4,7 @@ namespace Icinga\Module\Businessprocess; use Icinga\Web\Url; -class ServiceNode extends Node +class ServiceNode extends MonitoredNode { protected $hostname; @@ -42,7 +42,7 @@ class ServiceNode extends Node return $link; } - + protected function getActionIcons($view) { $icons = array(); From 924a2fd321666f6f96fc93a8f22fc9d5cf7b8232 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 22 Nov 2016 18:00:09 +0100 Subject: [PATCH 027/256] BusinessProcess: fallback for missing host --- library/Businessprocess/BusinessProcess.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index 1789415..81b86c1 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -482,7 +482,11 @@ class BusinessProcess if ($pos !== false) { $host = substr($name, 0, $pos); $service = substr($name, $pos + 1); - return $this->createService($host, $service); + if ($service === 'Hoststatus') { + return $this->create($host); + } else { + return $this->createService($host, $service); + } } throw new Exception( From 30cdc707835caa4b6fbe0b77778cb2e94f4d4d1c Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 22 Nov 2016 18:03:58 +0100 Subject: [PATCH 028/256] edit/simulationlink: remove obsolete view scripts --- application/views/scripts/editlink.phtml | 9 --------- application/views/scripts/simulationlink.phtml | 6 ------ 2 files changed, 15 deletions(-) delete mode 100644 application/views/scripts/editlink.phtml delete mode 100644 application/views/scripts/simulationlink.phtml diff --git a/application/views/scripts/editlink.phtml b/application/views/scripts/editlink.phtml deleted file mode 100644 index d00d340..0000000 --- a/application/views/scripts/editlink.phtml +++ /dev/null @@ -1,9 +0,0 @@ - bp->isEditMode()): ?> - bp->hasBeenChanged()): ?> - icon('ok') ?> - - icon('cancel') ?> - - icon('wrench') ?> - - diff --git a/application/views/scripts/simulationlink.phtml b/application/views/scripts/simulationlink.phtml deleted file mode 100644 index 813d24d..0000000 --- a/application/views/scripts/simulationlink.phtml +++ /dev/null @@ -1,6 +0,0 @@ - bp->isSimulationMode()): ?> - icon('globe') ?> - - icon('magic') ?> - - From da3f3d6a63d2abd1458272db7b2f9b58044e9ef9 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 22 Nov 2016 18:11:56 +0100 Subject: [PATCH 029/256] BusinessProcess: fix (unused) method usesHardStates --- library/Businessprocess/BusinessProcess.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index 81b86c1..83764f0 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -292,7 +292,7 @@ class BusinessProcess public function usesHardStates() { - $this->state_type === self::HARD_STATE; + return $this->state_type === self::HARD_STATE; } public function addRootNode($name) From db8f0c54755792b2333b2d1a56c1b995a2248baa Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 22 Nov 2016 21:11:26 +0100 Subject: [PATCH 030/256] ProcessChanges,NodeAction(s): cleanup, docs --- library/Businessprocess/NodeAction.php | 84 +++++++++++++++++--- library/Businessprocess/NodeCreateAction.php | 46 +++++++++-- library/Businessprocess/NodeModifyAction.php | 35 +++++++- library/Businessprocess/NodeRemoveAction.php | 13 +++ library/Businessprocess/ProcessChanges.php | 83 ++++++++++++++++++- 5 files changed, 236 insertions(+), 25 deletions(-) diff --git a/library/Businessprocess/NodeAction.php b/library/Businessprocess/NodeAction.php index 88e0e34..3266574 100644 --- a/library/Businessprocess/NodeAction.php +++ b/library/Businessprocess/NodeAction.php @@ -4,41 +4,82 @@ namespace Icinga\Module\Businessprocess; use Icinga\Exception\ProgrammingError; +/** + * Abstract NodeAction class + * + * Every instance of a NodeAction represents a single applied change. Changes are pushed to + * a stack and consumed from there. When persisted, NodeActions are serialized with their name, + * node name and optionally additional properties according preserveProperties. For each property + * that should be preserved, getter and setter methods have to be defined. + * + * @package Icinga\Module\Businessprocess + */ abstract class NodeAction { - const TYPE_CREATE = 'create'; - - const TYPE_REMOVE = 'remove'; - - const TYPE_MODIFY = 'modify'; - - const TYPE_CHILD_ADD = 'childRemove'; - - const TYPE_CHILD_REMOVE = 'childAdd'; - - protected $nodeName; - + /** @var string Name of this action (currently create, modify, remove) */ protected $actionName; + /** @var string Name of the node this action applies to */ + protected $nodeName; + + /** @var array Properties which should be preeserved when serializing this action */ protected $preserveProperties = array(); + /** + * NodeAction constructor. + * + * @param Node|string $node + */ public function __construct($node) { $this->nodeName = (string) $node; } + /** + * Every NodeAction must be able to apply itself to a BusinessProcess + * + * @param BusinessProcess $bp + * @return mixed + */ abstract public function applyTo(BusinessProcess $bp); + /** + * Every NodeAction must be able to tell whether it could be applied to a BusinessProcess + * + * @param BusinessProcess $bp + * @return bool + */ + abstract public function appliesTo(BusinessProcess $bp); + + /** + * The name of the node this modification applies to + * + * @return string + */ public function getNodeName() { return $this->nodeName; } + /** + * Whether this is an instance of a given action name + * + * @param string $actionName + * @return bool + */ public function is($actionName) { return $this->getActionName() === $actionName; } + /** + * Create an instance of a given actionName for a specific Node + * + * @param string $actionName + * @param string $nodeName + * + * @return static + */ public static function create($actionName, $nodeName) { $classname = __NAMESPACE__ . '\\Node' . ucfirst($actionName) . 'Action'; @@ -46,6 +87,11 @@ abstract class NodeAction return $object; } + /** + * Returns a JSON-encoded serialized NodeAction + * + * @return string + */ public function serialize() { $object = (object) array( @@ -62,6 +108,12 @@ abstract class NodeAction return json_encode($object); } + /** + * Decodes a JSON-serialized NodeAction and returns an object instance + * + * @param $string + * @return NodeAction + */ public static function unserialize($string) { $object = json_decode($string); @@ -75,6 +127,13 @@ abstract class NodeAction return $action; } + /** + * Returns the defined action name or determines such from the class name + * + * @return string The action name + * + * @throws ProgrammingError when no such class exists + */ public function getActionName() { if ($this->actionName === null) { @@ -89,5 +148,4 @@ abstract class NodeAction return $this->actionName; } - } diff --git a/library/Businessprocess/NodeCreateAction.php b/library/Businessprocess/NodeCreateAction.php index c6e5a58..c95321e 100644 --- a/library/Businessprocess/NodeCreateAction.php +++ b/library/Businessprocess/NodeCreateAction.php @@ -2,64 +2,96 @@ namespace Icinga\Module\Businessprocess; +use stdClass; + class NodeCreateAction extends NodeAction { + /** @var string */ protected $parentName; + /** @var array */ protected $properties = array(); + /** @var array */ protected $preserveProperties = array('parentName', 'properties'); + /** + * @param Node $name + */ public function setParent(Node $name) { - $this->parentName = $name; + $this->parentName = (string) $name; } + /** + * @return bool + */ public function hasParent() { return $this->parentName !== null; } + /** + * @return string + */ public function getParentName() { return $this->parentName; } + /** + * @param string $name + */ public function setParentName($name) { $this->parentName = $name; } + /** + * @return array + */ public function getProperties() { return $this->properties; } - public function setProperties($properties) + /** + * @param stdClass $properties + * @return $this + */ + public function setProperties(stdClass $properties) { $this->properties = $properties; return $this; } + /** + * @inheritdoc + */ public function appliesTo(BusinessProcess $bp) { return ! $bp->hasNode($this->getNodeName()); } + /** + * @inheritdoc + */ public function applyTo(BusinessProcess $bp) { + $name = $this->getNodeName(); + $node = new BpNode($bp, (object) array( - 'name' => $this->getNodeName(), - 'operator' => $this->properties->operator, - 'child_names' => $this->properties->childNames + 'name' => $name, + 'operator' => $this->properties['operator'], + 'child_names' => $this->properties['childNames'] )); - foreach ($this->properties as $key => $val) { + foreach ($this->getProperties() as $key => $val) { $func = 'set' . ucfirst($key); $node->$func($val); } - $bp->addNode($this->getNodeName(), $node); + $bp->addNode($name, $node); if ($this->hasParent()) { $node->addParent($bp->getNode($this->getParentName())); } diff --git a/library/Businessprocess/NodeModifyAction.php b/library/Businessprocess/NodeModifyAction.php index da2e7fa..f50e166 100644 --- a/library/Businessprocess/NodeModifyAction.php +++ b/library/Businessprocess/NodeModifyAction.php @@ -10,11 +10,18 @@ class NodeModifyAction extends NodeAction protected $preserveProperties = array('formerProperties', 'properties'); -// Can be called multiple times - public function setNodeProperties(Node $node, $properties) + /** + * Set properties for a specific node + * + * Can be called multiple times + * + * @param Node $node + * @param array $properties + * + * @return $this + */ + public function setNodeProperties(Node $node, array $properties) { - $old = array(); - foreach (array_keys($properties) as $key) { $this->properties[$key] = $properties[$key]; @@ -30,6 +37,9 @@ class NodeModifyAction extends NodeAction return $this; } + /** + * @inheritdoc + */ public function appliesTo(BusinessProcess $bp) { $name = $this->getNodeName(); @@ -50,6 +60,9 @@ class NodeModifyAction extends NodeAction return true; } + /** + * @inheritdoc + */ public function applyTo(BusinessProcess $bp) { $node = $bp->getNode($this->getNodeName()); @@ -62,23 +75,37 @@ class NodeModifyAction extends NodeAction return $this; } + /** + * @param $properties + * @return $this + */ public function setProperties($properties) { $this->properties = $properties; return $this; } + /** + * @param $properties + * @return $this + */ public function setFormerProperties($properties) { $this->formerProperties = $properties; return $this; } + /** + * @return array + */ public function getProperties() { return $this->properties; } + /** + * @return array + */ public function getFormerProperties() { return $this->formerProperties; diff --git a/library/Businessprocess/NodeRemoveAction.php b/library/Businessprocess/NodeRemoveAction.php index 916265f..5c48279 100644 --- a/library/Businessprocess/NodeRemoveAction.php +++ b/library/Businessprocess/NodeRemoveAction.php @@ -2,13 +2,26 @@ namespace Icinga\Module\Businessprocess; +/** + * NodeRemoveAction + * + * Tracks removed nodes + * + * @package Icinga\Module\Businessprocess + */ class NodeRemoveAction extends NodeAction { + /** + * @inheritdoc + */ public function appliesTo(BusinessProcess $bp) { return $bp->hasNode($this->getNodeName()); } + /** + * @inheritdoc + */ public function applyTo(BusinessProcess $bp) { $bp->removeNode($this->getNodeName()); diff --git a/library/Businessprocess/ProcessChanges.php b/library/Businessprocess/ProcessChanges.php index 1fcf17c..cf1c2c5 100644 --- a/library/Businessprocess/ProcessChanges.php +++ b/library/Businessprocess/ProcessChanges.php @@ -3,22 +3,36 @@ namespace Icinga\Module\Businessprocess; use Icinga\Web\Session\SessionNamespace as Session; -use Icinga\Module\Businessprocess\NodeAction; class ProcessChanges { + /** @var NodeAction[] */ protected $changes = array(); + /** @var Session */ protected $session; + /** @var bool */ protected $hasBeenModified = false; + /** @var string Session storage key for this processes changes */ protected $sessionKey; + /** + * ProcessChanges constructor. + * + * Direct access is not allowed + */ private function __construct() { } + /** + * @param BusinessProcess $bp + * @param Session $session + * + * @return ProcessChanges + */ public static function construct(BusinessProcess $bp, Session $session) { $key = 'changes.' . $bp->getName(); @@ -35,6 +49,12 @@ class ProcessChanges return $changes; } + /** + * @param Node $node + * @param $properties + * + * @return $this + */ public function modifyNode(Node $node, $properties) { $action = new NodeModifyAction($node); @@ -42,6 +62,13 @@ class ProcessChanges return $this->push($action); } + /** + * @param Node|string $nodeName + * @param array $properties + * @param Node $parent + * + * @return $this + */ public function createNode($nodeName, $properties, Node $parent = null) { $action = new NodeCreateAction($nodeName); @@ -52,11 +79,23 @@ class ProcessChanges return $this->push($action); } + /** + * @param Node $node + * + * @return $this + */ public function deleteNode(Node $node) { return $this->push(new NodeDeleteAction($node)); } + /** + * Add a new action to the stack + * + * @param NodeAction $change + * + * @return $this + */ public function push(NodeAction $change) { $this->changes[] = $change; @@ -64,11 +103,22 @@ class ProcessChanges return $this; } + + /** + * Get all stacked actions + * + * @return NodeAction[] + */ public function getChanges() { return $this->changes; } + /** + * Forget all changes and remove them from the Session + * + * @return $this + */ public function clear() { $this->hasBeenModified = true; @@ -77,16 +127,31 @@ class ProcessChanges return $this; } + /** + * Whether there are no stacked changes + * + * @return bool + */ public function isEmpty() { return $this->count() === 0; } + /** + * Number of stacked changes + * + * @return bool + */ public function count() { return count($this->changes); } + /** + * Get the first change on the stack, false if empty + * + * @return NodeAction|boolean + */ public function shift() { if ($this->isEmpty()) { @@ -97,6 +162,11 @@ class ProcessChanges return array_shift($this->changes); } + /** + * Get the last change on the stack, false if empty + * + * @return NodeAction|boolean + */ public function pop() { if ($this->isEmpty()) { @@ -107,6 +177,11 @@ class ProcessChanges return array_pop($this->changes); } + /** + * The identifier used for this processes changes in our Session storage + * + * @return string + */ protected function getSessionKey() { return $this->sessionKey; @@ -117,6 +192,9 @@ class ProcessChanges return $this->hasBeenModified; } + /** + * @return array + */ public function serialize() { $serialized = array(); @@ -127,6 +205,9 @@ class ProcessChanges return $serialized; } + /** + * Persist to session on destruction + */ public function __destruct() { if (! $this->hasBeenModified()) { From 95d8696705d7e6e9530da8c4f01ddf5123d8704b Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 22 Nov 2016 21:14:01 +0100 Subject: [PATCH 031/256] Node: alllow to get any state name, fix typo --- library/Businessprocess/Node.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/library/Businessprocess/Node.php b/library/Businessprocess/Node.php index 6223983..7188af7 100644 --- a/library/Businessprocess/Node.php +++ b/library/Businessprocess/Node.php @@ -175,9 +175,18 @@ abstract class Node return $this; } - public function getStateName() + public function getStateName($state = null) { - return static::$state_names[ $this->getState() ]; + if ($state === null) { + return static::$state_names[ $this->getState() ]; + } else { + return static::$state_names[ $state ]; + } + } + + public function enumStateNames() + { + return static::$state_names; } public function getState() @@ -310,7 +319,7 @@ abstract class Node return static::$sortStateToStateMap[$sortState]; } - throw new ProgrammingError('Got invalid sorting state %s', $sort_state); + throw new ProgrammingError('Got invalid sorting state %s', $sortState); } protected function renderHtmlForChildren($view) From 9e17ff21ba6ceec19d57a4c429727ef807d4cd4f Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 23 Nov 2016 10:12:25 +0100 Subject: [PATCH 032/256] Test: add base test framework --- library/Businessprocess/Test/BaseTestCase.php | 47 +++++++++++++++++++ library/Businessprocess/Test/Bootstrap.php | 28 +++++++++++ phpunit.xml | 18 +++++++ test/bootstrap.php | 9 ++++ test/config/authentication.ini | 0 test/config/config.ini | 0 6 files changed, 102 insertions(+) create mode 100644 library/Businessprocess/Test/BaseTestCase.php create mode 100644 library/Businessprocess/Test/Bootstrap.php create mode 100644 phpunit.xml create mode 100644 test/bootstrap.php create mode 100644 test/config/authentication.ini create mode 100644 test/config/config.ini diff --git a/library/Businessprocess/Test/BaseTestCase.php b/library/Businessprocess/Test/BaseTestCase.php new file mode 100644 index 0000000..8154bd4 --- /dev/null +++ b/library/Businessprocess/Test/BaseTestCase.php @@ -0,0 +1,47 @@ +app(); + } + + /** + * @param null $subDir + * @return string + */ + protected function getTestsBaseDir($subDir = null) + { + $dir = dirname(dirname(dirname(__DIR__))) . '/test'; + if ($subDir === null) { + return $dir; + } else { + return $dir . '/' . ltrim($subDir, '/'); + } + } + + /** + * @return ApplicationBootstrap + */ + protected function app() + { + if (self::$app === null) { + self::$app = Icinga::app(); + } + + return self::$app; + } +} diff --git a/library/Businessprocess/Test/Bootstrap.php b/library/Businessprocess/Test/Bootstrap.php new file mode 100644 index 0000000..3bd5187 --- /dev/null +++ b/library/Businessprocess/Test/Bootstrap.php @@ -0,0 +1,28 @@ +getModuleManager() + ->loadModule('businessprocess', $basedir); + } +} diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..72f31b3 --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,18 @@ + + + + + test/php + + + diff --git a/test/bootstrap.php b/test/bootstrap.php new file mode 100644 index 0000000..9a0fe64 --- /dev/null +++ b/test/bootstrap.php @@ -0,0 +1,9 @@ + Date: Wed, 23 Nov 2016 10:43:35 +0100 Subject: [PATCH 033/256] LegacyStorageTest: add a bunch of tests --- .../processes/broken_wrong-operator.conf | 1 + .../processes/simple_with-header.conf | 12 ++ .../processes/simple_without-header.conf | 6 + .../Storage/LegacyStorageTest.php | 112 ++++++++++++++++++ 4 files changed, 131 insertions(+) create mode 100644 test/config/modules/businessprocess/processes/broken_wrong-operator.conf create mode 100644 test/config/modules/businessprocess/processes/simple_with-header.conf create mode 100644 test/config/modules/businessprocess/processes/simple_without-header.conf create mode 100644 test/php/library/Businessprocess/Storage/LegacyStorageTest.php diff --git a/test/config/modules/businessprocess/processes/broken_wrong-operator.conf b/test/config/modules/businessprocess/processes/broken_wrong-operator.conf new file mode 100644 index 0000000..9a58f23 --- /dev/null +++ b/test/config/modules/businessprocess/processes/broken_wrong-operator.conf @@ -0,0 +1 @@ +hostsAnd = host1;Hoststatus + host2;Hoststatus \ No newline at end of file diff --git a/test/config/modules/businessprocess/processes/simple_with-header.conf b/test/config/modules/businessprocess/processes/simple_with-header.conf new file mode 100644 index 0000000..7c986bb --- /dev/null +++ b/test/config/modules/businessprocess/processes/simple_with-header.conf @@ -0,0 +1,12 @@ +############################################ +# +# Title: Simple with header +# +############################################ + +hostsAnd = host1;Hoststatus & host2;Hoststatus +servicesOr = host1;ping | host2;ping | host3;ping +singleHost = host1;Hoststatus +minTwo = 2 of: hostsAnd + servicesOr + singleHost +top = minTwo & hostsAnd & servicesOr +display 1;top;Top Node \ No newline at end of file diff --git a/test/config/modules/businessprocess/processes/simple_without-header.conf b/test/config/modules/businessprocess/processes/simple_without-header.conf new file mode 100644 index 0000000..7d4efc6 --- /dev/null +++ b/test/config/modules/businessprocess/processes/simple_without-header.conf @@ -0,0 +1,6 @@ +hostsAnd = host1;Hoststatus & host2;Hoststatus +servicesOr = host1;ping | host2;ping | host3;ping +singleHost = host1;Hoststatus +minTwo = 2 of: hostsAnd + servicesOr + singleHost +top = minTwo & hostsAnd & servicesOr +display 1;top;Top Node \ No newline at end of file diff --git a/test/php/library/Businessprocess/Storage/LegacyStorageTest.php b/test/php/library/Businessprocess/Storage/LegacyStorageTest.php new file mode 100644 index 0000000..985ddf3 --- /dev/null +++ b/test/php/library/Businessprocess/Storage/LegacyStorageTest.php @@ -0,0 +1,112 @@ +assertInstanceOf( + $baseClass, + new LegacyStorage(Config::module('businessprocess')->getSection('global')) + ); + } + + public function testWhetherDefaultConfigDirIsDetermined() + { + $this->assertEquals( + $this->getTestsBaseDir('config/modules/businessprocess/processes'), + $this->makeInstance()->getConfigDir() + ); + } + + public function testWhetherAllProcessesAreListed() + { + $keys = array_keys($this->makeInstance()->listProcesses()); + sort($keys); + $this->assertEquals( + $keys, + array( + 'broken_wrong-operator', + 'simple_with-header', + 'simple_without-header', + ) + ); + } + + public function testWhetherHeadersAreRespectedInProcessList() + { + $keys = array_values($this->makeInstance()->listProcesses()); + sort($keys); + $this->assertEquals( + $keys, + array( + 'Simple with header (simple_with-header)', + 'broken_wrong-operator', + 'simple_without-header', + ) + ); + } + + public function testWhetherProcessFilenameIsReturned() + { + $this->assertEquals( + $this->getTestsBaseDir('config/modules/businessprocess/processes/simple_with-header.conf'), + $this->makeInstance()->getFilename('simple_with-header') + ); + } + + public function testWhetherExistingProcessExists() + { + $this->assertTrue( + $this->makeInstance()->hasProcess('simple_with-header') + ); + } + + public function testWhetherMissingProcessIsMissing() + { + $this->assertFalse( + $this->makeInstance()->hasProcess('simple_with-headerx') + ); + } + + public function testWhetherValidProcessCanBeLoaded() + { + $processClass = 'Icinga\\Module\\Businessprocess\\BusinessProcess'; + $this->assertInstanceOf( + $processClass, + $this->makeInstance()->loadProcess('simple_with-header') + ); + } + + public function testWhetherConfigCanBeLoadedFromAString() + { + $processClass = 'Icinga\\Module\\Businessprocess\\BusinessProcess'; + $this->assertInstanceOf( + $processClass, + $this->makeInstance()->loadFromString('dummy', 'a = Host1;ping & Host2;ping') + ); + } + + public function testWhetherProcessSourceCanBeFetched() + { + $this->assertEquals( + file_get_contents($this->getTestsBaseDir('config/modules/businessprocess/processes/simple_with-header.conf')), + $this->makeInstance()->getSource('simple_with-header') + ); + } + + /** + * @return LegacyStorage + */ + protected function makeInstance() + { + return new LegacyStorage(Config::module('businessprocess')->getSection('global')); + } +} \ No newline at end of file From a2fd5879896a320154e3f95e699bd9cd3d0223f0 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 23 Nov 2016 10:44:24 +0100 Subject: [PATCH 034/256] LegacyStorage: reorganize some methods --- .../Businessprocess/Storage/LegacyStorage.php | 71 +++++++++++-------- library/Businessprocess/Storage/Storage.php | 14 ++++ 2 files changed, 55 insertions(+), 30 deletions(-) diff --git a/library/Businessprocess/Storage/LegacyStorage.php b/library/Businessprocess/Storage/LegacyStorage.php index 8d9761b..5e1e533 100644 --- a/library/Businessprocess/Storage/LegacyStorage.php +++ b/library/Businessprocess/Storage/LegacyStorage.php @@ -2,55 +2,63 @@ namespace Icinga\Module\Businessprocess\Storage; +use DirectoryIterator; +use Icinga\Application\Benchmark; use Icinga\Application\Icinga; use Icinga\Authentication\Auth; -use Icinga\Data\ConfigObject; use Icinga\Exception\ConfigurationError; -use Icinga\Module\Businessprocess\BusinessProcess; use Icinga\Module\Businessprocess\BpNode; -use Icinga\Module\Businessprocess\Storage\Storage; -use DirectoryIterator; +use Icinga\Module\Businessprocess\BusinessProcess; use Icinga\Exception\SystemPermissionException; -use Icinga\Application\Benchmark; class LegacyStorage extends Storage { + /** @var string */ protected $configDir; + /** @var int */ protected $parsing_line_number; + /** @var string */ protected $currentFilename; public function getConfigDir() { if ($this->configDir === null) { - $dir = Icinga::app() - ->getModuleManager() - ->getModule('businessprocess') - ->getConfigDir(); - - // TODO: This is silly. We need Config::requireDirectory(). - if (! is_dir($dir)) { - if (! is_dir(dirname($dir))) { - if (! @mkdir(dirname($dir))) { - throw new SystemPermissionException('Could not create config directory "%s"', dirname($dir)); - } - } - if (! mkdir($dir)) { - throw new SystemPermissionException('Could not create config directory "%s"', $dir); - } - } - $dir = $dir . '/processes'; - if (! is_dir($dir)) { - if (! mkdir($dir)) { - throw new SystemPermissionException('Could not create config directory "%s"', $dir); - } - } - $this->configDir = $dir; + $this->prepareDefaultConfigDir(); } + return $this->configDir; } + protected function prepareDefaultConfigDir() + { + $dir = Icinga::app() + ->getModuleManager() + ->getModule('businessprocess') + ->getConfigDir(); + + // TODO: This is silly. We need Config::requireDirectory(). + if (! is_dir($dir)) { + if (! is_dir(dirname($dir))) { + if (! @mkdir(dirname($dir))) { + throw new SystemPermissionException('Could not create config directory "%s"', dirname($dir)); + } + } + if (! mkdir($dir)) { + throw new SystemPermissionException('Could not create config directory "%s"', $dir); + } + } + $dir = $dir . '/processes'; + if (! is_dir($dir)) { + if (! mkdir($dir)) { + throw new SystemPermissionException('Could not create config directory "%s"', $dir); + } + } + + $this->configDir = $dir; + } + /** * @return array */ @@ -191,13 +199,16 @@ class LegacyStorage extends Storage $bp = new BusinessProcess(); $bp->setName($name); $this->parseString($string, $bp); - $this->loadHeader($name, $bp); + // $this->loadHeader($name, $bp); return $bp; } + /** + * @inheritdoc + */ public function deleteProcess($name) { - unlink($this->getFilename($name)); + return @unlink($this->getFilename($name)); } /** diff --git a/library/Businessprocess/Storage/Storage.php b/library/Businessprocess/Storage/Storage.php index 21d130f..89e1a7f 100644 --- a/library/Businessprocess/Storage/Storage.php +++ b/library/Businessprocess/Storage/Storage.php @@ -7,8 +7,15 @@ use Icinga\Module\Businessprocess\BusinessProcess; abstract class Storage { + /** + * @var ConfigObject + */ protected $config; + /** + * Storage constructor. + * @param ConfigObject $config + */ public function __construct(ConfigObject $config) { $this->config = $config; @@ -25,13 +32,20 @@ abstract class Storage abstract public function listProcesses(); /** + * @param $name * @return BusinessProcess */ abstract public function loadProcess($name); /** + * @param BusinessProcess $name + * @return mixed */ abstract public function storeProcess(BusinessProcess $name); + /** + * @param $name + * @return bool Whether the process has been deleted + */ abstract public function deleteProcess($name); } From 9acdba0dc452624175195af631c3779ad41d1d07 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 23 Nov 2016 11:13:29 +0100 Subject: [PATCH 035/256] various: fix some phpcs PSR2 complaints --- application/views/helpers/RenderStateBadges.php | 14 +++++++++++--- .../Businessprocess/Storage/LegacyStorage.php | 17 +++++++++++------ .../Businessprocess/Web/Component/ActionBar.php | 2 +- .../Web/Component/Attributes.php | 4 ++-- .../Businessprocess/Web/Component/Component.php | 4 ++-- .../Businessprocess/Web/Component/Container.php | 2 +- library/Businessprocess/Web/Component/Link.php | 2 +- 7 files changed, 29 insertions(+), 16 deletions(-) diff --git a/application/views/helpers/RenderStateBadges.php b/application/views/helpers/RenderStateBadges.php index 14db762..7a4d708 100644 --- a/application/views/helpers/RenderStateBadges.php +++ b/application/views/helpers/RenderStateBadges.php @@ -1,15 +1,23 @@ $cnt) { - if ($cnt === 0) continue; - if ($state === 'OK') continue; - if ($state === 'UP') continue; + if ($cnt === 0 + || $state === 'OK' + ||$ state === 'UP' + ) { + continue; + } + $html .= '' . $cnt . ''; diff --git a/library/Businessprocess/Storage/LegacyStorage.php b/library/Businessprocess/Storage/LegacyStorage.php index 5e1e533..7309948 100644 --- a/library/Businessprocess/Storage/LegacyStorage.php +++ b/library/Businessprocess/Storage/LegacyStorage.php @@ -67,7 +67,10 @@ class LegacyStorage extends Storage $files = array(); foreach (new DirectoryIterator($this->getConfigDir()) as $file) { - if($file->isDot()) continue; + if ($file->isDot()) { + continue; + } + $filename = $file->getFilename(); if (substr($filename, -5) === '.conf') { $name = substr($filename, 0, -5); @@ -94,8 +97,7 @@ class LegacyStorage extends Storage return true; } - if ( - $header['Allowed users'] === null + if ($header['Allowed users'] === null && $header['Allowed groups'] === null && $header['Allowed roles'] === null ) { @@ -336,18 +338,21 @@ class LegacyStorage extends Storage $op_name = $op; if ($op === '+') { - if (! preg_match('~^(\d+)\s*of:\s*(.+?)$~', $value, $m)) { + if (! preg_match('~^(\d+)(?::(\d+))?\s*of:\s*(.+?)$~', $value, $m)) { $this->parseError('syntax: = of: + [+ ]*'); } $op_name = $m[1]; - $value = $m[2]; + // New feature: $minWarn = $m[2]; + $value = $m[3]; } $cmps = preg_split('~\s*\\' . $op . '\s*~', $value, -1, PREG_SPLIT_NO_EMPTY); $childNames = array(); foreach ($cmps as $val) { if (strpos($val, ';') !== false) { - if ($bp->hasNode($val)) continue; + if ($bp->hasNode($val)) { + continue; + } list($host, $service) = preg_split('~;~', $val, 2); if ($service === 'Hoststatus') { diff --git a/library/Businessprocess/Web/Component/ActionBar.php b/library/Businessprocess/Web/Component/ActionBar.php index 0a71df0..887e06f 100644 --- a/library/Businessprocess/Web/Component/ActionBar.php +++ b/library/Businessprocess/Web/Component/ActionBar.php @@ -7,4 +7,4 @@ class ActionBar extends Container protected $separator = ' '; protected $attributes = array('class' => 'action-bar'); -} \ No newline at end of file +} diff --git a/library/Businessprocess/Web/Component/Attributes.php b/library/Businessprocess/Web/Component/Attributes.php index 1c073b7..c0c8039 100644 --- a/library/Businessprocess/Web/Component/Attributes.php +++ b/library/Businessprocess/Web/Component/Attributes.php @@ -110,7 +110,7 @@ class Attributes extends Component } return $this; - } else if ($attribute instanceof Attribute) { + } elseif ($attribute instanceof Attribute) { return $this->setAttribute($attribute); } else { return $this->setAttribute(new Attribute($attribute, $value)); @@ -155,4 +155,4 @@ class Attributes extends Component return ' ' . implode(' ', $this->attributes); } -} \ No newline at end of file +} diff --git a/library/Businessprocess/Web/Component/Component.php b/library/Businessprocess/Web/Component/Component.php index fb7d873..3830e06 100644 --- a/library/Businessprocess/Web/Component/Component.php +++ b/library/Businessprocess/Web/Component/Component.php @@ -56,7 +56,7 @@ abstract class Component /** * @return string */ - abstract function render(); + abstract public function render(); public function wantHtml($any, $separator = '') { @@ -131,4 +131,4 @@ abstract class Component return $this->renderError($e); } } -} \ No newline at end of file +} diff --git a/library/Businessprocess/Web/Component/Container.php b/library/Businessprocess/Web/Component/Container.php index b4d90d4..69cce5a 100644 --- a/library/Businessprocess/Web/Component/Container.php +++ b/library/Businessprocess/Web/Component/Container.php @@ -141,4 +141,4 @@ class Container extends Component $this->tag ); } -} \ No newline at end of file +} diff --git a/library/Businessprocess/Web/Component/Link.php b/library/Businessprocess/Web/Component/Link.php index fa9e2fc..525694c 100644 --- a/library/Businessprocess/Web/Component/Link.php +++ b/library/Businessprocess/Web/Component/Link.php @@ -73,4 +73,4 @@ class Link extends Component $this->getRenderedText() ); } -} \ No newline at end of file +} From 2b98629f3e706e5d96bcdbe02d02316bca9d54dd Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 23 Nov 2016 12:27:55 +0100 Subject: [PATCH 036/256] Modification: move modification handling to... ...a dedicated namespace --- application/forms/ProcessForm.php | 21 ++++++++++++------- library/Businessprocess/BusinessProcess.php | 18 ++++++++++++---- library/Businessprocess/Controller.php | 5 ++--- .../{ => Modification}/NodeAction.php | 12 ++++++----- .../{ => Modification}/NodeCreateAction.php | 5 ++++- .../{ => Modification}/NodeModifyAction.php | 5 ++++- .../{ => Modification}/NodeRemoveAction.php | 4 +++- .../{ => Modification}/ProcessChanges.php | 10 +++++---- 8 files changed, 53 insertions(+), 27 deletions(-) rename library/Businessprocess/{ => Modification}/NodeAction.php (90%) rename library/Businessprocess/{ => Modification}/NodeCreateAction.php (91%) rename library/Businessprocess/{ => Modification}/NodeModifyAction.php (93%) rename library/Businessprocess/{ => Modification}/NodeRemoveAction.php (80%) rename library/Businessprocess/{ => Modification}/ProcessChanges.php (93%) diff --git a/application/forms/ProcessForm.php b/application/forms/ProcessForm.php index a238de9..ddad40b 100644 --- a/application/forms/ProcessForm.php +++ b/application/forms/ProcessForm.php @@ -2,25 +2,30 @@ namespace Icinga\Module\Businessprocess\Forms; -use Icinga\Web\Notification; -use Icinga\Web\Request; -use Icinga\Module\Businessprocess\Node; use Icinga\Module\Businessprocess\BpNode; -use Icinga\Module\Businessprocess\Form; -use Icinga\Module\Businessprocess\ProcessChanges; +use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\Modification\ProcessChanges; +use Icinga\Module\Businessprocess\Web\Form\QuickForm; +use Icinga\Module\Monitoring\Backend\MonitoringBackend; +use Icinga\Web\Notification; +use Icinga\Web\Session\SessionNamespace; -class ProcessForm extends Form +class ProcessForm extends QuickForm { + /** @var MonitoringBackend */ protected $backend; - protected $process; + /** @var BusinessProcess */ + protected $bp; + /** @var BpNode */ protected $node; protected $objectList = array(); protected $processList = array(); + /** @var SessionNamespace */ protected $session; public function setup() @@ -37,7 +42,7 @@ class ProcessForm extends Form 'label' => $this->translate('Title'), 'description' => $this->translate( 'Usually this title will be shown for this node. Equals name' - . ' if not given' + . ' if not given' ), )); diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index 83764f0..6e89b57 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -3,6 +3,8 @@ namespace Icinga\Module\Businessprocess; use Icinga\Application\Benchmark; +use Icinga\Exception\ProgrammingError; +use Icinga\Module\Businessprocess\Modification\ProcessChanges; use Icinga\Module\Monitoring\Backend\MonitoringBackend; use Icinga\Data\Filter\Filter; use Exception; @@ -360,7 +362,7 @@ class BusinessProcess $hostStatus = $backend->select()->from('hostStatus', array( 'hostname' => 'host_name', - 'last_state_change' => $hostStateChangeColumn, + 'last_state_change' => $hostStateChangeColumn, 'in_downtime' => 'host_in_downtime', 'ack' => 'host_acknowledged', 'state' => $hostStateColumn @@ -369,7 +371,7 @@ class BusinessProcess $serviceStatus = $backend->select()->from('serviceStatus', array( 'hostname' => 'host_name', 'service' => 'service_description', - 'last_state_change' => $serviceStateChangeColumn, + 'last_state_change' => $serviceStateChangeColumn, 'in_downtime' => 'service_in_downtime', 'ack' => 'service_acknowledged', 'state' => $serviceStateColumn @@ -381,7 +383,7 @@ class BusinessProcess foreach ($hostStatus as $row) { $this->handleDbRow($row); - } + } ksort($this->root_nodes); Benchmark::measure('Got states for business process ' . $this->getName()); @@ -398,7 +400,10 @@ class BusinessProcess $key .= ';Hoststatus'; } // We fetch more states than we need, so skip unknown ones - if (! $this->hasNode($key)) return; + if (! $this->hasNode($key)) { + return; + } + $node = $this->getNode($key); if ($row->state !== null) { @@ -527,6 +532,11 @@ class BusinessProcess return $this; } + public function removeNode($name) + { + throw new ProgrammingError('Not implemented yet'); + } + public function listBpNodes() { $nodes = array(); diff --git a/library/Businessprocess/Controller.php b/library/Businessprocess/Controller.php index 070adbb..fa4499f 100644 --- a/library/Businessprocess/Controller.php +++ b/library/Businessprocess/Controller.php @@ -3,13 +3,12 @@ namespace Icinga\Module\Businessprocess; use Icinga\Application\Icinga; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\Modification\ProcessChanges; use Icinga\Module\Businessprocess\Storage\LegacyStorage; use Icinga\Module\Businessprocess\Web\Component\ActionBar; -use Icinga\Module\Monitoring\Backend; +use Icinga\Module\Businessprocess\Web\Form\FormLoader; use Icinga\Web\Controller as ModuleController; use Icinga\Web\Notification; -use Icinga\Module\Businessprocess\Web\Form\FormLoader; use Icinga\Web\Widget; class Controller extends ModuleController diff --git a/library/Businessprocess/NodeAction.php b/library/Businessprocess/Modification/NodeAction.php similarity index 90% rename from library/Businessprocess/NodeAction.php rename to library/Businessprocess/Modification/NodeAction.php index 3266574..98329bd 100644 --- a/library/Businessprocess/NodeAction.php +++ b/library/Businessprocess/Modification/NodeAction.php @@ -1,7 +1,9 @@ actionName, $object->nodeName); diff --git a/library/Businessprocess/NodeCreateAction.php b/library/Businessprocess/Modification/NodeCreateAction.php similarity index 91% rename from library/Businessprocess/NodeCreateAction.php rename to library/Businessprocess/Modification/NodeCreateAction.php index c95321e..b47dacb 100644 --- a/library/Businessprocess/NodeCreateAction.php +++ b/library/Businessprocess/Modification/NodeCreateAction.php @@ -1,7 +1,10 @@ get($key)) { foreach ($actions as $string) { - $changes->push(NodeAction::unserialize($string)); + $changes->push(NodeAction::unSerialize($string)); } } $changes->session = $session; @@ -64,7 +66,7 @@ class ProcessChanges /** * @param Node|string $nodeName - * @param array $properties + * @param object $properties * @param Node $parent * * @return $this @@ -86,7 +88,7 @@ class ProcessChanges */ public function deleteNode(Node $node) { - return $this->push(new NodeDeleteAction($node)); + return $this->push(new NodeRemoveAction($node)); } /** From afcf42e8712ecef470b39069cf2c1428c734c4d4 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 23 Nov 2016 12:35:17 +0100 Subject: [PATCH 037/256] phpcs: some more --- application/forms/BpConfigForm.php | 8 +++++--- application/views/helpers/RenderStateBadges.php | 2 +- library/Businessprocess/BpNode.php | 5 ++++- library/Businessprocess/MonitoredNode.php | 2 +- library/Businessprocess/Node.php | 8 ++++++-- library/Businessprocess/ServiceNode.php | 2 +- 6 files changed, 18 insertions(+), 9 deletions(-) diff --git a/application/forms/BpConfigForm.php b/application/forms/BpConfigForm.php index b22bd23..abf3111 100644 --- a/application/forms/BpConfigForm.php +++ b/application/forms/BpConfigForm.php @@ -40,7 +40,7 @@ class BpConfigForm extends QuickForm 'label' => $this->translate('Title'), 'description' => $this->translate( 'Usually this title will be shown for this process. Equals name' - . ' if not given' + . ' if not given' ), )); @@ -48,7 +48,7 @@ class BpConfigForm extends QuickForm 'label' => $this->translate('Backend'), 'description' => $this->translate( 'Icinga Web Monitoring Backend where current object states for' - . ' this process should be retrieved from' + . ' this process should be retrieved from' ), 'multiOptions' => array( null => $this->translate('Use the configured default backend'), @@ -193,7 +193,9 @@ class BpConfigForm extends QuickForm public function shouldBeDeleted() { - if (! $this->hasDeleteButton()) return false; + if (! $this->hasDeleteButton()) { + return false; + } $name = $this->deleteButtonName; return $this->getSentValue($name) === $this->getElement($name)->getLabel(); diff --git a/application/views/helpers/RenderStateBadges.php b/application/views/helpers/RenderStateBadges.php index 7a4d708..bf24edc 100644 --- a/application/views/helpers/RenderStateBadges.php +++ b/application/views/helpers/RenderStateBadges.php @@ -13,7 +13,7 @@ class Zend_View_Helper_RenderStateBadges extends Zend_View_Helper_Abstract foreach ($summary as $state => $cnt) { if ($cnt === 0 || $state === 'OK' - ||$ state === 'UP' + || $state === 'UP' ) { continue; } diff --git a/library/Businessprocess/BpNode.php b/library/Businessprocess/BpNode.php index f71f531..895836e 100644 --- a/library/Businessprocess/BpNode.php +++ b/library/Businessprocess/BpNode.php @@ -362,7 +362,10 @@ class BpNode extends Node foreach ($this->getChildren() as $name => $child) { $children[] = (string) $child; - if (array_key_exists($name, $rendered)) { continue; } + if (array_key_exists($name, $rendered)) { + continue; + } + if ($child instanceof BpNode) { $cfg .= $child->toLegacyConfigString($rendered) . "\n"; } diff --git a/library/Businessprocess/MonitoredNode.php b/library/Businessprocess/MonitoredNode.php index 85ec5fe..2ffd77f 100644 --- a/library/Businessprocess/MonitoredNode.php +++ b/library/Businessprocess/MonitoredNode.php @@ -21,4 +21,4 @@ abstract class MonitoredNode extends Node ) ); } -} \ No newline at end of file +} diff --git a/library/Businessprocess/Node.php b/library/Businessprocess/Node.php index 7188af7..fba675b 100644 --- a/library/Businessprocess/Node.php +++ b/library/Businessprocess/Node.php @@ -465,8 +465,12 @@ abstract class Node return ' '; } - public function toLegacyConfigString(& $rendered = array()) { return '';} - //abstract public function toLegacyConfigString(); + // TODO: Why isn't this abstract? + // abstract public function toLegacyConfigString(); + public function toLegacyConfigString(& $rendered = array()) + { + return ''; + } public function __toString() { diff --git a/library/Businessprocess/ServiceNode.php b/library/Businessprocess/ServiceNode.php index b169e24..299c679 100644 --- a/library/Businessprocess/ServiceNode.php +++ b/library/Businessprocess/ServiceNode.php @@ -49,7 +49,7 @@ class ServiceNode extends MonitoredNode if (! $this->bp->isLocked()) { - $url = Url::fromPath( 'businessprocess/node/simulate', array( + $url = Url::fromPath('businessprocess/node/simulate', array( 'config' => $this->bp->getName(), 'node' => $this->name )); From 5189c79a27a109a4224e991b32435204425d09dd Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 23 Nov 2016 13:53:47 +0100 Subject: [PATCH 038/256] LegacyStorage: split header handling --- .../Businessprocess/Storage/LegacyStorage.php | 71 ++++++++++++++----- 1 file changed, 55 insertions(+), 16 deletions(-) diff --git a/library/Businessprocess/Storage/LegacyStorage.php b/library/Businessprocess/Storage/LegacyStorage.php index 7309948..4d4ea8c 100644 --- a/library/Businessprocess/Storage/LegacyStorage.php +++ b/library/Businessprocess/Storage/LegacyStorage.php @@ -74,7 +74,7 @@ class LegacyStorage extends Storage $filename = $file->getFilename(); if (substr($filename, -5) === '.conf') { $name = substr($filename, 0, -5); - $header = $this->readHeader($file->getPathname(), $name); + $header = $this->readHeader($file->getPathname()); if (! $this->headerPermissionsAreSatisfied($header)) { continue; } @@ -147,11 +147,33 @@ class LegacyStorage extends Storage return preg_split('/\s*,\s*/', $string, -1, PREG_SPLIT_NO_EMPTY); } - protected function readHeader($file, $name) + protected function readHeader($file) { $fh = fopen($file, 'r'); $cnt = 0; - $header = array( + $header = $this->emptyHeader(); + while ($cnt < 15 && false !== ($line = fgets($fh))) { + $cnt++; + $this->parseHeaderLine($line, $header); + } + + fclose($fh); + return $header; + } + + protected function readHeaderString($string) + { + $header = $this->emptyHeader(); + foreach (preg_split('/\n/', $string) as $line) { + $this->parseHeaderLine($line, $header); + } + + return $header; + } + + protected function emptyHeader() + { + return array( 'Title' => null, 'Owner' => null, 'Allowed users' => null, @@ -161,20 +183,19 @@ class LegacyStorage extends Storage 'Statetype' => 'soft', 'SLA Hosts' => null ); - while ($cnt < 15 && false !== ($line = fgets($fh))) { - $cnt++; - if (preg_match('/^\s*#\s+(.+?)\s*:\s*(.+)$/', $line, $m)) { - if (array_key_exists($m[1], $header)) { - $header[$m[1]] = $m[2]; - } + } + + protected function parseHeaderLine($line, & $header) + { + if (preg_match('/^\s*#\s+(.+?)\s*:\s*(.+)$/', $line, $m)) { + if (array_key_exists($m[1], $header)) { + $header[$m[1]] = $m[2]; } } - - fclose($fh); - return $header; } /** + * @param BusinessProcess $process */ public function storeProcess(BusinessProcess $process) { @@ -201,7 +222,7 @@ class LegacyStorage extends Storage $bp = new BusinessProcess(); $bp->setName($name); $this->parseString($string, $bp); - // $this->loadHeader($name, $bp); + $this->readHeaderString($string); return $bp; } @@ -227,6 +248,10 @@ class LegacyStorage extends Storage return $bp; } + /** + * @param $name + * @return bool + */ public function hasProcess($name) { $file = $this->getFilename($name); @@ -234,16 +259,30 @@ class LegacyStorage extends Storage return false; } - $name = substr($file, 0, -5); - $header = $this->readHeader($file, $name); + $header = $this->readHeader($file); + $bp = new BusinessProcess(); + $this->loadHeader($name, $bp); return $this->headerPermissionsAreSatisfied($header); } + /** + * @param string $name + * @param BusinessProcess $bp + */ protected function loadHeader($name, $bp) { // TODO: do not open twice, this is quick and dirty based on existing code $file = $this->currentFilename = $this->getFilename($name); - $header = $this->readHeader($file, $name); + $header = $this->readHeader($file); + $this->applyHeader($header, $bp); + } + + /** + * @param array $header + * @param BusinessProcess $bp + */ + protected function applyHeader($header, $bp) + { $bp->setTitle($header['Title']); if ($header['Backend']) { $bp->setBackendName($header['Backend']); From cc93a1d04a4b820ce864268fd5669e81131e9996 Mon Sep 17 00:00:00 2001 From: Dirk Goetz Date: Wed, 16 Dec 2015 15:52:49 +0100 Subject: [PATCH 039/256] Change pipe to broken pipe for or in detail output refs #10868 --- application/clicommands/CheckCommand.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/application/clicommands/CheckCommand.php b/application/clicommands/CheckCommand.php index b6ddd50..8895959 100644 --- a/application/clicommands/CheckCommand.php +++ b/application/clicommands/CheckCommand.php @@ -103,6 +103,8 @@ class CheckCommand extends Command $output .= $this->renderProblemTree($subtree['children'], $useColors, $depth + 1); } + $output = str_replace("|", "¦", $output); + return $output; } From 1e351a772482081f563b9f54dacc244fe4a09bf5 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 23 Nov 2016 15:20:46 +0100 Subject: [PATCH 040/256] ProcessCommand: replace and deprecate CheckCommand fixes #13295 --- application/clicommands/CheckCommand.php | 103 +----------------- application/clicommands/ProcessCommand.php | 118 +++++++++++++++++++++ 2 files changed, 123 insertions(+), 98 deletions(-) create mode 100644 application/clicommands/ProcessCommand.php diff --git a/application/clicommands/CheckCommand.php b/application/clicommands/CheckCommand.php index 8895959..d1c561f 100644 --- a/application/clicommands/CheckCommand.php +++ b/application/clicommands/CheckCommand.php @@ -2,37 +2,15 @@ namespace Icinga\Module\Businessprocess\Clicommands; -use Icinga\Cli\Command; -use Icinga\Module\Businessprocess\Storage\LegacyStorage; -use Icinga\Module\Businessprocess\BpNode; -use Icinga\Module\Businessprocess\HostNode; - -class CheckCommand extends Command +class CheckCommand extends ProcessCommand { - protected $storage; - - protected $hostColors = array( - 0 => array('black', 'lightgreen'), - 1 => array('black', 'lightred'), - 2 => array('black', 'brown'), - 99 => array('black', 'lightgray'), - ); - - protected $serviceColors = array( - 0 => array('black', 'lightgreen'), - 1 => array('black', 'yellow'), - 2 => array('black', 'lightred'), - 3 => array('black', 'lightpurple'), - 99 => array('black', 'lightgray'), - ); - - public function init() + public function listActions() { - $this->storage = new LegacyStorage($this->Config()->getSection('global')); + return array('process'); } /** - * Check a specific process + * 'check process' is DEPRECATED, please use 'process check' instead * * USAGE * @@ -40,77 +18,6 @@ class CheckCommand extends Command */ public function processAction() { - $name = $this->params->get('config'); - if ($name === null) { - $name = $this->getFirstProcessName(); - } - - $bp = $this->storage->loadProcess($name); - - if (null !== ($stateType = $this->params->get('state-type'))) { - if ($stateType === 'soft') { - $bp->useSoftStates(); - } - if ($stateType === 'hard') { - $bp->useHardStates(); - } - } - - $node = $bp->getNode($this->params->shift()); - $bp->retrieveStatesFromBackend(); - if ($bp->hasErrors()) { - printf( - "Checking Business Process %s failed: %s\n", - $node->getAlias(), - implode("\n", $bp->getErrors()) - ); - exit(3); - } - - printf("Business Process %s: %s\n", $node->getStateName(), $node->getAlias()); - if ($this->params->shift('details')) { - echo $this->renderProblemTree($node->getProblemTree(), $this->params->shift('colors')); - } - - exit($node->getState()); - } - - protected function renderProblemTree($tree, $useColors = false, $depth = 0) - { - $output = ''; - - foreach ($tree as $name => $subtree) { - $node = $subtree['node']; - - if ($node instanceof HostNode) { - $colors = $this->hostColors[$node->getState()]; - } else { - $colors = $this->serviceColors[$node->getState()]; - } - - $state = sprintf('[%s]', $node->getStateName()); - if ($useColors) { - $state = $this->screen->colorize($state, $colors[0], $colors[1]); - } - - $output .= sprintf( - "%s%s %s %s\n", - str_repeat(' ', $depth), - $node instanceof BpNode ? $node->getOperator() : '-', - $state, - $node->getAlias() - ); - $output .= $this->renderProblemTree($subtree['children'], $useColors, $depth + 1); - } - - $output = str_replace("|", "¦", $output); - - return $output; - } - - protected function getFirstProcessName() - { - $list = $this->storage->listProcesses(); - return key($list); + $this->checkAction(); } } diff --git a/application/clicommands/ProcessCommand.php b/application/clicommands/ProcessCommand.php new file mode 100644 index 0000000..a2974a5 --- /dev/null +++ b/application/clicommands/ProcessCommand.php @@ -0,0 +1,118 @@ + array('black', 'lightgreen'), + 1 => array('lightgray', 'lightred'), + 2 => array('black', 'brown'), + 99 => array('black', 'lightgray'), + ); + + protected $serviceColors = array( + 0 => array('black', 'lightgreen'), + 1 => array('black', 'yellow'), + 2 => array('lightgray', 'lightred'), + 3 => array('black', 'lightpurple'), + 99 => array('black', 'lightgray'), + ); + + public function init() + { + $this->storage = new LegacyStorage($this->Config()->getSection('global')); + } + + /** + * Check a specific process + * + * USAGE + * + * icingacli businessprocess process check [--config ] + */ + public function checkAction() + { + $name = $this->params->get('config'); + if ($name === null) { + $name = $this->getFirstProcessName(); + } + + $bp = $this->storage->loadProcess($name); + + if (null !== ($stateType = $this->params->get('state-type'))) { + if ($stateType === 'soft') { + $bp->useSoftStates(); + } + if ($stateType === 'hard') { + $bp->useHardStates(); + } + } + + /** @var BpNode $node */ + $node = $bp->getNode($this->params->shift()); + $bp->retrieveStatesFromBackend(); + if ($bp->hasErrors()) { + printf( + "Checking Business Process %s failed: %s\n", + $node->getAlias(), + implode("\n", $bp->getErrors()) + ); + exit(3); + } + + printf("Business Process %s: %s\n", $node->getStateName(), $node->getAlias()); + if ($this->params->shift('details')) { + echo $this->renderProblemTree($node->getProblemTree(), $this->params->shift('colors')); + } + + exit($node->getState()); + } + + protected function renderProblemTree($tree, $useColors = false, $depth = 0) + { + $output = ''; + + foreach ($tree as $name => $subtree) { + $node = $subtree['node']; + + if ($node instanceof HostNode) { + $colors = $this->hostColors[$node->getState()]; + } else { + $colors = $this->serviceColors[$node->getState()]; + } + + $state = sprintf('[%s]', $node->getStateName()); + if ($useColors) { + $state = $this->screen->colorize($state, $colors[0], $colors[1]); + } + + $output .= sprintf( + "%s%s %s %s\n", + str_repeat(' ', $depth), + $node instanceof BpNode ? $node->getOperator() : '-', + $state, + $node->getAlias() + ); + $output .= $this->renderProblemTree($subtree['children'], $useColors, $depth + 1); + } + + return $output; + } + + protected function getFirstProcessName() + { + $list = $this->storage->listProcesses(); + return key($list); + } +} From 656a5ada0eef812de2719d67d1d999fe0f904dd6 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 23 Nov 2016 15:29:14 +0100 Subject: [PATCH 041/256] ProcessCommand: add a 'list' action fixes #13293 --- application/clicommands/ProcessCommand.php | 23 ++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/application/clicommands/ProcessCommand.php b/application/clicommands/ProcessCommand.php index a2974a5..55a44f6 100644 --- a/application/clicommands/ProcessCommand.php +++ b/application/clicommands/ProcessCommand.php @@ -34,6 +34,29 @@ class ProcessCommand extends Command $this->storage = new LegacyStorage($this->Config()->getSection('global')); } + /** + * List all available process + * + * USAGE + * + * icingacli businessprocess list processes [options] + * + * OPTIONS + * + * --no-title Show only the process names and no related title + */ + public function listAction() + { + $noTitle = $this->params->shift('no-title'); + foreach ($this->storage->listProcesses() as $key => $title) { + if ($noTitle) { + echo $key . "\n"; + } else { + echo $title . "\n"; + } + } + } + /** * Check a specific process * From 4506181f0c9b7008128214a30705aeb808ddaf3d Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 23 Nov 2016 20:51:39 +0100 Subject: [PATCH 042/256] BusinessProcess,Node: new helper methods Mostly (but not only) useful for tests --- library/Businessprocess/BusinessProcess.php | 25 +++++++++++++++++++++ library/Businessprocess/Node.php | 11 +++++++++ 2 files changed, 36 insertions(+) diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index 6e89b57..88989c2 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -468,6 +468,26 @@ class BusinessProcess return $node; } + /** + * Create and attach a new process (BpNode) + * + * @param string $name Process name + * @param string $operator Operator (defaults to &) + * + * @return BpNode + */ + public function createBp($name, $operator = '&') + { + $node = new BpNode($this, (object) array( + 'name' => $name, + 'operator' => $operator, + 'child_names' => array(), + )); + + $this->addNode($name, $node); + return $node; + } + public function createImportedNode($config, $name) { $node = new ImportedNode($this, (object) array('name' => $name, 'configName' => $config)); @@ -475,6 +495,11 @@ class BusinessProcess return $node; } + /** + * @param $name + * @return Node + * @throws Exception + */ public function getNode($name) { if (array_key_exists($name, $this->nodes)) { diff --git a/library/Businessprocess/Node.php b/library/Businessprocess/Node.php index fba675b..6636db8 100644 --- a/library/Businessprocess/Node.php +++ b/library/Businessprocess/Node.php @@ -163,6 +163,17 @@ abstract class Node return $this; } + /** + * Forget my state + * + * @return $this + */ + public function clearState() + { + $this->state = null; + return $this; + } + public function setAck($ack = true) { $this->ack = $ack; From c774d686a0202518bf47784f1f3ce2a6f93f9f85 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 23 Nov 2016 20:55:57 +0100 Subject: [PATCH 043/256] NotOperatorTest: test NOT operator, parser and... ...state evaluation fixes #10315 --- .../Operators/NotOperatorTest.php | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 test/php/library/Businessprocess/Operators/NotOperatorTest.php diff --git a/test/php/library/Businessprocess/Operators/NotOperatorTest.php b/test/php/library/Businessprocess/Operators/NotOperatorTest.php new file mode 100644 index 0000000..d1fcbe9 --- /dev/null +++ b/test/php/library/Businessprocess/Operators/NotOperatorTest.php @@ -0,0 +1,85 @@ +emptyConfigSection()); + $expressions = array( + 'a = !b', + 'a = ! b', + 'a = b ! c ! d', + 'a = ! b ! c ! d !', + ); + + foreach ($expressions as $expression) { + $this->assertInstanceOf( + 'Icinga\\Module\\Businessprocess\\Businessprocess', + $storage->loadFromString('dummy', $expression) + ); + } + } + + public function testWhetherNegationsMatch() + { + $storage = new LegacyStorage($this->emptyConfigSection()); + $expression = 'a = !b'; + $bp = $storage->loadFromString('dummy', $expression); + $a = $bp->getNode('a'); + $b = $bp->createBp('b')->setState(3); + $this->assertEquals( + 'OK', + $a->getStateName() + ); + + $a->clearState(); + $b->setState(0); + $this->assertEquals( + 'CRITICAL', + $a->getStateName() + ); + } + + public function testWhetherNegatingMultipleValuesBehavesLikeNotAnd() + { + $storage = new LegacyStorage($this->emptyConfigSection()); + $expression = 'a = ! b ! c ! d'; + $bp = $storage->loadFromString('dummy', $expression); + $a = $bp->getNode('a'); + $b = $bp->createBp('b')->setState(3); + $c = $bp->createBp('c')->setState(3); + $d = $bp->createBp('d')->setState(3); + $this->assertEquals( + 'OK', + $a->getStateName() + ); + + $a->clearState(); + $b->setState(0); + $this->assertEquals( + 'OK', + $a->getStateName() + ); + + $a->clearState(); + $c->setState(0); + $this->assertEquals( + 'OK', + $a->getStateName() + ); + + $a->clearState(); + $d->setState(0); + $this->assertEquals( + 'CRITICAL', + $a->getStateName() + ); + } +} + From a19bbfed6374080beb77493fe344218540c942d6 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 23 Nov 2016 21:10:48 +0100 Subject: [PATCH 044/256] ProcessCommand: document new flags and options fixes #10866 --- application/clicommands/ProcessCommand.php | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/application/clicommands/ProcessCommand.php b/application/clicommands/ProcessCommand.php index 55a44f6..e96eff7 100644 --- a/application/clicommands/ProcessCommand.php +++ b/application/clicommands/ProcessCommand.php @@ -3,9 +3,10 @@ namespace Icinga\Module\Businessprocess\Clicommands; use Icinga\Cli\Command; -use Icinga\Module\Businessprocess\Storage\LegacyStorage; use Icinga\Module\Businessprocess\BpNode; use Icinga\Module\Businessprocess\HostNode; +use Icinga\Module\Businessprocess\Node; +use Icinga\Module\Businessprocess\Storage\LegacyStorage; class ProcessCommand extends Command { @@ -43,7 +44,7 @@ class ProcessCommand extends Command * * OPTIONS * - * --no-title Show only the process names and no related title + * --no-title Show only the process names and no related title */ public function listAction() { @@ -62,7 +63,16 @@ class ProcessCommand extends Command * * USAGE * - * icingacli businessprocess process check [--config ] + * icingacli businessprocess process check [options] + * + * OPTIONS + * + * --config Name of the config that contains + * --details Show problem details as a tree + * --colors Show colored output + * --state-type Define which state type to look at. Could be + * either soft or hard, overrides an eventually + * configured default */ public function checkAction() { @@ -107,6 +117,7 @@ class ProcessCommand extends Command $output = ''; foreach ($tree as $name => $subtree) { + /** @var Node $node */ $node = $subtree['node']; if ($node instanceof HostNode) { From 8b9716c9b2e6c2deaf9e6743bec8eb8c05165a9f Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 23 Nov 2016 22:41:10 +0100 Subject: [PATCH 045/256] Operators: add new tests and a related helper fixes #13299 --- library/Businessprocess/BusinessProcess.php | 14 +++ .../Operators/AndOperatorTest.php | 119 ++++++++++++++++++ .../Operators/MinOperatorTest.php | 118 +++++++++++++++++ .../Operators/OrOperatorTest.php | 119 ++++++++++++++++++ 4 files changed, 370 insertions(+) create mode 100644 test/php/library/Businessprocess/Operators/AndOperatorTest.php create mode 100644 test/php/library/Businessprocess/Operators/MinOperatorTest.php create mode 100644 test/php/library/Businessprocess/Operators/OrOperatorTest.php diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index 88989c2..c852e83 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -524,6 +524,20 @@ class BusinessProcess ); } + /** + * Set the state for a specific node + * + * @param string $name Node name + * @param int $state Desired state + * + * @return $this + */ + public function setNodeState($name, $state) + { + $this->getNode($name)->setState($state); + return $this; + } + public function addObjectName($name) { $this->all_checks[$name] = 1; diff --git a/test/php/library/Businessprocess/Operators/AndOperatorTest.php b/test/php/library/Businessprocess/Operators/AndOperatorTest.php new file mode 100644 index 0000000..fbd3791 --- /dev/null +++ b/test/php/library/Businessprocess/Operators/AndOperatorTest.php @@ -0,0 +1,119 @@ +emptyConfigSection()); + $expressions = array( + 'a = b', + 'a = b & c & d', + ); + + foreach ($expressions as $expression) { + $this->assertInstanceOf( + 'Icinga\\Module\\Businessprocess\\Businessprocess', + $storage->loadFromString('dummy', $expression) + ); + } + } + + public function testThreeTimesCriticalIsCritical() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 2); + $bp->setNodeState('c', 2); + $bp->setNodeState('d', 2); + + $this->assertEquals( + 'CRITICAL', + $bp->getNode('a')->getStateName() + ); + } + + public function testTwoTimesCriticalAndOkIsCritical() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 2); + $bp->setNodeState('c', 0); + $bp->setNodeState('d', 2); + + $this->assertEquals( + 'CRITICAL', + $bp->getNode('a')->getStateName() + ); + } + + public function testCriticalAndWarningAndOkIsCritical() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 2); + $bp->setNodeState('c', 1); + $bp->setNodeState('d', 0); + + $this->assertEquals( + 'CRITICAL', + $bp->getNode('a')->getStateName() + ); + } + + public function testUnknownAndWarningAndOkIsUnknown() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 0); + $bp->setNodeState('c', 1); + $bp->setNodeState('d', 3); + + $this->assertEquals( + 'UNKNOWN', + $bp->getNode('a')->getStateName() + ); + } + + public function testTwoTimesWarningAndOkIsWarning() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 0); + $bp->setNodeState('c', 1); + $bp->setNodeState('d', 1); + + $this->assertEquals( + 'WARNING', + $bp->getNode('a')->getStateName() + ); + } + + public function testThreeTimesOkIsOk() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 0); + $bp->setNodeState('c', 0); + $bp->setNodeState('d', 0); + + $this->assertEquals( + 'OK', + $bp->getNode('a')->getStateName() + ); + } + + /** + * @return BusinessProcess + */ + protected function getBp() + { + $storage = new LegacyStorage($this->emptyConfigSection()); + $expression = 'a = b & c & d'; + $bp = $storage->loadFromString('dummy', $expression); + $bp->createBp('b'); + $bp->createBp('c'); + $bp->createBp('d'); + + return $bp; + } +} diff --git a/test/php/library/Businessprocess/Operators/MinOperatorTest.php b/test/php/library/Businessprocess/Operators/MinOperatorTest.php new file mode 100644 index 0000000..43126dd --- /dev/null +++ b/test/php/library/Businessprocess/Operators/MinOperatorTest.php @@ -0,0 +1,118 @@ +emptyConfigSection()); + $expressions = array( + 'a = 1 of: b', + 'a = 2 of: b + c + d', + ); + $this->getName(); + foreach ($expressions as $expression) { + $this->assertInstanceOf( + 'Icinga\\Module\\Businessprocess\\Businessprocess', + $storage->loadFromString('dummy', $expression) + ); + } + } + public function testTwoOfThreeTimesCriticalAreAtLeastCritical() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 2); + $bp->setNodeState('c', 2); + $bp->setNodeState('d', 2); + + $this->assertEquals( + 'CRITICAL', + $bp->getNode('a')->getStateName() + ); + } + + public function testTwoOfTwoTimesCriticalAndUnknownAreAtLeastCritical() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 2); + $bp->setNodeState('c', 3); + $bp->setNodeState('d', 2); + + $this->assertEquals( + 'CRITICAL', + $bp->getNode('a')->getStateName() + ); + } + + public function testTwoOfCriticalAndWarningAndOkAreAtLeastWarning() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 2); + $bp->setNodeState('c', 1); + $bp->setNodeState('d', 0); + + $this->assertEquals( + 'WARNING', + $bp->getNode('a')->getStateName() + ); + } + + public function testTwoOfUnknownAndWarningAndCriticalAreAtLeastUnknown() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 2); + $bp->setNodeState('c', 1); + $bp->setNodeState('d', 3); + + $this->assertEquals( + 'UNKNOWN', + $bp->getNode('a')->getStateName() + ); + } + + public function testTwoOfTwoTimesWarningAndUnknownAreAtLeastWarning() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 0); + $bp->setNodeState('c', 1); + $bp->setNodeState('d', 1); + + $this->assertEquals( + 'WARNING', + $bp->getNode('a')->getStateName() + ); + } + + public function testTwoOfThreeTimesOkAreAtLeastOk() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 0); + $bp->setNodeState('c', 0); + $bp->setNodeState('d', 0); + + $this->assertEquals( + 'OK', + $bp->getNode('a')->getStateName() + ); + } + + /** + * @return BusinessProcess + */ + protected function getBp() + { + $storage = new LegacyStorage($this->emptyConfigSection()); + $expression = 'a = 2 of: b + c + d'; + $bp = $storage->loadFromString('dummy', $expression); + $bp->createBp('b'); + $bp->createBp('c'); + $bp->createBp('d'); + + return $bp; + } +} diff --git a/test/php/library/Businessprocess/Operators/OrOperatorTest.php b/test/php/library/Businessprocess/Operators/OrOperatorTest.php new file mode 100644 index 0000000..ac2f5f3 --- /dev/null +++ b/test/php/library/Businessprocess/Operators/OrOperatorTest.php @@ -0,0 +1,119 @@ +emptyConfigSection()); + $expressions = array( + 'a = b', + 'a = b | c | d', + ); + + foreach ($expressions as $expression) { + $this->assertInstanceOf( + 'Icinga\\Module\\Businessprocess\\Businessprocess', + $storage->loadFromString('dummy', $expression) + ); + } + } + + public function testThreeTimesCriticalIsCritical() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 2); + $bp->setNodeState('c', 2); + $bp->setNodeState('d', 2); + + $this->assertEquals( + 'CRITICAL', + $bp->getNode('a')->getStateName() + ); + } + + public function testTwoTimesCriticalOrUnknownIsUnknown() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 2); + $bp->setNodeState('c', 3); + $bp->setNodeState('d', 2); + + $this->assertEquals( + 'UNKNOWN', + $bp->getNode('a')->getStateName() + ); + } + + public function testCriticalOrWarningOrOkIsOk() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 2); + $bp->setNodeState('c', 1); + $bp->setNodeState('d', 0); + + $this->assertEquals( + 'OK', + $bp->getNode('a')->getStateName() + ); + } + + public function testUnknownOrWarningOrCriticalIsWarning() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 2); + $bp->setNodeState('c', 1); + $bp->setNodeState('d', 3); + + $this->assertEquals( + 'WARNING', + $bp->getNode('a')->getStateName() + ); + } + + public function testTwoTimesWarningAndOkIsOk() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 0); + $bp->setNodeState('c', 1); + $bp->setNodeState('d', 1); + + $this->assertEquals( + 'OK', + $bp->getNode('a')->getStateName() + ); + } + + public function testThreeTimesWarningIsWarning() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 1); + $bp->setNodeState('c', 1); + $bp->setNodeState('d', 1); + + $this->assertEquals( + 'WARNING', + $bp->getNode('a')->getStateName() + ); + } + + /** + * @return BusinessProcess + */ + protected function getBp() + { + $storage = new LegacyStorage($this->emptyConfigSection()); + $expression = 'a = b | c | d'; + $bp = $storage->loadFromString('dummy', $expression); + $bp->createBp('b'); + $bp->createBp('c'); + $bp->createBp('d'); + + return $bp; + } +} From d6f1d86ea4bf35cef8cb49b4cb5f44c83eb48ffc Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 23 Nov 2016 23:02:32 +0100 Subject: [PATCH 046/256] NotOperatorTest: make it easier to understand --- test/bootstrap.php | 2 +- .../Operators/NotOperatorTest.php | 117 +++++++++++++++++- 2 files changed, 112 insertions(+), 7 deletions(-) diff --git a/test/bootstrap.php b/test/bootstrap.php index 9a0fe64..40c16cf 100644 --- a/test/bootstrap.php +++ b/test/bootstrap.php @@ -2,7 +2,7 @@ use Icinga\Module\Businessprocess\Test\Bootstrap; -call_user_func(function() { +call_user_func(function () { $basedir = dirname(__DIR__); require_once $basedir . '/library/Businessprocess/Test/Bootstrap.php'; Bootstrap::cli($basedir); diff --git a/test/php/library/Businessprocess/Operators/NotOperatorTest.php b/test/php/library/Businessprocess/Operators/NotOperatorTest.php index d1fcbe9..0185d59 100644 --- a/test/php/library/Businessprocess/Operators/NotOperatorTest.php +++ b/test/php/library/Businessprocess/Operators/NotOperatorTest.php @@ -1,14 +1,14 @@ emptyConfigSection()); $expressions = array( @@ -26,7 +26,7 @@ class NotOperatorTest extends BaseTestCase } } - public function testWhetherNegationsMatch() + public function testASimpleNegationGivesTheCorrectResult() { $storage = new LegacyStorage($this->emptyConfigSection()); $expression = 'a = !b'; @@ -46,7 +46,7 @@ class NotOperatorTest extends BaseTestCase ); } - public function testWhetherNegatingMultipleValuesBehavesLikeNotAnd() + public function testAnExpressionGivesTheCorrectResultForVariousStateCombinations() { $storage = new LegacyStorage($this->emptyConfigSection()); $expression = 'a = ! b ! c ! d'; @@ -81,5 +81,110 @@ class NotOperatorTest extends BaseTestCase $a->getStateName() ); } -} + public function testThreeTimesCriticalIsOk() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 2); + $bp->setNodeState('c', 2); + $bp->setNodeState('d', 2); + + $this->assertEquals( + 'OK', + $bp->getNode('a')->getStateName() + ); + } + + public function testThreeTimesUnknownIsOk() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 3); + $bp->setNodeState('c', 3); + $bp->setNodeState('d', 3); + + $this->assertEquals( + 'OK', + $bp->getNode('a')->getStateName() + ); + } + + public function testThreeTimesWarningIsWarning() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 1); + $bp->setNodeState('c', 1); + $bp->setNodeState('d', 1); + + $this->assertEquals( + 'WARNING', + $bp->getNode('a')->getStateName() + ); + } + + public function testThreeTimesOkIsCritical() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 0); + $bp->setNodeState('c', 0); + $bp->setNodeState('d', 0); + + $this->assertEquals( + 'CRITICAL', + $bp->getNode('a')->getStateName() + ); + } + + public function testNotOkAndWarningAndCriticalIsOk() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 0); + $bp->setNodeState('c', 1); + $bp->setNodeState('d', 2); + + $this->assertEquals( + 'OK', + $bp->getNode('a')->getStateName() + ); + } + + public function testNotWarningAndUnknownAndCriticalIsOk() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 3); + $bp->setNodeState('c', 2); + $bp->setNodeState('d', 1); + + $this->assertEquals( + 'OK', + $bp->getNode('a')->getStateName() + ); + } + + public function testNotTwoTimesWarningAndOkIsWarning() + { + $bp = $this->getBp(); + $bp->setNodeState('b', 0); + $bp->setNodeState('c', 1); + $bp->setNodeState('d', 1); + + $this->assertEquals( + 'WARNING', + $bp->getNode('a')->getStateName() + ); + } + + /** + * @return BusinessProcess + */ + protected function getBp() + { + $storage = new LegacyStorage($this->emptyConfigSection()); + $expression = 'a = ! b ! c ! d'; + $bp = $storage->loadFromString('dummy', $expression); + $bp->createBp('b'); + $bp->createBp('c'); + $bp->createBp('d'); + + return $bp; + } +} From 12a9459eafde3ca20fc0788c2ae015a55b4b921a Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 23 Nov 2016 23:03:48 +0100 Subject: [PATCH 047/256] LegacyStorageTest: improve readability... ...and move config "loading" to BaseTestCase --- library/Businessprocess/Test/BaseTestCase.php | 6 ++++ .../Storage/LegacyStorageTest.php | 34 ++++++++++--------- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/library/Businessprocess/Test/BaseTestCase.php b/library/Businessprocess/Test/BaseTestCase.php index 8154bd4..0a74885 100644 --- a/library/Businessprocess/Test/BaseTestCase.php +++ b/library/Businessprocess/Test/BaseTestCase.php @@ -2,6 +2,7 @@ namespace Icinga\Module\Businessprocess\Test; +use Icinga\Application\Config; use Icinga\Application\ApplicationBootstrap; use Icinga\Application\Icinga; use PHPUnit_Framework_TestCase; @@ -19,6 +20,11 @@ abstract class BaseTestCase extends PHPUnit_Framework_TestCase $this->app(); } + protected function emptyConfigSection() + { + return Config::module('businessprocess')->getSection('global'); + } + /** * @param null $subDir * @return string diff --git a/test/php/library/Businessprocess/Storage/LegacyStorageTest.php b/test/php/library/Businessprocess/Storage/LegacyStorageTest.php index 985ddf3..5abcc87 100644 --- a/test/php/library/Businessprocess/Storage/LegacyStorageTest.php +++ b/test/php/library/Businessprocess/Storage/LegacyStorageTest.php @@ -2,23 +2,21 @@ namespace Tests\Icinga\Module\Businessprocess\Storage; -use Icinga\Application\Config; use Icinga\Module\Businessprocess\Test\BaseTestCase; use Icinga\Module\Businessprocess\Storage\LegacyStorage; -use Icinga\Module\Businessprocess\Storage\Storage; class LegacyStorageTest extends BaseTestCase { - public function testWhetherItCanBeInstantiatedWithAnEmptyConfigSection() + public function testCanBeInstantiatedWithAnEmptyConfigSection() { $baseClass = 'Icinga\\Module\\Businessprocess\\Storage\\LegacyStorage'; $this->assertInstanceOf( $baseClass, - new LegacyStorage(Config::module('businessprocess')->getSection('global')) + new LegacyStorage($this->emptyConfigSection()) ); } - public function testWhetherDefaultConfigDirIsDetermined() + public function testDefaultConfigDirIsDiscoveredCorrectly() { $this->assertEquals( $this->getTestsBaseDir('config/modules/businessprocess/processes'), @@ -26,7 +24,7 @@ class LegacyStorageTest extends BaseTestCase ); } - public function testWhetherAllProcessesAreListed() + public function testAllAvailableProcessesAreListed() { $keys = array_keys($this->makeInstance()->listProcesses()); sort($keys); @@ -40,7 +38,7 @@ class LegacyStorageTest extends BaseTestCase ); } - public function testWhetherHeadersAreRespectedInProcessList() + public function testHeaderTitlesAreRespectedInProcessList() { $keys = array_values($this->makeInstance()->listProcesses()); sort($keys); @@ -54,7 +52,7 @@ class LegacyStorageTest extends BaseTestCase ); } - public function testWhetherProcessFilenameIsReturned() + public function testProcessFilenameIsReturned() { $this->assertEquals( $this->getTestsBaseDir('config/modules/businessprocess/processes/simple_with-header.conf'), @@ -62,21 +60,21 @@ class LegacyStorageTest extends BaseTestCase ); } - public function testWhetherExistingProcessExists() + public function testAnExistingProcessExists() { $this->assertTrue( $this->makeInstance()->hasProcess('simple_with-header') ); } - public function testWhetherMissingProcessIsMissing() + public function testAMissingProcessIsMissing() { $this->assertFalse( $this->makeInstance()->hasProcess('simple_with-headerx') ); } - public function testWhetherValidProcessCanBeLoaded() + public function testAValidProcessCanBeLoaded() { $processClass = 'Icinga\\Module\\Businessprocess\\BusinessProcess'; $this->assertInstanceOf( @@ -85,7 +83,7 @@ class LegacyStorageTest extends BaseTestCase ); } - public function testWhetherConfigCanBeLoadedFromAString() + public function testProcessConfigCanBeLoadedFromAString() { $processClass = 'Icinga\\Module\\Businessprocess\\BusinessProcess'; $this->assertInstanceOf( @@ -94,10 +92,14 @@ class LegacyStorageTest extends BaseTestCase ); } - public function testWhetherProcessSourceCanBeFetched() + public function testFullProcessSourceCanBeFetched() { $this->assertEquals( - file_get_contents($this->getTestsBaseDir('config/modules/businessprocess/processes/simple_with-header.conf')), + file_get_contents( + $this->getTestsBaseDir( + 'config/modules/businessprocess/processes/simple_with-header.conf' + ) + ), $this->makeInstance()->getSource('simple_with-header') ); } @@ -107,6 +109,6 @@ class LegacyStorageTest extends BaseTestCase */ protected function makeInstance() { - return new LegacyStorage(Config::module('businessprocess')->getSection('global')); + return new LegacyStorage($this->emptyConfigSection()); } -} \ No newline at end of file +} From e6c292333b3881066382a6ad92041ddf3e514b64 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 24 Nov 2016 00:20:59 +0100 Subject: [PATCH 048/256] FakeRequest, Url-Wrapper: allow tests involving... ...Url::fromPath fixes #13301 --- library/Businessprocess/Test/BaseTestCase.php | 2 + .../Web/Component/Component.php | 15 +++-- .../Businessprocess/Web/Component/Link.php | 2 +- library/Businessprocess/Web/FakeRequest.php | 26 +++++++++ library/Businessprocess/Web/Url.php | 55 +++++++++++++++++++ 5 files changed, 94 insertions(+), 6 deletions(-) create mode 100644 library/Businessprocess/Web/FakeRequest.php create mode 100644 library/Businessprocess/Web/Url.php diff --git a/library/Businessprocess/Test/BaseTestCase.php b/library/Businessprocess/Test/BaseTestCase.php index 0a74885..8673cda 100644 --- a/library/Businessprocess/Test/BaseTestCase.php +++ b/library/Businessprocess/Test/BaseTestCase.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Businessprocess\Test; use Icinga\Application\Config; use Icinga\Application\ApplicationBootstrap; use Icinga\Application\Icinga; +use Icinga\Module\Businessprocess\Web\FakeRequest; use PHPUnit_Framework_TestCase; abstract class BaseTestCase extends PHPUnit_Framework_TestCase @@ -18,6 +19,7 @@ abstract class BaseTestCase extends PHPUnit_Framework_TestCase public function setUp() { $this->app(); + FakeRequest::setConfiguredBaseUrl('/icingaweb2/'); } protected function emptyConfigSection() diff --git a/library/Businessprocess/Web/Component/Component.php b/library/Businessprocess/Web/Component/Component.php index 3830e06..eaec881 100644 --- a/library/Businessprocess/Web/Component/Component.php +++ b/library/Businessprocess/Web/Component/Component.php @@ -42,12 +42,17 @@ abstract class Component protected function discoveredView() { if (self::$discoveredView === null) { - $viewRenderer = Icinga::app()->getViewRenderer(); - if ($viewRenderer->view === null) { - $viewRenderer->initView(); - } + $icinga = Icinga::app(); + if ($icinga->isCli()) { + self::$discoveredView = new View(); + } else { + $viewRenderer = $icinga->getViewRenderer(); + if ($viewRenderer->view === null) { + $viewRenderer->initView(); + } - self::$discoveredView = $viewRenderer->view; + self::$discoveredView = $viewRenderer->view; + } } return self::$discoveredView; diff --git a/library/Businessprocess/Web/Component/Link.php b/library/Businessprocess/Web/Component/Link.php index 525694c..5f82b59 100644 --- a/library/Businessprocess/Web/Component/Link.php +++ b/library/Businessprocess/Web/Component/Link.php @@ -2,7 +2,7 @@ namespace Icinga\Module\Businessprocess\Web\Component; -use Icinga\Web\Url; +use Icinga\Module\Businessprocess\Web\Url; class Link extends Component { diff --git a/library/Businessprocess/Web/FakeRequest.php b/library/Businessprocess/Web/FakeRequest.php new file mode 100644 index 0000000..6735e81 --- /dev/null +++ b/library/Businessprocess/Web/FakeRequest.php @@ -0,0 +1,26 @@ +setPath($url); + } + + $parts = parse_url($url); + + $self->setBasePath($request->getBaseUrl()); + if (isset($parts['path'])) { + $self->setPath($parts['path']); + } + + if (isset($parts['fragment'])) { + $self->setAnchor($parts['fragment']); + } + + $self->setParams($params); + return $self; + } + + protected static function getRequest() + { + $app = Icinga::app(); + if ($app->isCli()) { + return new FakeRequest(); + } else { + return $app->getRequest(); + } + } + +} \ No newline at end of file From 8c12491a84a632df675bad70f165cf18cdd513e6 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 24 Nov 2016 00:29:30 +0100 Subject: [PATCH 049/256] Host/ServiceNodeTest: just a few simple tests fixes #13303 --- .../library/Businessprocess/HostNodeTest.php | 48 ++++++++++++++++ .../Businessprocess/ServiceNodeTest.php | 57 +++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 test/php/library/Businessprocess/HostNodeTest.php create mode 100644 test/php/library/Businessprocess/ServiceNodeTest.php diff --git a/test/php/library/Businessprocess/HostNodeTest.php b/test/php/library/Businessprocess/HostNodeTest.php new file mode 100644 index 0000000..bfcecf2 --- /dev/null +++ b/test/php/library/Businessprocess/HostNodeTest.php @@ -0,0 +1,48 @@ +assertEquals( + 'localhost', + $this->localhost()->getHostname() + ); + } + + public function testReturnsCorrectAlias() + { + $this->assertEquals( + 'localhost', + $this->localhost()->getAlias() + ); + } + + public function testRendersCorrectLink() + { + $this->assertEquals( + '' + . 'localhost: ping <> pong', + $this->localhost()->renderLink(new View()) + ); + } + + /** + * @return HostNode + */ + protected function localhost() + { + $bp = new BusinessProcess(); + return new HostNode($bp, (object) array( + 'hostname' => 'localhost', + 'state' => 0, + )); + } +} \ No newline at end of file diff --git a/test/php/library/Businessprocess/ServiceNodeTest.php b/test/php/library/Businessprocess/ServiceNodeTest.php new file mode 100644 index 0000000..0d3d794 --- /dev/null +++ b/test/php/library/Businessprocess/ServiceNodeTest.php @@ -0,0 +1,57 @@ +assertEquals( + 'localhost', + $this->pingOnLocalhost()->getHostname() + ); + } + + public function testReturnsCorrectServiceDescription() + { + $this->assertEquals( + 'ping <> pong', + $this->pingOnLocalhost()->getServiceDescription() + ); + } + + public function testReturnsCorrectAlias() + { + $this->assertEquals( + 'localhost: ping <> pong', + $this->pingOnLocalhost()->getAlias() + ); + } + + public function testRendersCorrectLink() + { + $this->assertEquals( + '' + . 'localhost: ping <> pong', + $this->pingOnLocalhost()->renderLink(new View()) + ); + } + + /** + * @return ServiceNode + */ + protected function pingOnLocalhost() + { + $bp = new BusinessProcess(); + return new ServiceNode($bp, (object) array( + 'hostname' => 'localhost', + 'service' => 'ping <> pong', + 'state' => 0, + )); + } +} \ No newline at end of file From 91087b73ab6cfde028d0c0ad5054760eb461bf95 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 24 Nov 2016 00:30:34 +0100 Subject: [PATCH 050/256] Host/ServiceNode: fix tests using Link component That way it's no longer directly based on Icinga\Web\Url and runs through. Also fixed wrong expectations in HostNodeTest --- library/Businessprocess/HostNode.php | 5 +++-- library/Businessprocess/ServiceNode.php | 10 +++++++--- test/php/library/Businessprocess/HostNodeTest.php | 4 ++-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/library/Businessprocess/HostNode.php b/library/Businessprocess/HostNode.php index 4c2496e..40341e8 100644 --- a/library/Businessprocess/HostNode.php +++ b/library/Businessprocess/HostNode.php @@ -2,7 +2,8 @@ namespace Icinga\Module\Businessprocess; -use Icinga\Web\Url; +use Icinga\Module\Businessprocess\Web\Component\Link; +use Icinga\Module\Businessprocess\Web\Url; class HostNode extends MonitoredNode { @@ -56,7 +57,7 @@ class HostNode extends MonitoredNode if ($this->bp->hasBackendName()) { $params['backend'] = $this->bp->getBackendName(); } - return $view->qlink($this->hostname, 'monitoring/host/show', $params); + return Link::create($this->hostname, 'monitoring/host/show', $params)->render(); } protected function getActionIcons($view) diff --git a/library/Businessprocess/ServiceNode.php b/library/Businessprocess/ServiceNode.php index 299c679..3c984fd 100644 --- a/library/Businessprocess/ServiceNode.php +++ b/library/Businessprocess/ServiceNode.php @@ -2,7 +2,8 @@ namespace Icinga\Module\Businessprocess; -use Icinga\Web\Url; +use Icinga\Module\Businessprocess\Web\Component\Link; +use Icinga\Module\Businessprocess\Web\Url; class ServiceNode extends MonitoredNode { @@ -38,9 +39,12 @@ class ServiceNode extends MonitoredNode if ($this->bp->hasBackendName()) { $params['backend'] = $this->bp->getBackendName(); } - $link = $view->qlink($this->getAlias(), 'monitoring/service/show', $params); - return $link; + return Link::create( + $this->getAlias(), + 'monitoring/service/show', + $params + )->render(); } protected function getActionIcons($view) diff --git a/test/php/library/Businessprocess/HostNodeTest.php b/test/php/library/Businessprocess/HostNodeTest.php index bfcecf2..af0c65c 100644 --- a/test/php/library/Businessprocess/HostNodeTest.php +++ b/test/php/library/Businessprocess/HostNodeTest.php @@ -20,7 +20,7 @@ class HostNodeTest extends BaseTestCase public function testReturnsCorrectAlias() { $this->assertEquals( - 'localhost', + 'localhost;Hoststatus', $this->localhost()->getAlias() ); } @@ -29,7 +29,7 @@ class HostNodeTest extends BaseTestCase { $this->assertEquals( '' - . 'localhost: ping <> pong', + . 'localhost', $this->localhost()->renderLink(new View()) ); } From d51dc6cd07037b4967c4084b0ceb45f4032bad94 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 24 Nov 2016 00:48:50 +0100 Subject: [PATCH 051/256] NotOperatorTest: remove obsolete test --- .../Operators/NotOperatorTest.php | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/test/php/library/Businessprocess/Operators/NotOperatorTest.php b/test/php/library/Businessprocess/Operators/NotOperatorTest.php index 0185d59..9711b1e 100644 --- a/test/php/library/Businessprocess/Operators/NotOperatorTest.php +++ b/test/php/library/Businessprocess/Operators/NotOperatorTest.php @@ -46,42 +46,6 @@ class NotOperatorTest extends BaseTestCase ); } - public function testAnExpressionGivesTheCorrectResultForVariousStateCombinations() - { - $storage = new LegacyStorage($this->emptyConfigSection()); - $expression = 'a = ! b ! c ! d'; - $bp = $storage->loadFromString('dummy', $expression); - $a = $bp->getNode('a'); - $b = $bp->createBp('b')->setState(3); - $c = $bp->createBp('c')->setState(3); - $d = $bp->createBp('d')->setState(3); - $this->assertEquals( - 'OK', - $a->getStateName() - ); - - $a->clearState(); - $b->setState(0); - $this->assertEquals( - 'OK', - $a->getStateName() - ); - - $a->clearState(); - $c->setState(0); - $this->assertEquals( - 'OK', - $a->getStateName() - ); - - $a->clearState(); - $d->setState(0); - $this->assertEquals( - 'CRITICAL', - $a->getStateName() - ); - } - public function testThreeTimesCriticalIsOk() { $bp = $this->getBp(); From cdbd28b25b669c37e753353f2a1f0ac90b4569c4 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 24 Nov 2016 00:49:09 +0100 Subject: [PATCH 052/256] Link: play nice with those different Url objects --- library/Businessprocess/Web/Component/Link.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/library/Businessprocess/Web/Component/Link.php b/library/Businessprocess/Web/Component/Link.php index 5f82b59..149c7af 100644 --- a/library/Businessprocess/Web/Component/Link.php +++ b/library/Businessprocess/Web/Component/Link.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Businessprocess\Web\Component; use Icinga\Module\Businessprocess\Web\Url; +use Icinga\Web\Url as WebUrl; class Link extends Component { @@ -31,14 +32,18 @@ class Link extends Component { $link = new static(); $link->text = $text; - if ($url instanceof Url) { + if ($url instanceof WebUrl) { // Hint: Url is also a WebUrl if ($urlParams !== null) { $url->addParams($urlParams); } $link->url = $url; } else { - $link->url = Url::fromPath($url, $urlParams); + if ($urlParams === null) { + $link->url = Url::fromPath($url); + } else { + $link->url = Url::fromPath($url, $urlParams); + } } $link->attributes = new Attributes($attributes); From b8df6a8823469c01a47ef4ef9c30ea0f5e8ab3c8 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 24 Nov 2016 02:40:01 +0100 Subject: [PATCH 053/256] BpNode: rename child_nodes to childNode... ...and move method related to children from base Node --- library/Businessprocess/BpNode.php | 45 ++++++++++++++++++++++++++---- library/Businessprocess/Node.php | 15 ---------- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/library/Businessprocess/BpNode.php b/library/Businessprocess/BpNode.php index 895836e..49a1421 100644 --- a/library/Businessprocess/BpNode.php +++ b/library/Businessprocess/BpNode.php @@ -3,7 +3,6 @@ namespace Icinga\Module\Businessprocess; use Icinga\Exception\ConfigurationError; -use Icinga\Exception\ProgrammingError; class BpNode extends Node { @@ -14,8 +13,12 @@ class BpNode extends Node protected $url; protected $info_command; protected $display = 0; + + /** @var Node[] */ protected $children; - protected $child_names = array(); + + /** @var array */ + protected $childNames = array(); protected $alias; protected $counters; protected $missing = null; @@ -89,6 +92,30 @@ class BpNode extends Node return false; } + /** + * @param Node $node + * @return $this + * @throws ConfigurationError + */ + public function addChild(Node $node) + { + if ($this->children === null) { + $this->getChildren(); + } + + $name = (string) $node; + if (array_key_exists($name, $this->children)) { + throw new ConfigurationError( + 'Node "%s" has been defined more than once', + $name + ); + } + $this->children[$name] = $node; + $this->children[] = $name; + $node->addParent($this); + return $this; + } + public function getProblematicChildren() { $problems = array(); @@ -304,26 +331,32 @@ class BpNode extends Node public function setChildNames($names) { sort($names); - $this->child_names = $names; + $this->childNames = $names; $this->children = null; return $this; } + public function hasChildren($filter = null) + { + return !empty($this->childNames); + } + public function getChildNames() { - return $this->child_names; + return $this->childNames; } public function getChildren($filter = null) { if ($this->children === null) { $this->children = array(); - natsort($this->child_names); - foreach ($this->child_names as $name) { + natsort($this->childNames); + foreach ($this->childNames as $name) { $this->children[$name] = $this->bp->getNode($name); $this->children[$name]->addParent($this); } } + return $this->children; } diff --git a/library/Businessprocess/Node.php b/library/Businessprocess/Node.php index 6636db8..96fdfef 100644 --- a/library/Businessprocess/Node.php +++ b/library/Businessprocess/Node.php @@ -142,21 +142,6 @@ abstract class Node return false; } - public function addChild(Node $node) - { - if (array_key_exists((string) $node, $this->children)) { - throw new Exception( - sprintf( - 'Node "%s" has been defined more than once', - $node - ) - ); - } - $this->childs[(string) $node] = $node; - $node->addParent($this); - return $this; - } - public function setState($state) { $this->state = (int) $state; From 2fc48f2124fd282e41f069c7599f22d29d8c3780 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 24 Nov 2016 09:39:44 +0100 Subject: [PATCH 054/256] js: Handle collapsed state of multiple trees fixes #8569 --- application/views/scripts/process/show.phtml | 2 +- library/Businessprocess/BusinessProcess.php | 5 +++++ public/js/module.js | 20 +++++++++++--------- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/application/views/scripts/process/show.phtml b/application/views/scripts/process/show.phtml index 4ee8336..5c1bedc 100644 --- a/application/views/scripts/process/show.phtml +++ b/application/views/scripts/process/show.phtml @@ -57,7 +57,7 @@ use Icinga\Module\Businessprocess\BusinessProcess; -
+
bp->renderHtml($this) ?> bpconfig->isLocked()): ?> bp instanceof BusinessProcess): /* do not render when showing subtree */ ?> diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index c852e83..af90113 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -171,6 +171,11 @@ class BusinessProcess return $this->name; } + public function getHtmlId() + { + return 'businessprocess-' . preg_replace('/[\r\n\t\s]/', '_', $this->getName()); + } + public function setTitle($title) { $this->title = $title; diff --git a/public/js/module.js b/public/js/module.js index a612aa3..09372dd 100644 --- a/public/js/module.js +++ b/public/js/module.js @@ -134,20 +134,20 @@ }, fixOpenedBps: function(event) { - var $container = $(event.currentTarget); - var container_id = $container.attr('id'); + var $bpDiv = $(event.currentTarget).find('div.bp'); + var bpName = $bpDiv.attr('id'); - if (typeof this.idCache[container_id] === 'undefined') { + if (typeof this.idCache[bpName] === 'undefined') { return; } - var $procs = $('table.process', $container); - $.each(this.idCache[$container.attr('id')], function(idx, id) { + 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)); - }); }); @@ -160,9 +160,11 @@ * Only get the deepest nodes to keep requests as small as possible */ rememberOpenedBps: function (event) { - var $container = $(event.currentTarget); var ids = []; - $('table.process', $container) + 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) { @@ -172,7 +174,7 @@ return; } - this.idCache[$container.attr('id')] = ids; + this.idCache[$bpName] = ids; } }; From 8c05ed7277bade314bc11e72a597e5968ad85dec Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 24 Nov 2016 10:57:30 +0100 Subject: [PATCH 055/256] Test and implement loop detection fixes #9786 --- library/Businessprocess/BpNode.php | 62 ++++++++++++++++++- library/Businessprocess/BusinessProcess.php | 18 +++++- library/Businessprocess/Test/BaseTestCase.php | 21 +++++++ .../library/Businessprocess/BpNodeTest.php | 41 ++++++++++++ .../Storage/LegacyStorageTest.php | 18 +++--- 5 files changed, 148 insertions(+), 12 deletions(-) create mode 100644 test/php/library/Businessprocess/BpNodeTest.php diff --git a/library/Businessprocess/BpNode.php b/library/Businessprocess/BpNode.php index 49a1421..374c304 100644 --- a/library/Businessprocess/BpNode.php +++ b/library/Businessprocess/BpNode.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Businessprocess; use Icinga\Exception\ConfigurationError; +use Icinga\Module\Businessprocess\Exception\NestingError; class BpNode extends Node { @@ -44,6 +45,8 @@ class BpNode extends Node 0 => 4 ); + protected static $loopDetection = array(); + protected $className = 'process'; public function __construct(BusinessProcess $bp, $object) @@ -262,11 +265,24 @@ class BpNode extends Node return $this; } + /** + * @return int + */ public function getState() { if ($this->state === null) { - $this->calculateState(); + try { + $this->reCalculateState(); + } catch (NestingError $e) { + $this->bp->addError( + $this->bp->translate('Nesting error detected: %s'), + $e->getMessage() + ); + + $this->state = 3; + } } + return $this->state; } @@ -275,13 +291,23 @@ class BpNode extends Node return self::$sortStateInversionMap[$state >> self::SHIFT_FLAGS] << self::SHIFT_FLAGS; } - protected function calculateState() + /** + * @return $this + */ + public function reCalculateState() { $sort_states = array(); $lastStateChange = 0; + if (!$this->hasChildren()) { + $this->state = 0; + return $this; + } + foreach ($this->getChildren() as $child) { + $this->beginLoopDetection(); $sort_states[] = $child->getSortingState(); $lastStateChange = max($lastStateChange, $child->getLastStateChange()); + $this->endLoopDetection(); } $this->setLastStateChange($lastStateChange); @@ -315,6 +341,38 @@ class BpNode extends Node } $this->state = $this->sortStateTostate($sort_state); + return $this; + } + + protected function beginLoopDetection() + { + $name = $this->name; + if (array_key_exists($name, self::$loopDetection)) { + $loop = array_keys(self::$loopDetection); + $loop[] = $name; + self::$loopDetection = array(); + throw new NestingError('Loop detected: %s', implode(' -> ', $loop)); + } + + self::$loopDetection[$name] = true; + } + + protected function endLoopDetection() + { + unset(self::$loopDetection[$this->name]); + } + + public function checkForLoops() + { + foreach ($this->getChildren() as $child) { + $this->beginLoopDetection(); + if ($child instanceof BpNode) { + $child->checkForLoops(); + } + $this->endLoopDetection(); + } + + return $this; } public function setDisplay($display) diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index af90113..e1b0005 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -649,7 +649,7 @@ class BusinessProcess return $errors; } - protected function translate($msg) + public function translate($msg) { return mt('businessprocess', $msg); } @@ -670,6 +670,22 @@ class BusinessProcess } } + /** + * @param string $msg,... + * + * @return $this + */ + public function addError($msg) + { + $args = func_get_args(); + array_shift($args); + $this->errors[] = vsprintf($msg, $args); + return $this; + } + + /** + * @deprecated + */ protected function error($msg) { $args = func_get_args(); diff --git a/library/Businessprocess/Test/BaseTestCase.php b/library/Businessprocess/Test/BaseTestCase.php index 8673cda..2113494 100644 --- a/library/Businessprocess/Test/BaseTestCase.php +++ b/library/Businessprocess/Test/BaseTestCase.php @@ -5,6 +5,8 @@ namespace Icinga\Module\Businessprocess\Test; use Icinga\Application\Config; use Icinga\Application\ApplicationBootstrap; use Icinga\Application\Icinga; +use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\Storage\LegacyStorage; use Icinga\Module\Businessprocess\Web\FakeRequest; use PHPUnit_Framework_TestCase; @@ -27,6 +29,25 @@ abstract class BaseTestCase extends PHPUnit_Framework_TestCase return Config::module('businessprocess')->getSection('global'); } + /*** + * @return BusinessProcess + */ + protected function makeLoop() + { + return $this->makeInstance()->loadFromString( + 'loop', + "a = b\nb = c\nc = a\nd = a" + ); + } + + /** + * @return LegacyStorage + */ + protected function makeInstance() + { + return new LegacyStorage($this->emptyConfigSection()); + } + /** * @param null $subDir * @return string diff --git a/test/php/library/Businessprocess/BpNodeTest.php b/test/php/library/Businessprocess/BpNodeTest.php new file mode 100644 index 0000000..b9ef61c --- /dev/null +++ b/test/php/library/Businessprocess/BpNodeTest.php @@ -0,0 +1,41 @@ +makeLoop()->getNode('d'); + $bpNode->checkForLoops(); + } + + /** + * @expectedExceptionMessage d -> a -> b -> c -> a + * @expectedException \Icinga\Module\Businessprocess\Exception\NestingError + */ + public function testNestingErrorReportsFullLoop() + { + /** @var BpNode $bpNode */ + $bpNode = $this->makeLoop()->getNode('d'); + $bpNode->checkForLoops(); + } + + public function testStateForALoopGivesUnknown() + { + $loop = $this->makeLoop(); + /** @var BpNode $bpNode */ + $bpNode = $loop->getNode('d'); + $this->assertEquals( + 'UNKNOWN', + $bpNode->getStateName() + ); + } +} diff --git a/test/php/library/Businessprocess/Storage/LegacyStorageTest.php b/test/php/library/Businessprocess/Storage/LegacyStorageTest.php index 5abcc87..77adacf 100644 --- a/test/php/library/Businessprocess/Storage/LegacyStorageTest.php +++ b/test/php/library/Businessprocess/Storage/LegacyStorageTest.php @@ -7,6 +7,8 @@ use Icinga\Module\Businessprocess\Storage\LegacyStorage; class LegacyStorageTest extends BaseTestCase { + private $processClass = 'Icinga\\Module\\Businessprocess\\BusinessProcess'; + public function testCanBeInstantiatedWithAnEmptyConfigSection() { $baseClass = 'Icinga\\Module\\Businessprocess\\Storage\\LegacyStorage'; @@ -76,18 +78,16 @@ class LegacyStorageTest extends BaseTestCase public function testAValidProcessCanBeLoaded() { - $processClass = 'Icinga\\Module\\Businessprocess\\BusinessProcess'; $this->assertInstanceOf( - $processClass, + $this->processClass, $this->makeInstance()->loadProcess('simple_with-header') ); } public function testProcessConfigCanBeLoadedFromAString() { - $processClass = 'Icinga\\Module\\Businessprocess\\BusinessProcess'; $this->assertInstanceOf( - $processClass, + $this->processClass, $this->makeInstance()->loadFromString('dummy', 'a = Host1;ping & Host2;ping') ); } @@ -104,11 +104,11 @@ class LegacyStorageTest extends BaseTestCase ); } - /** - * @return LegacyStorage - */ - protected function makeInstance() + public function testAConfiguredLoopCanBeParsed() { - return new LegacyStorage($this->emptyConfigSection()); + $this->assertInstanceOf( + $this->processClass, + $this->makeLoop() + ); } } From 08f56452fcda994e762e46447a252f7e58de4b63 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Sat, 26 Nov 2016 21:13:54 +0100 Subject: [PATCH 056/256] NodeTests: phpcs --- test/php/library/Businessprocess/HostNodeTest.php | 2 +- test/php/library/Businessprocess/ServiceNodeTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/php/library/Businessprocess/HostNodeTest.php b/test/php/library/Businessprocess/HostNodeTest.php index af0c65c..7db45db 100644 --- a/test/php/library/Businessprocess/HostNodeTest.php +++ b/test/php/library/Businessprocess/HostNodeTest.php @@ -45,4 +45,4 @@ class HostNodeTest extends BaseTestCase 'state' => 0, )); } -} \ No newline at end of file +} diff --git a/test/php/library/Businessprocess/ServiceNodeTest.php b/test/php/library/Businessprocess/ServiceNodeTest.php index 0d3d794..a9a4d4d 100644 --- a/test/php/library/Businessprocess/ServiceNodeTest.php +++ b/test/php/library/Businessprocess/ServiceNodeTest.php @@ -54,4 +54,4 @@ class ServiceNodeTest extends BaseTestCase 'state' => 0, )); } -} \ No newline at end of file +} From 1f61d8b728c4dcf0421d3b27619602e415fa9ce8 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Sat, 26 Nov 2016 21:14:38 +0100 Subject: [PATCH 057/256] NestingError: add missing exception class --- library/Businessprocess/Exception/NestingError.php | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 library/Businessprocess/Exception/NestingError.php diff --git a/library/Businessprocess/Exception/NestingError.php b/library/Businessprocess/Exception/NestingError.php new file mode 100644 index 0000000..89cbf81 --- /dev/null +++ b/library/Businessprocess/Exception/NestingError.php @@ -0,0 +1,9 @@ + Date: Sat, 26 Nov 2016 21:18:18 +0100 Subject: [PATCH 058/256] Cleanup, Url handling improvements, some helpers --- library/Businessprocess/BpNode.php | 2 +- library/Businessprocess/BusinessProcess.php | 3 ++ library/Businessprocess/Controller.php | 37 +++++++++++++++++-- library/Businessprocess/Node.php | 5 +++ library/Businessprocess/Simulation.php | 6 ++- .../Businessprocess/Storage/LegacyStorage.php | 2 + library/Businessprocess/Web/FakeRequest.php | 2 +- library/Businessprocess/Web/Url.php | 20 ++++++++-- 8 files changed, 68 insertions(+), 9 deletions(-) diff --git a/library/Businessprocess/BpNode.php b/library/Businessprocess/BpNode.php index 374c304..52086e6 100644 --- a/library/Businessprocess/BpNode.php +++ b/library/Businessprocess/BpNode.php @@ -63,7 +63,7 @@ class BpNode extends Node $this->getState(); $this->counters = self::$emptyStateSummary; - foreach ($this->children as $child) { + foreach ($this->getChildren() as $child) { if ($child instanceof BpNode) { $counters = $child->getStateSummary(); foreach ($counters as $k => $v) { diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index e1b0005..526c135 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -425,6 +425,9 @@ class BusinessProcess } } + /** + * @return BpNode[] + */ public function getChildren() { return $this->getRootNodes(); diff --git a/library/Businessprocess/Controller.php b/library/Businessprocess/Controller.php index fa4499f..1161806 100644 --- a/library/Businessprocess/Controller.php +++ b/library/Businessprocess/Controller.php @@ -5,22 +5,33 @@ namespace Icinga\Module\Businessprocess; use Icinga\Application\Icinga; use Icinga\Module\Businessprocess\Modification\ProcessChanges; use Icinga\Module\Businessprocess\Storage\LegacyStorage; +use Icinga\Module\Businessprocess\Storage\Storage; use Icinga\Module\Businessprocess\Web\Component\ActionBar; use Icinga\Module\Businessprocess\Web\Form\FormLoader; +use Icinga\Module\Businessprocess\Web\Url; use Icinga\Web\Controller as ModuleController; use Icinga\Web\Notification; +use Icinga\Web\View; use Icinga\Web\Widget; class Controller extends ModuleController { - protected $config; - + /** @deprecated, obsolete */ protected $backend; + /** @var BusinessProcess */ protected $bp; + /** @var View */ + public $view; + + /** @var Storage */ private $storage; + /** @var bool */ + private $showFullscreen; + + /** @var Url */ private $url; public function init() @@ -31,17 +42,31 @@ class Controller extends ModuleController } $this->view->errors = array(); + $this->url(); + $this->view->showFullscreen + = $this->showFullscreen + = (bool) $this->_helper->layout()->showFullscreen; + $this->view->compact = $this->params->get('view') === 'compact'; } + /** + * @return Url + */ protected function url() { if ($this->url === null) { - $this->url = clone $this->getRequest()->getUrl(); + $this->url = Url::fromPath( + $this->getRequest()->getUrl()->getPath() + )->setParams($this->params); } + return $this->url; } + /** + * @return ActionBar + */ protected function actions() { if ($this->view->actions === null) { @@ -64,6 +89,12 @@ class Controller extends ModuleController return $this->Window()->getSessionNamespace('businessprocess'); } + protected function setViewScript($name) + { + $this->_helper->viewRenderer->setNoController(true); + $this->_helper->viewRenderer->setScriptAction($name); + } + protected function setTitle($title) { $args = func_get_args(); diff --git a/library/Businessprocess/Node.php b/library/Businessprocess/Node.php index 96fdfef..693b6e8 100644 --- a/library/Businessprocess/Node.php +++ b/library/Businessprocess/Node.php @@ -298,6 +298,11 @@ abstract class Node return count($this->parents) > 0; } + public function getParents() + { + return $this->parents; + } + protected function stateToSortState($state) { if (array_key_exists($state, static::$stateToSortStateMap)) { diff --git a/library/Businessprocess/Simulation.php b/library/Businessprocess/Simulation.php index 4babc48..2d47470 100644 --- a/library/Businessprocess/Simulation.php +++ b/library/Businessprocess/Simulation.php @@ -2,12 +2,13 @@ namespace Icinga\Module\Businessprocess; +use Icinga\Exception\ProgrammingError; use Icinga\Web\Session\SessionNamespace; class Simulation { /** - * @var Session + * @var SessionNamespace */ protected $session; @@ -21,6 +22,9 @@ class Simulation */ protected $key; + /** + * @var + */ protected $simulations; public function __construct(BusinessProcess $bp, SessionNamespace $session) diff --git a/library/Businessprocess/Storage/LegacyStorage.php b/library/Businessprocess/Storage/LegacyStorage.php index 4d4ea8c..3d50ce3 100644 --- a/library/Businessprocess/Storage/LegacyStorage.php +++ b/library/Businessprocess/Storage/LegacyStorage.php @@ -196,6 +196,8 @@ class LegacyStorage extends Storage /** * @param BusinessProcess $process + * + * @return void */ public function storeProcess(BusinessProcess $process) { diff --git a/library/Businessprocess/Web/FakeRequest.php b/library/Businessprocess/Web/FakeRequest.php index 6735e81..4e54117 100644 --- a/library/Businessprocess/Web/FakeRequest.php +++ b/library/Businessprocess/Web/FakeRequest.php @@ -23,4 +23,4 @@ class FakeRequest extends Request return self::$baseUrl; } } -} \ No newline at end of file +} diff --git a/library/Businessprocess/Web/Url.php b/library/Businessprocess/Web/Url.php index a466d30..4f508e6 100644 --- a/library/Businessprocess/Web/Url.php +++ b/library/Businessprocess/Web/Url.php @@ -5,7 +5,18 @@ namespace Icinga\Module\Businessprocess\Web; use Icinga\Application\Icinga; use Icinga\Exception\ProgrammingError; use Icinga\Web\Url as WebUrl; +use Icinga\Web\UrlParams; +/** + * Class Url + * + * The main purpose of this class is to get unit tests running on CLI + * Little code from Icinga\Web\Url has been duplicated, as neither fromPath() + * nor getRequest() can be extended in a meaningful way at the time of this + * writing + * + * @package Icinga\Module\Businessprocess\Web + */ class Url extends WebUrl { public static function fromPath($url, array $params = array(), $request = null) @@ -33,7 +44,11 @@ class Url extends WebUrl if (isset($parts['path'])) { $self->setPath($parts['path']); } - + + if (isset($urlParts['query'])) { + $params = UrlParams::fromQueryString($urlParts['query'])->mergeValues($params); + } + if (isset($parts['fragment'])) { $self->setAnchor($parts['fragment']); } @@ -51,5 +66,4 @@ class Url extends WebUrl return $app->getRequest(); } } - -} \ No newline at end of file +} From 34ef3f26af03e5b4b69132a7ac0a9febb9af82ce Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Sun, 27 Nov 2016 23:54:03 +0100 Subject: [PATCH 059/256] Html: introduce a new namespace --- .../{Web/Component => Html}/Attribute.php | 40 ++++- .../{Web/Component => Html}/Attributes.php | 49 ++++-- library/Businessprocess/Html/Container.php | 67 ++++++++ .../{Web/Component => Html}/Link.php | 54 +++---- .../Web/Component/ActionBar.php | 6 +- .../Web/Component/Component.php | 139 ----------------- .../Web/Component/Container.php | 144 ------------------ 7 files changed, 166 insertions(+), 333 deletions(-) rename library/Businessprocess/{Web/Component => Html}/Attribute.php (63%) rename library/Businessprocess/{Web/Component => Html}/Attributes.php (75%) create mode 100644 library/Businessprocess/Html/Container.php rename library/Businessprocess/{Web/Component => Html}/Link.php (51%) delete mode 100644 library/Businessprocess/Web/Component/Component.php delete mode 100644 library/Businessprocess/Web/Component/Container.php diff --git a/library/Businessprocess/Web/Component/Attribute.php b/library/Businessprocess/Html/Attribute.php similarity index 63% rename from library/Businessprocess/Web/Component/Attribute.php rename to library/Businessprocess/Html/Attribute.php index dfabd00..d0ee15e 100644 --- a/library/Businessprocess/Web/Component/Attribute.php +++ b/library/Businessprocess/Html/Attribute.php @@ -1,8 +1,8 @@ value; } + /** + * @param string|array $value + * @return $this + */ + public function setValue($value) + { + $this->value = $value; + return $this; + } + + /** + * @param string $value + * @return $this + */ + public function addValue($value) + { + if (! is_array($this->value)) { + $this->value = array($this->value); + } + + if (is_array($value)) { + $this->value = array_merge($this->value, $value); + } else { + $this->value[] = $value; + } + return $this; + } + /** * @return string */ @@ -92,7 +120,11 @@ class Attribute extends Component */ public static function escapeValue($value) { - // TODO: escape - return (string) $value; + // TODO: escape differently + if (is_array($value)) { + return Util::escapeForHtml(implode(' ', $value)); + } else { + return Util::escapeForHtml((string) $value); + } } } diff --git a/library/Businessprocess/Web/Component/Attributes.php b/library/Businessprocess/Html/Attributes.php similarity index 75% rename from library/Businessprocess/Web/Component/Attributes.php rename to library/Businessprocess/Html/Attributes.php index c0c8039..ef130cb 100644 --- a/library/Businessprocess/Web/Component/Attributes.php +++ b/library/Businessprocess/Html/Attributes.php @@ -1,15 +1,17 @@ attributes = array(); if (empty($attributes)) { return; } @@ -44,7 +45,8 @@ class Attributes extends Component /** * @param Attributes|array|null $attributes - * @return static + * @return Attributes + * @throws IcingaException */ public static function wantAttributes($attributes) { @@ -144,15 +146,42 @@ class Attributes extends Component return $this; } + /** + * Callback must return an instance of Attribute + * + * @param $name + * @param $callback + */ + public function registerCallbackFor($name, callable $callback) + { + $this->callbacks[$name] = $callback; + return $this; + } + /** * @inheritdoc */ public function render() { - if (empty($this->attributes)) { + if (empty($this->attributes) && empty($this->callbacks)) { return ''; } - return ' ' . implode(' ', $this->attributes); + $parts = array(); + foreach ($this->callbacks as $callback) { + $attribute = call_user_func($callback); + if (! $attribute instanceof Attribute) { + throw new ProgrammingError( + 'A registered attribute callback must return an Attribute' + ); + } + + $parts[] = $attribute->render(); + } + + foreach ($this->attributes as $attribute) { + $parts[] = $attribute->render(); + } + return ' ' . implode(' ', $parts); } } diff --git a/library/Businessprocess/Html/Container.php b/library/Businessprocess/Html/Container.php new file mode 100644 index 0000000..14e8fc7 --- /dev/null +++ b/library/Businessprocess/Html/Container.php @@ -0,0 +1,67 @@ +renderContainerFor(parent::render()); + } + + /** + * @inheritdoc + */ + public function renderError($error) + { + // TODO: eventually add class="error" + return $this->renderContainerFor( + parent::renderError($error) + ); + } + + /** + * @param bool $render + * @return $this + */ + public function renderIfEmpty($render = true) + { + $this->renderIfEmpty = $render; + return $this; + } + + /** + * @param string $content + * @return string + */ + protected function renderContainerFor($content) + { + return sprintf( + '<%s%s>%s', + $this->tag, + $this->attributes->render(), + $content, + $this->tag + ); + } +} diff --git a/library/Businessprocess/Web/Component/Link.php b/library/Businessprocess/Html/Link.php similarity index 51% rename from library/Businessprocess/Web/Component/Link.php rename to library/Businessprocess/Html/Link.php index 149c7af..b82e0a2 100644 --- a/library/Businessprocess/Web/Component/Link.php +++ b/library/Businessprocess/Html/Link.php @@ -1,37 +1,39 @@ text = $text; + $link->content = Util::wantHtml($content); + $link->attributes()->registerCallbackFor('href', array($link, 'getHrefAttribute')); + $link->setAttributes($attributes); + return $link; + } + + public function setUrl($url, $urlParams) + { if ($url instanceof WebUrl) { // Hint: Url is also a WebUrl if ($urlParams !== null) { $url->addParams($urlParams); @@ -45,17 +47,15 @@ class Link extends Component $link->url = Url::fromPath($url, $urlParams); } } - $link->attributes = new Attributes($attributes); - - return $link; + $link->url->getParams(); } /** - * @return string + * @return Attribute */ - public function getRenderedText() + public function getHrefAttribute() { - return $this->view()->escape((string) $this->text); + return new Attribute('href', $this->getUrl()->getAbsoluteUrl('&')); } /** @@ -63,19 +63,7 @@ class Link extends Component */ public function getUrl() { + // TODO: What if null? #? return $this->url; } - - /** - * @inheritdoc - */ - public function render() - { - return sprintf( - '%s', - $this->getUrl(), - $this->attributes->render(), - $this->getRenderedText() - ); - } } diff --git a/library/Businessprocess/Web/Component/ActionBar.php b/library/Businessprocess/Web/Component/ActionBar.php index 887e06f..ec80347 100644 --- a/library/Businessprocess/Web/Component/ActionBar.php +++ b/library/Businessprocess/Web/Component/ActionBar.php @@ -1,10 +1,10 @@ 'action-bar'); + protected $defaultAttributes = array('class' => 'action-bar'); } diff --git a/library/Businessprocess/Web/Component/Component.php b/library/Businessprocess/Web/Component/Component.php deleted file mode 100644 index eaec881..0000000 --- a/library/Businessprocess/Web/Component/Component.php +++ /dev/null @@ -1,139 +0,0 @@ -view = $view; - return $this; - } - - /** - * @return View - */ - public function view() - { - if ($this->view === null) { - $this->view = $this->discoveredView(); - } - return $this->view; - } - - /** - * @return View - */ - protected function discoveredView() - { - if (self::$discoveredView === null) { - $icinga = Icinga::app(); - if ($icinga->isCli()) { - self::$discoveredView = new View(); - } else { - $viewRenderer = $icinga->getViewRenderer(); - if ($viewRenderer->view === null) { - $viewRenderer->initView(); - } - - self::$discoveredView = $viewRenderer->view; - } - } - - return self::$discoveredView; - } - - /** - * @return string - */ - abstract public function render(); - - public function wantHtml($any, $separator = '') - { - if ($any instanceof Component) { - return $any; - } elseif (is_string($any)) { - return $this->view()->escape($any); - } elseif (is_array($any)) { - $safe = array(); - foreach ($any as $el) { - $safe .= $this->wantHtml($el); - } - - return implode($separator, $safe); - } else { - // TODO: Should we add a dedicated Exception class? - throw new IcingaException( - 'String, Web Component or Array of such expected, got "%s"', - $this->getPhpTypeName($any) - ); - } - } - - public function getPhpTypeName($any) - { - if (is_object($any)) { - return get_class($any); - } else { - return gettype($any); - } - } - - /** - * @param Exception|string $error - * @return string - */ - protected function renderError($error) - { - if ($error instanceof Exception) { - $file = preg_split('/[\/\\\]/', $error->getFile(), -1, PREG_SPLIT_NO_EMPTY); - $file = array_pop($file); - $msg = sprintf( - '%s (%s:%d)', - $error->getMessage(), - $file, - $error->getLine() - ); - } elseif (is_string($error)) { - $msg = $error; - } else { - $msg = 'Got an invalid error'; - } - - - - - $view = $this->view(); - return sprintf( - $view->translate('ERROR: %s'), - $view->escape($msg) - ); - } - - /** - * @return string - */ - public function __toString() - { - try { - return $this->render(); - } catch (Exception $e) { - return $this->renderError($e); - } - } -} diff --git a/library/Businessprocess/Web/Component/Container.php b/library/Businessprocess/Web/Component/Container.php deleted file mode 100644 index 69cce5a..0000000 --- a/library/Businessprocess/Web/Component/Container.php +++ /dev/null @@ -1,144 +0,0 @@ -addContent($content); - - if ($this->attributes === null) { - $this->attributes = Attributes::wantAttributes($attributes); - } else { - $this->attributes = Attributes::wantAttributes($this->attributes); - } - if ($tag !== null) { - $this->tag = $tag; - } - } - - /** - * @param Component|array|string $content - * @param Attributes|array $attributes - * @param string $tag - * - * @return static - */ - public static function create($content = array(), $attributes = null, $tag = null) - { - return new static($content, $attributes, $tag); - } - - /** - * @return Attributes - */ - public function attributes() - { - return $this->attributes; - } - - /** - * @see addContent() - * @param Component|array|string $content - * @return $this - */ - public function add($content) - { - return $this->addContent($content); - } - - /** - * @param Component|array|string $content - * @return $this - */ - public function addContent($content) - { - if (is_array($content)) { - foreach ($content as $c) { - $this->addContent($c); - } - } - - $htmlOrComponent = $this->wantHtml($content); - if (strlen($htmlOrComponent)) { - $this->content[] = $htmlOrComponent; - } - - return $this; - } - - /** - * @param Component|array|string $content - * @return $this - */ - public function setContent($content) - { - $this->content = array(); - $this->addContent($content); - - return $this; - } - - /** - * @return string - */ - public function renderContent() - { - return implode($this->separator, $this->content); - } - - /** - * @inheritdoc - */ - public function render() - { - return $this->renderContainerFor($this->renderContent()); - } - - /** - * @inheritdoc - */ - public function renderError($error) - { - // TODO: eventually add class="error" - return $this->renderContainerFor( - parent::renderError($error) - ); - } - - /** - * @param $content - * @return string - */ - protected function renderContainerFor($content) - { - return sprintf( - '<%s%s>%s', - $this->tag, - $this->attributes->render(), - $content, - $this->tag - ); - } -} From ea7e79248cbfd907c19e8fee67b77a1c819daec5 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Sun, 27 Nov 2016 23:54:38 +0100 Subject: [PATCH 060/256] Html: a bunch of new classes, some changes --- library/Businessprocess/Html/Attribute.php | 2 +- library/Businessprocess/Html/Attributes.php | 23 ++-- library/Businessprocess/Html/BaseElement.php | 130 +++++++++++++++++++ library/Businessprocess/Html/Container.php | 29 ++++- library/Businessprocess/Html/Element.php | 34 +++++ library/Businessprocess/Html/Html.php | 120 +++++++++++++++++ library/Businessprocess/Html/HtmlString.php | 8 ++ library/Businessprocess/Html/Link.php | 20 ++- library/Businessprocess/Html/Renderable.php | 8 ++ library/Businessprocess/Html/Text.php | 61 +++++++++ library/Businessprocess/Html/Util.php | 130 +++++++++++++++++++ 11 files changed, 544 insertions(+), 21 deletions(-) create mode 100644 library/Businessprocess/Html/BaseElement.php create mode 100644 library/Businessprocess/Html/Element.php create mode 100644 library/Businessprocess/Html/Html.php create mode 100644 library/Businessprocess/Html/HtmlString.php create mode 100644 library/Businessprocess/Html/Renderable.php create mode 100644 library/Businessprocess/Html/Text.php create mode 100644 library/Businessprocess/Html/Util.php diff --git a/library/Businessprocess/Html/Attribute.php b/library/Businessprocess/Html/Attribute.php index d0ee15e..9012c3c 100644 --- a/library/Businessprocess/Html/Attribute.php +++ b/library/Businessprocess/Html/Attribute.php @@ -1,6 +1,6 @@ getPhpTypeName($attributes) + Util::getPhpTypeName($attributes) ); } return $self; @@ -74,7 +74,7 @@ class Attributes /** * @return Attribute[] */ - public function attributes() + public function getAttributes() { return $this->attributes; } @@ -87,16 +87,20 @@ class Attributes public function add($attribute, $value = null) { if ($attribute instanceof static) { - foreach ($attribute as $a) { + foreach ($attribute->getAttributes() as $a) { $this->add($a); } - - return $this; } elseif ($attribute instanceof Attribute) { - return $this->addAttribute($attribute); + $this->addAttribute($attribute); + } elseif (is_array($attribute)) { + foreach ($attribute as $name => $value) { + $this->add($name, $value); + } } else { - return $this->addAttribute(Attribute::create($attribute, $value)); + $this->addAttribute(Attribute::create($attribute, $value)); } + + return $this; } /** @@ -151,6 +155,7 @@ class Attributes * * @param $name * @param $callback + * @return $this */ public function registerCallbackFor($name, callable $callback) { diff --git a/library/Businessprocess/Html/BaseElement.php b/library/Businessprocess/Html/BaseElement.php new file mode 100644 index 0000000..8fefed4 --- /dev/null +++ b/library/Businessprocess/Html/BaseElement.php @@ -0,0 +1,130 @@ +attributes === null) { + $default = $this->getDefaultAttributes(); + if (empty($default)) { + $this->attributes = new Attributes(); + } else { + $this->attributes = Attributes::wantAttributes($default); + } + + } + + return $this->attributes; + } + + /** + * @param Attributes|array|null $attributes + * @return $this + */ + public function setAttributes($attributes) + { + $this->attributes = Attributes::wantAttributes($attributes); + return $this; + } + + /** + * @param Attributes|array|null $attributes + * @return $this + */ + public function addAttributes($attributes) + { + $this->attributes = Attributes::wantAttributes($attributes); + return $this; + } + + public function getDefaultAttributes() + { + return $this->defaultAttributes; + } + + public function setTag($tag) + { + $this->tag = $tag; + return $this; + } + + public function getTag() + { + return $this->tag; + } + + public function renderContent() + { + return parent::render(); + } + + /** + * @return string + */ + public function render() + { + $tag = $this->getTag(); + + return sprintf( + '<%s%s>%s', + $tag, + $this->attributes()->render(), + $this->renderContent(), + $tag + ); + } + + protected function translate($msg) + { + // TODO: Not so nice + return mt('businessprocess', $msg); + } + + /** + * Whether the given something can be rendered + * + * @param mixed $any + * @return bool + */ + protected function canBeRendered($any) + { + return is_string($any) || is_int($any) || is_null($any); + } + + /** + * @param Exception|string $error + * @return string + */ + protected function renderError($error) + { + return Util::renderError($error); + } + + /** + * @return string + */ + public function __toString() + { + try { + return $this->render(); + } catch (Exception $e) { + return $this->renderError($e); + } + } +} diff --git a/library/Businessprocess/Html/Container.php b/library/Businessprocess/Html/Container.php index 14e8fc7..6aaba22 100644 --- a/library/Businessprocess/Html/Container.php +++ b/library/Businessprocess/Html/Container.php @@ -1,6 +1,6 @@ setContent($content); + } + + if ($attributes !== null) { + $container->setAttributes($attributes); + } + if ($tag !== null) { + $container->setTag($tag); + } + + return $container; } +} + +class Old { + /** * @inheritdoc */ @@ -64,4 +84,5 @@ class Container extends BaseElement $this->tag ); } -} + +} \ No newline at end of file diff --git a/library/Businessprocess/Html/Element.php b/library/Businessprocess/Html/Element.php new file mode 100644 index 0000000..2f19f7e --- /dev/null +++ b/library/Businessprocess/Html/Element.php @@ -0,0 +1,34 @@ +tag = $tag; + + if ($attributes !== null) { + $this->attributes = $this->attributes()->add($attributes); + } + } + + /** + * Container constructor. + * + * @param string $tag + * @param Attributes|array $attributes + * + * @return static + */ + public static function create($tag, $attributes = null) + { + return new static($tag, $attributes); + } +} diff --git a/library/Businessprocess/Html/Html.php b/library/Businessprocess/Html/Html.php new file mode 100644 index 0000000..eafd2e7 --- /dev/null +++ b/library/Businessprocess/Html/Html.php @@ -0,0 +1,120 @@ +content[] = $element; + return $this; + } + + /** + * @param Renderable|array|string $content + * @return $this + */ + public function setContent($content) + { + $this->content = array( + static::escape($content) + ); + + return $this; + } + + /** + * @param Renderable|array|string $content + * @return $this + */ + public function addContent($content) + { + $this->content[] = static::escape($content); + return $this; + } + + /** + * return Html + */ + public function getContent() + { + if ($this->content === null) { + $this->content = array(new Html()); + } + + return $this->content; + } + + public function hasContent() + { + if ($this->content === null) { + return false; + } + + // TODO: unfinished + // return $this->content->isEmpty(); + return true; + } + + /** + * @param $separator + * @return $this + */ + public function setSeparator($separator) + { + $this->contentSeparator = $separator; + return $this; + } + + /** + * @inheritdoc + */ + public function render() + { + $html = array(); + + foreach ($this->content as $element) { + if (is_string($element)) { + var_dump($this->content); + } + $html[] = $element->render(); + } + + return implode($this->contentSeparator, $html); + } + + public static function element($name, $attributes = null) + { + // TODO: This might be anything here, add a better check + if (! ctype_alnum($name)) { + throw new ProgrammingError('Invalid element requested'); + } + + $class = __NAMESPACE__ . '\\' . $name; + /** @var Element $element */ + $element = new $class(); + if ($attributes !== null) { + $element->setAttributes($attributes); + } + + return $element; + } +} diff --git a/library/Businessprocess/Html/HtmlString.php b/library/Businessprocess/Html/HtmlString.php new file mode 100644 index 0000000..559c903 --- /dev/null +++ b/library/Businessprocess/Html/HtmlString.php @@ -0,0 +1,8 @@ +content = Util::wantHtml($content); - $link->attributes()->registerCallbackFor('href', array($link, 'getHrefAttribute')); + $link->setContent($content); $link->setAttributes($attributes); + $link->attributes()->registerCallbackFor('href', array($link, 'getHrefAttribute')); + $link->setUrl($url, $urlParams); return $link; } @@ -39,15 +44,16 @@ class Link extends BaseElement $url->addParams($urlParams); } - $link->url = $url; + $this->url = $url; } else { if ($urlParams === null) { - $link->url = Url::fromPath($url); + $this->url = Url::fromPath($url); } else { - $link->url = Url::fromPath($url, $urlParams); + $this->url = Url::fromPath($url, $urlParams); } } - $link->url->getParams(); + + $this->url->getParams(); } /** diff --git a/library/Businessprocess/Html/Renderable.php b/library/Businessprocess/Html/Renderable.php new file mode 100644 index 0000000..7c1a560 --- /dev/null +++ b/library/Businessprocess/Html/Renderable.php @@ -0,0 +1,8 @@ +string = (string) $string; + } + + /** + * @return string + */ + public function getText() + { + return $this->string; + } + + /** + * @param bool $escaped + * @return $this + */ + public function setEscaped($escaped = true) + { + $this->escaped = $escaped; + return $this; + } + + /** + * @param $text + * + * @return static + */ + public static function create($text) + { + return new static($text); + } + + /** + * @return string + */ + public function render() + { + if ($this->escaped) { + return $this->string; + } else { + return Util::escapeForHtml($this->string); + } + } +} \ No newline at end of file diff --git a/library/Businessprocess/Html/Util.php b/library/Businessprocess/Html/Util.php new file mode 100644 index 0000000..ee82eb0 --- /dev/null +++ b/library/Businessprocess/Html/Util.php @@ -0,0 +1,130 @@ +getFile(), -1, PREG_SPLIT_NO_EMPTY); + $file = array_pop($file); + $msg = sprintf( + '%s (%s:%d)', + $error->getMessage(), + $file, + $error->getLine() + ); + } elseif (is_string($error)) { + $msg = $error; + } else { + $msg = 'Got an invalid error'; // TODO: translate? + } + + return sprintf( + 'ERROR: %s', // TODO: translate? + static::escapeForHtml($msg) + ); + } + + /** + * @param $any + * @return Renderable + * @throws IcingaException + */ + public static function wantHtml($any) + { + if ($any instanceof Renderable) { + return $any; + } elseif (static::canBeRenderedAsString($any)) { + return new Text($any); + } elseif (is_array($any)) { + $html = new Html(); + foreach ($any as $el) { + $html->add(static::wantHtml($el)); + } + + return $html; + } else { + // TODO: Should we add a dedicated Exception class? + throw new IcingaException( + 'String, Html Element or Array of such expected, got "%s"', + Util::getPhpTypeName($any) + ); + } + } + + public static function canBeRenderedAsString($any) + { + return is_string($any) || is_int($any) || is_null($any); + } + + /** + * @param $any + * @return string + */ + public static function getPhpTypeName($any) + { + if (is_object($any)) { + return get_class($any); + } else { + return gettype($any); + } + } + + /** + * This defines the flags used when escaping for HTML + * + * - Single quotes are not escaped (ENT_COMPAT) + * - With PHP >= 5.4, invalid characters are replaced with � (ENT_SUBSTITUTE) + * - With PHP 5.3 they are ignored (ENT_IGNORE, less secure) + * - Uses HTML5 entities for PHP >= 5.4, disallowing + * + * @return int + */ + protected static function htmlEscapeFlags() + { + if (self::$htmlEscapeFlags === null) { + if (version_compare(PHP_VERSION, '5.4.0') >= 0) { + self::$htmlEscapeFlags = ENT_COMPAT | ENT_SUBSTITUTE | ENT_HTML5; + } else { + self::$htmlEscapeFlags = ENT_COMPAT | ENT_IGNORE; + } + } + + return self::$htmlEscapeFlags; + } +} From 1f43d2c71caabed9c41f384cddbe0b50d6751586 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Sun, 27 Nov 2016 23:55:01 +0100 Subject: [PATCH 061/256] tests: first tests for our HTML classes --- .../Businessprocess/Html/AttributeTest.php | 150 ++++++++++++++++++ .../Businessprocess/Html/AttributesTest.php | 111 +++++++++++++ .../library/Businessprocess/Html/HtmlTest.php | 42 +++++ .../library/Businessprocess/Html/LinkTest.php | 43 +++++ 4 files changed, 346 insertions(+) create mode 100644 test/php/library/Businessprocess/Html/AttributeTest.php create mode 100644 test/php/library/Businessprocess/Html/AttributesTest.php create mode 100644 test/php/library/Businessprocess/Html/HtmlTest.php create mode 100644 test/php/library/Businessprocess/Html/LinkTest.php diff --git a/test/php/library/Businessprocess/Html/AttributeTest.php b/test/php/library/Businessprocess/Html/AttributeTest.php new file mode 100644 index 0000000..af4d82b --- /dev/null +++ b/test/php/library/Businessprocess/Html/AttributeTest.php @@ -0,0 +1,150 @@ +assertInstanceOf( + get_class(new Attribute('a', 'b')), + Attribute::create('a', 'b') + ); + } + + public function testKnowsItsName() + { + $a = new Attribute('target', '_blank'); + $this->assertEquals( + 'target', + $a->getName() + ); + } + + public function testKnowsItsValue() + { + $a = new Attribute('target', '_blank'); + $this->assertEquals( + '_blank', + $a->getValue() + ); + } + + public function testItsValueCanBeModified() + { + $a = new Attribute('target', '_blank'); + $a->setValue('_self'); + $this->assertEquals( + '_self', + $a->getValue() + ); + } + + public function testPreservesComplexValues() + { + $a = new Attribute('special', 'süß "\'&'); + $this->assertEquals( + 'süß "\'&', + $a->getValue() + ); + } + + public function testAllowsToExtendItsValue() + { + $a = new Attribute('class', 'first'); + $a->addValue('second'); + + $this->assertEquals( + array('first', 'second'), + $a->getValue() + ); + + $a->addValue('third'); + + $this->assertEquals( + array('first', 'second', 'third'), + $a->getValue() + ); + + $a->addValue(array('some', 'more')); + + $this->assertEquals( + array('first', 'second', 'third', 'some', 'more'), + $a->getValue() + ); + } + + public function testAcceptsAndReturnsArrayValues() + { + $value = array('first', 'second', 'third'); + $a = new Attribute('class', $value); + + $this->assertEquals( + $value, + $a->getValue() + ); + + $value[] = 'forth'; + + $a->setValue($value); + $this->assertEquals( + $value, + $a->getValue() + ); + } + + public function testCorrectlyRendersItsName() + { + $a = new Attribute('class', 'test'); + $this->assertEquals( + 'class', + $a->renderName() + ); + } + + public function testCorrectlyRendersItsValue() + { + $a = new Attribute('href', '/some/url?a=b&c=d'); + $this->assertEquals( + '/some/url?a=b&c=d', + $a->renderValue() + ); + } + + public function testCorrectlyRendersArrayValues() + { + $a = new Attribute('weird', array('"sü?ß', '/some/url?a=b&c=d')); + $this->assertEquals( + '"sü?ß /some/url?a=b&c=d', + $a->renderValue() + ); + } + + public function testCorrectlyEscapesAName() + { + $this->assertEquals( + 'class', + Attribute::escapeName('class') + ); + } + + public function testCorrectlyEscapesAValue() + { + $this->assertEquals( + ""sü?ß' /some/url?a=b&c=d", + Attribute::escapeValue('"sü?ß\' /some/url?a=b&c=d') + ); + } + + public function testRendersCorrectls() + { + $a = new Attribute('weird', array('"sü?ß', '/some/url?a=b&c=d')); + $this->assertEquals( + 'weird=""sü?ß /some/url?a=b&c=d"', + $a->render() + ); + } +} diff --git a/test/php/library/Businessprocess/Html/AttributesTest.php b/test/php/library/Businessprocess/Html/AttributesTest.php new file mode 100644 index 0000000..9cbfc08 --- /dev/null +++ b/test/php/library/Businessprocess/Html/AttributesTest.php @@ -0,0 +1,111 @@ + array('small', 'nice'), + 'target' => '_blank' + ) + ); + + $this->assertEquals( + ' class="small nice" target="_blank"', + $a->render() + ); + } + + public function testCanBeInstantiatedThroughCreate() + { + $class = get_class(new Attributes()); + + $this->assertInstanceOf( + $class, + Attributes::create() + ); + + $this->assertInstanceOf( + $class, + Attributes::create(array('some' => 'attr')) + ); + } + + public function testCanBeCreatedForArrayOrNullOrAttributes() + { + $empty = Attributes::wantAttributes(null); + $this->assertEquals('', $empty->render()); + + $array = Attributes::wantAttributes(array('a' => 'b')); + $this->assertEquals(' a="b"', $array->render()); + + $attributes = Attributes::wantAttributes( + Attributes::create(array('a' => 'b')) + ); + $this->assertEquals(' a="b"', $attributes->render()); + } + + public function testCanBeExtendedWithAnAttribute() + { + $a = Attributes::create(); + $a->add(Attribute::create('a', 'b')); + $this->assertEquals(' a="b"', $a->render()); + + $a->add(Attribute::create('c', 'd')); + $this->assertEquals(' a="b" c="d"', $a->render()); + + $a->add(Attribute::create('a', 'c')); + $this->assertEquals(' a="b c" c="d"', $a->render()); + } + + public function testCanBeExtendedWithAttributes() + { + $a = Attributes::create(); + $a->add(Attributes::create(array('a' => 'b'))); + $this->assertEquals(' a="b"', $a->render()); + + $a->add(Attributes::create( + array( + 'a' => 'c', + 'c' => 'd' + ) + )); + $this->assertEquals(' a="b c" c="d"', $a->render()); + } + + public function testCanBeExtendedWithANameValuePair() + { + $a = Attributes::create(); + $a->add('a', 'b'); + $this->assertEquals(' a="b"', $a->render()); + } + + public function testAttributesCanBeReplacedWithAnAttribute() + { + $a = Attributes::create(); + $a->add(Attribute::create('a', 'b')); + $a->set(Attribute::create('a', 'c')); + $this->assertEquals(' a="c"', $a->render()); + } + + public function testAttributesCanBeReplacedWithANameValuePair() + { + $a = Attributes::create(); + $a->add(Attribute::create('a', 'b')); + $a->set('a', 'c'); + $this->assertEquals(' a="c"', $a->render()); + } + + public function testCanBeConstructedAndRenderedEmpty() + { + $a = new Attributes(); + $this->assertEquals('', $a->render()); + } +} diff --git a/test/php/library/Businessprocess/Html/HtmlTest.php b/test/php/library/Businessprocess/Html/HtmlTest.php new file mode 100644 index 0000000..87200cd --- /dev/null +++ b/test/php/library/Businessprocess/Html/HtmlTest.php @@ -0,0 +1,42 @@ +setContent('Some content'); + $this->assertEquals( + 'Some content', + $h->render() + ); + } + + public function testContentCanBeExtended() + { + $h = new Html(); + $h->setContent('Some content'); + $h->addContent('More content'); + $this->assertEquals( + 'Some contentMore content', + $h->render() + ); + } + + public function testSeparatorsAreRespected() + { + $h = new Html(); + $h->setContent('Some content'); + $h->setSeparator(', and '); + $h->addContent('More content'); + $this->assertEquals( + 'Some content, and More content', + $h->render() + ); + } +} diff --git a/test/php/library/Businessprocess/Html/LinkTest.php b/test/php/library/Businessprocess/Html/LinkTest.php new file mode 100644 index 0000000..a8d4b37 --- /dev/null +++ b/test/php/library/Businessprocess/Html/LinkTest.php @@ -0,0 +1,43 @@ +assertEquals( + 'Click here', + $l->renderContent() + ); + } + + public function testSimpleLinkRendersCorrectly() + { + $l = Link::create('Click here', 'go/some/where'); + $this->assertEquals( + 'Click here', + $l->render() + ); + } + + public function testLinkWithParamsRendersCorrectly() + { + $l = Link::create( + 'Click here', + 'go/some/where', + array( + 'with' => 'me', + 'and' => 'aDog' + ) + ); + $this->assertEquals( + 'Click here', + $l->render() + ); + } +} From fbd6aef886bc908b41e450fb1a1ccdc321021f15 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 00:12:07 +0100 Subject: [PATCH 062/256] Businessprocess/Renderer: prepare new renderers --- library/Businessprocess/Renderer/Renderer.php | 226 ++++++++++++++++++ .../Businessprocess/Renderer/TileRenderer.php | 125 ++++++++++ .../Renderer/TileRenderer/AddNewTile.php | 47 ++++ .../Renderer/TileRenderer/NodeTile.php | 53 ++++ 4 files changed, 451 insertions(+) create mode 100644 library/Businessprocess/Renderer/Renderer.php create mode 100644 library/Businessprocess/Renderer/TileRenderer.php create mode 100644 library/Businessprocess/Renderer/TileRenderer/AddNewTile.php create mode 100644 library/Businessprocess/Renderer/TileRenderer/NodeTile.php diff --git a/library/Businessprocess/Renderer/Renderer.php b/library/Businessprocess/Renderer/Renderer.php new file mode 100644 index 0000000..d2f47ed --- /dev/null +++ b/library/Businessprocess/Renderer/Renderer.php @@ -0,0 +1,226 @@ +bp = $bp; + $this->parent = $parent; + $this->view = $view; + } + + /** + * @return BusinessProcess + */ + public function getBusinessProcess() + { + return $this->bp; + } + + /** + * Whether this will render all root nodes + * + * @return bool + */ + public function wantsRootNodes() + { + return $this->parent === null; + } + + /** + * Whether this will only render parts of given config + * + * @return bool + */ + public function rendersSubNode() + { + return $this->parent !== null; + } + + /** + * @return BpNode[] + */ + public function getParentNodes() + { + if ($this->wantsRootNodes()) { + return array(); + } + + return $this->parent->getParents(); + } + + /** + * @param $summary + * @return Container + */ + public function renderStateBadges($summary) + { + $container = Container::create( + array('class' => 'badges') + )/* ->renderIfEmpty(false) */; + + foreach ($summary as $state => $cnt) { + if ($cnt === 0 + || $state === 'OK' + || $state === 'UP' + ) { + continue; + } + + $container->addContent( + Element::create( + 'span', + array( + 'class' => array( + 'badge', + 'badge-' . strtolower($state) + ), + // TODO: We should translate this in this module + 'title' => mt('monitoring', $state) + ) + )->setContent($cnt) + ); + + } + + return $container; + } + + public function getNodeClasses(Node $node) + { + $classes = array( + strtolower($node->getStateName()) + ); + + if ($node->isHandled()) { + $classes[] = 'handled'; + } + + if ($node instanceof BpNode) { + $classes[] = 'process-node'; + } else { + $classes[] = 'monitored-node'; + } + + return $classes; + } + + public function setPath(array $path) + { + $this->path = $path; + return $this; + } + + /** + * @return string|null + */ + public function getPath() + { + return $this->path; + } + + public function getMyPath() + { + $path = $this->getPath(); + if ($this->rendersSubNode()) { + $path[] = (string) $this->parent; + } + return $path; + } + + /** + * @param Url $url + * @return $this + */ + public function setBaseUrl(Url $url) + { + $this->baseUrl = $url->without(array('config', 'node', 'path')); + return $this; + } + + /** + * @return Url + * @throws ProgrammingError + */ + public function getBaseUrl() + { + if ($this->baseUrl === null) { + throw new ProgrammingError('Renderer has no baseUrl'); + } + + return clone($this->baseUrl); + } + + /** + * @return bool + */ + public function isLocked() + { + return $this->locked; + } + + /** + * @return $this + */ + public function lock() + { + $this->locked = true; + return $this; + } + + /** + * @return $this + */ + public function unlock() + { + $this->locked = false; + return $this; + } + /** + * Just to be on the safe side + */ + public function __destruct() + { + unset($this->parent); + unset($this->bp); + unset($this->view); + } +} diff --git a/library/Businessprocess/Renderer/TileRenderer.php b/library/Businessprocess/Renderer/TileRenderer.php new file mode 100644 index 0000000..46fdba1 --- /dev/null +++ b/library/Businessprocess/Renderer/TileRenderer.php @@ -0,0 +1,125 @@ +bp; + $nodesDiv = Container::create( + array( + 'class' => array( + 'tiles', + $this->howMany() + ), + 'data-base-target' => '_main', + ) + ); + + if ($this->wantsRootNodes()) { + $nodes = $bp->getChildren(); + } else { + $nodes = $this->parent->getChildren(); + } + + if (! $this->isLocked()) { + $this->add(new AddNewTile($this)); + } + + foreach ($nodes as $name => $node) { + $this->add(new NodeTile($this, $name, $node)); + } + + $nodesDiv->addContent($this->getContent()); + $this->setContent($this->renderBreadCrumb()) + ->addContent($nodesDiv); + + return parent::render(); + } + + public function renderBreadCrumb() + { + $breadcrumb = Element::create('ul', array('class' => 'breadcrumb')); + $breadcrumb = Element::create( 'ul', array( + 'class' => 'breadcrumb', + 'data-base-target' => '_main' + )); + + + $breadcrumb->add(Element::create('li')->add( + Link::create('Process', $this->getBaseUrl())) + ); + $bp = $this->bp; + $path = $this->getMyPath(); + $max = 20; + $chosen = array(); + for ($i = 1; $i <= $max; $i++) { + if (! empty($path)) { + $chosen[] = array_pop($path); + } + } + $chosen = array_reverse($chosen); + $consumed = array(); + while ($parent = array_shift($chosen)) { + $breadcrumb->add($this->renderParent($bp->getNode($parent), $consumed)); + $consumed[] = $parent; + } + + return $breadcrumb; + } + + /** + * @param BpNode $parent + */ + public function renderParent(BpNode $parent, $path) + { + $p = new NodeTile($this, (string) $parent, $parent, $path); + $p->attributes()->add('class', $this->getNodeClasses($parent)); + $p->setTag('li'); + return $p; + // $p->attributes()->add('class', 'parent'); + } + + /** + * @return array + */ + public function getDefaultAttributes() + { + return array( + 'class' => 'tiles aaaa' . $this->howMany() + ); + } + + /** + * A CSS class giving a rough indication of how many nodes we have + * + * This is used to show larger tiles when there are few and smaller + * ones if there are many. + * + * @return string + */ + protected function howMany() + { + $count = $this->bp->countChildren(); + $howMany = 'normal'; + + if ($count < 20) { + $howMany = 'few'; + } elseif ($count > 50) { + $howMany = 'many'; + } + + return $howMany; + } +} diff --git a/library/Businessprocess/Renderer/TileRenderer/AddNewTile.php b/library/Businessprocess/Renderer/TileRenderer/AddNewTile.php new file mode 100644 index 0000000..37c2273 --- /dev/null +++ b/library/Businessprocess/Renderer/TileRenderer/AddNewTile.php @@ -0,0 +1,47 @@ + 'addnew'); + + public function __construct(Renderer $renderer) + { + $bp = $renderer->getBusinessProcess(); + $path = $renderer->getMyPath(); + + $params = array( + 'config' => $bp->getName() + ); + + // Workaround for array issues + $url = Url::fromPath('businessprocess/process/create'); + $p = $url->getParams(); + $p->mergeValues($params); + if (! empty($path)) { + $p->addValues('path', $path); + } + + $this->add( + Link::create( + $this->translate('Add'), + $url, + null, + array( + 'title' => $this->translate('Add a new business process node') + ) + ) + ); + } +} \ No newline at end of file diff --git a/library/Businessprocess/Renderer/TileRenderer/NodeTile.php b/library/Businessprocess/Renderer/TileRenderer/NodeTile.php new file mode 100644 index 0000000..acea444 --- /dev/null +++ b/library/Businessprocess/Renderer/TileRenderer/NodeTile.php @@ -0,0 +1,53 @@ +attributes(); + $attributes->add('class', $renderer->getNodeClasses($node)); + $attributes->add('id', 'bp-' . (string) $node); + + if ($node instanceof MonitoredNode) { + $attributes->add('data-base-target', '_next'); + $url = $node->getUrl(); + } else { + $bp = $renderer->getBusinessProcess(); + $params = array( + 'config' => $node instanceof ImportedNode ? + $node->getConfigName() : + $bp->getName() + ); + + if ($name !== null) { + $params['node'] = $name; + } + + $url = $renderer->getBaseUrl(); + $p = $url->getParams(); + $p->mergeValues($params); + if (! empty($path)) { + $p->addValues('path', $path); + } + } + + $link = Link::create($node->getAlias(), $url); + + $this->add($link); + if ($node instanceof BpNode) { + $link->addContent($renderer->renderStateBadges($node->getStateSummary())); + } + } +} \ No newline at end of file From d1f62353939c4a66cc805a81e7a5b19576644d67 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 00:15:14 +0100 Subject: [PATCH 063/256] RenderStateBadges: deprecate the legacy helper --- application/views/helpers/RenderStateBadges.php | 1 + 1 file changed, 1 insertion(+) diff --git a/application/views/helpers/RenderStateBadges.php b/application/views/helpers/RenderStateBadges.php index bf24edc..70633aa 100644 --- a/application/views/helpers/RenderStateBadges.php +++ b/application/views/helpers/RenderStateBadges.php @@ -1,6 +1,7 @@ Date: Mon, 28 Nov 2016 00:16:47 +0100 Subject: [PATCH 064/256] ActionBar: this is a component and not generic --- library/Businessprocess/Web/Component/ActionBar.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/library/Businessprocess/Web/Component/ActionBar.php b/library/Businessprocess/Web/Component/ActionBar.php index ec80347..d44cccc 100644 --- a/library/Businessprocess/Web/Component/ActionBar.php +++ b/library/Businessprocess/Web/Component/ActionBar.php @@ -1,6 +1,8 @@ Date: Mon, 28 Nov 2016 00:18:53 +0100 Subject: [PATCH 065/256] MonitoredNode: add new simpler url/link helpers --- library/Businessprocess/HostNode.php | 44 +++++++++++++++------- library/Businessprocess/MonitoredNode.php | 15 +++++++- library/Businessprocess/ServiceNode.php | 46 ++++++++++++----------- 3 files changed, 67 insertions(+), 38 deletions(-) diff --git a/library/Businessprocess/HostNode.php b/library/Businessprocess/HostNode.php index 40341e8..3f9f540 100644 --- a/library/Businessprocess/HostNode.php +++ b/library/Businessprocess/HostNode.php @@ -2,7 +2,7 @@ namespace Icinga\Module\Businessprocess; -use Icinga\Module\Businessprocess\Web\Component\Link; +use Icinga\Module\Businessprocess\Html\Link; use Icinga\Module\Businessprocess\Web\Url; class HostNode extends MonitoredNode @@ -44,20 +44,9 @@ class HostNode extends MonitoredNode } } - public function renderLink($view) + public function getAlias() { - if ($this->isMissing()) { - return '' . $view->escape($this->hostname) . ''; - } - - $params = array( - 'host' => $this->getHostname(), - ); - - if ($this->bp->hasBackendName()) { - $params['backend'] = $this->bp->getBackendName(); - } - return Link::create($this->hostname, 'monitoring/host/show', $params)->render(); + return $this->getHostname(); } protected function getActionIcons($view) @@ -86,4 +75,31 @@ class HostNode extends MonitoredNode { return $this->hostname; } + + public function getUrl() + { + $params = array( + 'host' => $this->getHostname(), + ); + + if ($this->bp->hasBackendName()) { + $params['backend'] = $this->bp->getBackendName(); + } + + return Url::fromPath('monitoring/host/show', $params); + } + + public function getLink() + { + return Link::create($this->hostname, $this->getUrl()); + } + + public function renderLink($view) + { + if ($this->isMissing()) { + return '' . $view->escape($this->hostname) . ''; + } + + return $this->getLink()->render(); + } } diff --git a/library/Businessprocess/MonitoredNode.php b/library/Businessprocess/MonitoredNode.php index 2ffd77f..aa5e773 100644 --- a/library/Businessprocess/MonitoredNode.php +++ b/library/Businessprocess/MonitoredNode.php @@ -2,11 +2,22 @@ namespace Icinga\Module\Businessprocess; -use Icinga\Module\Businessprocess\Web\Component\Container; -use Icinga\Module\Businessprocess\Web\Component\Link; +use Icinga\Module\Businessprocess\Html\Container; +use Icinga\Module\Businessprocess\Html\Link; abstract class MonitoredNode extends Node { + abstract public function getUrl(); + + public function getLink() + { + if ($this->isMissing()) { + return Link::create($this->getAlias(), '#'); + } else { + return Link::create($this->getAlias(), $this->getUrl()); + } + } + protected function prepareActions(Container $actions) { $actions->add( diff --git a/library/Businessprocess/ServiceNode.php b/library/Businessprocess/ServiceNode.php index 3c984fd..52eea76 100644 --- a/library/Businessprocess/ServiceNode.php +++ b/library/Businessprocess/ServiceNode.php @@ -2,7 +2,7 @@ namespace Icinga\Module\Businessprocess; -use Icinga\Module\Businessprocess\Web\Component\Link; +use Icinga\Module\Businessprocess\Html\Link; use Icinga\Module\Businessprocess\Web\Url; class ServiceNode extends MonitoredNode @@ -26,27 +26,6 @@ class ServiceNode extends MonitoredNode } } - public function renderLink($view) - { - if ($this->isMissing()) { - return '' . $view->escape($this->getAlias()) . ''; - } - - $params = array( - 'host' => $this->getHostname(), - 'service' => $this->getServiceDescription() - ); - if ($this->bp->hasBackendName()) { - $params['backend'] = $this->bp->getBackendName(); - } - - return Link::create( - $this->getAlias(), - 'monitoring/service/show', - $params - )->render(); - } - protected function getActionIcons($view) { $icons = array(); @@ -83,4 +62,27 @@ class ServiceNode extends MonitoredNode { return $this->hostname . ': ' . $this->service; } + + public function getUrl() + { + $params = array( + 'host' => $this->getHostname(), + 'service' => $this->getServiceDescription() + ); + + if ($this->bp->hasBackendName()) { + $params['backend'] = $this->bp->getBackendName(); + } + + return Url::fromPath('monitoring/service/show', $params); + } + + public function renderLink($view) + { + if ($this->isMissing()) { + return '' . $view->escape($this->getAlias()) . ''; + } + + return $this->getLink()->render(); + } } From 69a1e454d2469aeb5bb1f93b69e75943088b3130 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 00:21:05 +0100 Subject: [PATCH 066/256] TileRenderer: some cleanup --- library/Businessprocess/Renderer/TileRenderer.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/library/Businessprocess/Renderer/TileRenderer.php b/library/Businessprocess/Renderer/TileRenderer.php index 46fdba1..7a36571 100644 --- a/library/Businessprocess/Renderer/TileRenderer.php +++ b/library/Businessprocess/Renderer/TileRenderer.php @@ -50,15 +50,13 @@ class TileRenderer extends Renderer public function renderBreadCrumb() { - $breadcrumb = Element::create('ul', array('class' => 'breadcrumb')); $breadcrumb = Element::create( 'ul', array( 'class' => 'breadcrumb', 'data-base-target' => '_main' )); - $breadcrumb->add(Element::create('li')->add( - Link::create('Process', $this->getBaseUrl())) + Link::create($this->bp->getTitle(), $this->getBaseUrl())) ); $bp = $this->bp; $path = $this->getMyPath(); @@ -88,7 +86,6 @@ class TileRenderer extends Renderer $p->attributes()->add('class', $this->getNodeClasses($parent)); $p->setTag('li'); return $p; - // $p->attributes()->add('class', 'parent'); } /** From dbeb05aac9c23e9adcfc9017cb052c8ccc3cf35e Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 00:22:31 +0100 Subject: [PATCH 067/256] Container: remove traces of old class --- library/Businessprocess/Html/Container.php | 48 ---------------------- 1 file changed, 48 deletions(-) diff --git a/library/Businessprocess/Html/Container.php b/library/Businessprocess/Html/Container.php index 6aaba22..f838721 100644 --- a/library/Businessprocess/Html/Container.php +++ b/library/Businessprocess/Html/Container.php @@ -38,51 +38,3 @@ class Container extends BaseElement return $container; } } - -class Old { - - /** - * @inheritdoc - */ - public function render() - { - return $this->renderContainerFor(parent::render()); - } - - /** - * @inheritdoc - */ - public function renderError($error) - { - // TODO: eventually add class="error" - return $this->renderContainerFor( - parent::renderError($error) - ); - } - - /** - * @param bool $render - * @return $this - */ - public function renderIfEmpty($render = true) - { - $this->renderIfEmpty = $render; - return $this; - } - - /** - * @param string $content - * @return string - */ - protected function renderContainerFor($content) - { - return sprintf( - '<%s%s>%s', - $this->tag, - $this->attributes->render(), - $content, - $this->tag - ); - } - -} \ No newline at end of file From d17b0efaa7730b7d7a67efad2c30aaed1137e9d5 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 00:24:36 +0100 Subject: [PATCH 068/256] Html, Renderer: phpcs --- library/Businessprocess/Html/HtmlString.php | 2 +- library/Businessprocess/Html/Renderable.php | 2 +- library/Businessprocess/Html/Text.php | 2 +- library/Businessprocess/Renderer/TileRenderer.php | 6 +++--- .../Businessprocess/Renderer/TileRenderer/AddNewTile.php | 2 +- library/Businessprocess/Renderer/TileRenderer/NodeTile.php | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/library/Businessprocess/Html/HtmlString.php b/library/Businessprocess/Html/HtmlString.php index 559c903..5232805 100644 --- a/library/Businessprocess/Html/HtmlString.php +++ b/library/Businessprocess/Html/HtmlString.php @@ -5,4 +5,4 @@ namespace Icinga\Module\Businessprocess\Html; class HtmlString extends Text { protected $escaped = true; -} \ No newline at end of file +} diff --git a/library/Businessprocess/Html/Renderable.php b/library/Businessprocess/Html/Renderable.php index 7c1a560..372baa4 100644 --- a/library/Businessprocess/Html/Renderable.php +++ b/library/Businessprocess/Html/Renderable.php @@ -5,4 +5,4 @@ namespace Icinga\Module\Businessprocess\Html; interface Renderable { public function render(); -} \ No newline at end of file +} diff --git a/library/Businessprocess/Html/Text.php b/library/Businessprocess/Html/Text.php index 8ae711f..994ab47 100644 --- a/library/Businessprocess/Html/Text.php +++ b/library/Businessprocess/Html/Text.php @@ -58,4 +58,4 @@ class Text implements Renderable return Util::escapeForHtml($this->string); } } -} \ No newline at end of file +} diff --git a/library/Businessprocess/Renderer/TileRenderer.php b/library/Businessprocess/Renderer/TileRenderer.php index 7a36571..1ae1b91 100644 --- a/library/Businessprocess/Renderer/TileRenderer.php +++ b/library/Businessprocess/Renderer/TileRenderer.php @@ -50,14 +50,14 @@ class TileRenderer extends Renderer public function renderBreadCrumb() { - $breadcrumb = Element::create( 'ul', array( + $breadcrumb = Element::create('ul', array( 'class' => 'breadcrumb', 'data-base-target' => '_main' )); $breadcrumb->add(Element::create('li')->add( - Link::create($this->bp->getTitle(), $this->getBaseUrl())) - ); + Link::create($this->bp->getTitle(), $this->getBaseUrl()) + )); $bp = $this->bp; $path = $this->getMyPath(); $max = 20; diff --git a/library/Businessprocess/Renderer/TileRenderer/AddNewTile.php b/library/Businessprocess/Renderer/TileRenderer/AddNewTile.php index 37c2273..d61fa4a 100644 --- a/library/Businessprocess/Renderer/TileRenderer/AddNewTile.php +++ b/library/Businessprocess/Renderer/TileRenderer/AddNewTile.php @@ -44,4 +44,4 @@ class AddNewTile extends BaseElement ) ); } -} \ No newline at end of file +} diff --git a/library/Businessprocess/Renderer/TileRenderer/NodeTile.php b/library/Businessprocess/Renderer/TileRenderer/NodeTile.php index acea444..9356c70 100644 --- a/library/Businessprocess/Renderer/TileRenderer/NodeTile.php +++ b/library/Businessprocess/Renderer/TileRenderer/NodeTile.php @@ -50,4 +50,4 @@ class NodeTile extends BaseElement $link->addContent($renderer->renderStateBadges($node->getStateSummary())); } } -} \ No newline at end of file +} From c56fec44eb0ee11ec33fe45818c96b674fe4e83a Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 00:28:33 +0100 Subject: [PATCH 069/256] CSS: style new tiles and breadcrumbs --- public/css/module.less | 238 ++++++++++++++++++++++++++++++----------- 1 file changed, 178 insertions(+), 60 deletions(-) diff --git a/public/css/module.less b/public/css/module.less index a49ecdc..793d7f2 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -348,42 +348,26 @@ table.bp { } } -.toplevel:after { +/** BEGIN Tiles **/ +.tiles:after { content:''; display:block; clear: both; } -.toplevel.few { - font-size: 3em; -} - -.toplevel.normal { - font-size: 2em; -} - -.toplevel.many { - font-size: 1.5em; -} - +.tiles.few { font-size: 3em; } +.tiles.normal { font-size: 2em; } +.tiles.many { font-size: 1.5em; } #layout.twocols, #layout.layout-minimal, .compact { - .toplevel.few { - font-size: 1.8em; - } - - .toplevel.normal { - font-size: 1.4em; - } - - .toplevel.many { - font-size: 0.9em; - } + .tiles.few { font-size: 1.8em; } + .tiles.normal { font-size: 1.4em; } + .tiles.many { font-size: 0.9em; } } -.toplevel > div { - width: 5em; - height: 5em; +.tiles > div { + width: 12em; + height: 6em; float: left; margin-right: 0.2em; margin-bottom: 0.2em; @@ -391,9 +375,19 @@ table.bp { background: #ccc; } -.toplevel > div > a { +.tiles > div.parent::before { + content: '&'; + position: absolute; + font-size: 1.2em; +} + +.tiles > div.parent { + width: 100%; + height: 2em; +} + +.tiles > div > a { text-decoration: none; - font-size: 0.5em; color: inherit; vertical-align: middle; text-align: center; @@ -401,72 +395,196 @@ table.bp { padding: 1em; font-weight: bold; word-wrap: break-word; - height: 10em; - width: 10em; + width: 100%; + height: 6em; box-sizing: border-box; } -.toplevel > div > a:hover { - color: black; +.tiles > div > a:hover { + color: @text-color; } -.toplevel > div.critical, .toplevel .badge-critical { - background: @colorCritical; +.badges { + display: block; + padding: 0.5em; + + .badge { + border: 1px solid white; + margin: 0; + margin-right: 1px; + } } -.toplevel > div.critical.handled { - background: @colorCriticalHandled; +.badge-critical, .badge-down { background: @colorCritical; } +.badge-unknown, .badge-unreachable { background: @colorUnknown; } +.badge-warning { background: @colorWarning; } +.badge-pending { background: @colorPending; } +.badge-missing { background: #ccc; } + +.tiles { + > .critical { background: @colorCritical; } + > .critical.handled { background: @colorCriticalHandled; } + > .down { background: @colorCritical; } + > .down.handled { background: @colorCriticalHandled; } + > .unknown { background: @colorUnknown; } + > .unknown.handled { background: @colorUnknownHandled; } + > .unreachable { background: @colorUnknown; } + > .unreachable.handled { background: @colorUnknownHandled; } + > .warning { background: @colorWarning; } + > .warning.handled { background: @colorWarningHandled; } + > .ok { background: @colorOk; } + > .pending { background: @colorPending; } + > .missing { background: #ccc; } } -.toplevel > div.down, .toplevel .badge-down { - background: @colorCritical; +.tiles > .addnew { + background: white; + a { + color: @icinga-blue; + border: 0.2em solid @icinga-blue; + &:hover { + color: white; + background: @icinga-blue; + } + } + + &.with-form { + width: auto; + height: auto; + min-width: 30em; + min-height: 10em; + a { + display: none; + } + } } -.toplevel > div.down.handled { - background: @colorCriticalHandled; +.tiles > .monitored-node a { + font-size: 0.8em; } -.toplevel > div.unknown, .toplevel .badge-unknown { - background: @colorUnknown; +.tiles .missing a { + pointer-events: none; + cursor: default; } -.toplevel > div.unknown.handled { - background: @colorUnknownHandled; + +/** END of tiles **/ + +/** BEGIN breadcrumb **/ + +.breadcrumb { + list-style: none; + overflow: hidden; + padding: 0; + + .badges { + display: inline-block; + padding: 0.02em 0 0.02em 0.5em; + margin-top: -0.2em; + + .badge { + font-size: 0.8em; + border: 1px solid white; + margin: 0; + margin-right: 1px; + } + } } -.toplevel > div.unreachable, .toplevel .badge-unreachable { - background: @colorUnknown; +.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; } } -.toplevel > div.unreachable.handled { - background: @colorUnknownHandled; +.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; } } -.toplevel > div.warning, .toplevel .badge-warning { - background: @colorWarning; +.breadcrumb:after { + content:''; + display:block; + clear: both; +} +.breadcrumb li { + float: left; + cursor: pointer; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + +} +.breadcrumb li a { + color: white; + margin: 0; + font-size: 1.2em; + text-decoration: none; + padding-left: 2em; + line-height: 3em; + background: @icinga-blue; + position: relative; + display: block; + float: left; + &:focus { + outline: none; + } } -.toplevel > div.warning.handled { - background: @colorWarningHandled; +.breadcrumb li a:before, .breadcrumb li a:after { + content: " "; + display: block; + width: 0; + height: 0; + border-top: 1.5em solid transparent; + border-bottom: 1.5em solid transparent; + position: absolute; + margin-top: -1.5em; + top: 50%; + left: 100%; } -.toplevel > div.ok { - background: @colorOk; +.breadcrumb li a:before { + border-left: 1.4em solid white; + margin-left: 1px; + z-index: 1; } -.toplevel > div.pending, .toplevel .badge-pending { - background: @colorPending; +.breadcrumb li a:after { + border-left: 1.4em solid @icinga-blue; + z-index: 2; } -.toplevel > div.missing, .toplevel .badge-missing { - background: #ccc; +.breadcrumb li:first-child a { + padding-left: 1em; } +.breadcrumb li:last-child a { + padding-right: 1em; + pointer-events: none; + cursor: default; +} + +.breadcrumb li a:hover, .breadcrumb li a:focus { background: @text-color; color: white; } +.breadcrumb li a:hover:after, .breadcrumb li a:focus:after { border-left-color: @text-color; } + +/** END of breadcrumg **/ + ul.error { padding: 0; list-style-type: none; background-color: @colorCritical; - font-size: 0.8em; li { font-weight: bold; From d930e97b9c7d9800252f557044cafff75f922123 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 00:46:53 +0100 Subject: [PATCH 070/256] ProcessController: remodel links, add bprenderer... ...to introduce the TileRenderer --- application/controllers/ProcessController.php | 141 +++++++++++++++++- .../views/scripts/process/bprenderer.phtml | 12 ++ 2 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 application/views/scripts/process/bprenderer.phtml diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 3f62c34..a095dea 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -4,17 +4,31 @@ namespace Icinga\Module\Businessprocess\Controllers; use Icinga\Module\Businessprocess\Controller; use Icinga\Module\Businessprocess\ConfigDiff; +use Icinga\Module\Businessprocess\Renderer\TileRenderer; use Icinga\Module\Businessprocess\Simulation; use Icinga\Module\Businessprocess\ProcessChanges; use Icinga\Module\Businessprocess\Storage\LegacyStorage; use Icinga\Module\Businessprocess\Forms\BpConfigForm; use Icinga\Module\Businessprocess\Forms\DeleteConfigForm; +use Icinga\Module\Businessprocess\Html\Link; +use Icinga\Module\Businessprocess\Web\Url; use Icinga\Web\Notification; use Icinga\Web\Widget\Tabextension\DashboardAction; - class ProcessController extends Controller { + protected function currentProcessParams() + { + $params = array(); + foreach (array('config', 'node') as $name) { + if ($value = $this->params->get($name)) { + $params[$name] = $value; + } + } + + return $params; + } + /** * Create a new business process configuration */ @@ -45,9 +59,72 @@ class ProcessController extends Controller */ public function showAction() { + $mode = $this->params->get('mode'); + $unlocked = (bool) $this->params->get('unlocked'); + + if ($mode === 'tile') { + $this->actions()->add( + Link::create( + $this->translate('Tree'), + 'businessprocess/process/show', + $this->currentProcessParams(), + array('class' => 'icon-sitemap') + ) + ); + } else { + $this->actions()->add( + Link::create( + $this->translate('Tiles'), + $this->url()->with('mode', 'tile'), + null, + array('class' => 'icon-dashboard') + ) + ); + } + + if ($unlocked) { + $this->actions()->add( + Link::create( + $this->translate('Lock'), + $this->url()->without('unlocked'), + null, + array( + 'class' => 'icon-lock', + 'title' => $this->translate('Lock this process'), + ) + ) + ); + } else { + $this->actions()->add( + Link::create( + $this->translate('Unlock'), + $this->url()->with('unlocked', true), + null, + array( + 'class' => 'icon-lock-open', + 'title' => $this->translate('Unlock this process'), + ) + ) + ); + } + + $this->actions()->add( + Link::create( + $this->translate('Store'), + 'businessprocess/process/config', + $this->currentProcessParams(), + array( + 'class' => 'icon-wrench', + 'title' => $this->translate('Modify this process'), + 'data-base-target' => '_next', + ) + ) + ); + + $this->prepareProcess(); $this->redirectOnConfigSwitch(); - if ($this->params->get('unlocked')) { + if ($unlocked) { $bp = $this->loadModifiedBpConfig(); $bp->unlock(); } else { @@ -64,21 +141,33 @@ class ProcessController extends Controller if ($node = $this->params->get('node')) { // Render a specific node + $this->view->nodeName = $node; - $this->view->bp = $bp->getNode($node); + $bpNode = $this->view->bp = $bp->getNode($node); } else { // Render a single process $this->view->bp = $bp; if ($bp->hasWarnings()) { $this->view->warnings = $bp->getWarnings(); } + $bpNode = null; } $bp->retrieveStatesFromBackend(); + if ($this->params->get('addSimulation')) { + $this->simulationForm(); + } + + // TODO: ... + $renderer = new TileRenderer($this->view, $bp, $bpNode); + $renderer->setBaseUrl($this->url()) + ->setPath($this->params->getValues('path')); + $this->view->bpRenderer = $renderer; if ($bp->isLocked()) { $this->tabs()->extend(new DashboardAction()); } else { + $renderer->unlock(); $simulation = new Simulation($bp, $this->session()); if ($this->params->get('dismissSimulations')) { Notification::success( @@ -95,7 +184,11 @@ class ProcessController extends Controller } if ($this->isXhr()) { - $this->setAutorefreshInterval(10); + if ($this->params->get('addSimulation')) { + $this->setAutorefreshInterval(30); + } else { + $this->setAutorefreshInterval(10); + } } else { // This will trigger the very first XHR refresh immediately on page // load. Please not that this may hammer the server in case we would @@ -103,16 +196,50 @@ class ProcessController extends Controller $this->setAutorefreshInterval(1); } - if ($this->params->get('mode') === 'toplevel') { - $this->render('toplevel'); + if ($mode === 'tile') { + $this->setViewScript('process/bprenderer'); } } + protected function prepareProcess() + { + if ($this->params->get('unlocked')) { + $bp = $this->loadModifiedBpConfig(); + $bp->unlock(); + } else { + $bp = $this->loadBpConfig(); + } + + if ($node = $this->params->get('node')) { + // Render a specific node + $this->view->nodeName = $node; + $this->view->bp = $bp->getNode($node); + } + } + + protected function simulationForm() + { + $this->prepareProcess(); + $bp = $this->loadBpConfig(); + $nodename = $this->getParam('simulationNode'); + $node = $bp->getNode($nodename); + + $url = $this->getRequest()->getUrl()->without('addSimulation')->without('simulationNode'); + $this->view->form = $this->loadForm('simulation') + ->setSimulation(new Simulation($bp, $this->session())) + ->setNode($node) + ->setSuccessUrl($url) + ->handleRequest(); + + $this->view->node = $node; + } + /** * Show the source code for a process */ public function sourceAction() { + $this->prepareProcess(); $this->tabsForConfig()->activate('source'); $bp = $this->loadModifiedBpConfig(); @@ -141,6 +268,7 @@ class ProcessController extends Controller */ public function downloadAction() { + $this->prepareProcess(); $bp = $this->loadModifiedBpConfig(); header( @@ -162,6 +290,7 @@ class ProcessController extends Controller */ public function configAction() { + $this->prepareProcess(); $this->tabsForConfig()->activate('config'); $bp = $this->loadModifiedBpConfig(); diff --git a/application/views/scripts/process/bprenderer.phtml b/application/views/scripts/process/bprenderer.phtml new file mode 100644 index 0000000..e99a95d --- /dev/null +++ b/application/views/scripts/process/bprenderer.phtml @@ -0,0 +1,12 @@ +compact && ! $this->showFullscreen): ?> +
+tabs ?> +

title ?>

+ actions->render() ?> +
+ + +
+ bpRenderer->render() ?> + render('warnings.phtml') ?> +
From 1c7792321a1cacac74b7b89b72cea84c89478b0f Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 00:48:35 +0100 Subject: [PATCH 071/256] process/show: use actionBar, not single links --- application/views/scripts/process/show.phtml | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/application/views/scripts/process/show.phtml b/application/views/scripts/process/show.phtml index 5c1bedc..1af2b8a 100644 --- a/application/views/scripts/process/show.phtml +++ b/application/views/scripts/process/show.phtml @@ -8,21 +8,13 @@ use Icinga\Module\Businessprocess\BusinessProcess; tabs ?>

formSelect('config', $this->configName, array('class' => 'autosubmit'), $this->processList) ?> -
- -icon('dashboard') ?> -bpconfig->isLocked()): ?> -icon('lock') ?> - -bpconfig->isEmpty()): ?> -icon('lock-open') ?> - -icon('wrench') ?> - +

+actions ?>
+
form ?>
bpconfig->isLocked()): ?> qlink('Add new node', 'businessprocess/node/add', array('config' => $this->configName)) ?> From 281b3286bd863b0e5e8b81ff8a038375b7a0b2ea Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 00:49:21 +0100 Subject: [PATCH 072/256] ProcessController: switch config based on a field --- application/controllers/ProcessController.php | 2 +- application/views/scripts/process/show.phtml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index a095dea..c13389b 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -318,7 +318,7 @@ class ProcessController extends Controller protected function redirectOnConfigSwitch() { $request = $this->getRequest(); - if ($request->isPost()) { + if ($request->isPost() && $request->getPost('action') === 'switchConfig') { // We switched the process in the config dropdown list $params = array( 'config' => $request->getPost('config') diff --git a/application/views/scripts/process/show.phtml b/application/views/scripts/process/show.phtml index 1af2b8a..bc23501 100644 --- a/application/views/scripts/process/show.phtml +++ b/application/views/scripts/process/show.phtml @@ -7,6 +7,7 @@ use Icinga\Module\Businessprocess\BusinessProcess;
tabs ?>

+ formSelect('config', $this->configName, array('class' => 'autosubmit'), $this->processList) ?>

actions ?> From f09a246392db4bc41951c9636cc18a973a882444 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 00:49:46 +0100 Subject: [PATCH 073/256] process/show: failsafe process rendering --- application/views/scripts/process/show.phtml | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/application/views/scripts/process/show.phtml b/application/views/scripts/process/show.phtml index bc23501..ffae377 100644 --- a/application/views/scripts/process/show.phtml +++ b/application/views/scripts/process/show.phtml @@ -51,7 +51,20 @@ use Icinga\Module\Businessprocess\BusinessProcess;
-bp->renderHtml($this) ?> +bp->renderHtml($this); +} catch (Exception $e) { + printf( + '
  • %s: %s
', + $this->translate('Error'), + $this->escape($e->getMessage()) + ); +} + + +?> bpconfig->isLocked()): ?> bp instanceof BusinessProcess): /* do not render when showing subtree */ ?> bp->renderUnbound($this) ?> From 386326efdf78606005d747d6bb650c9abe6e6e25 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 00:54:07 +0100 Subject: [PATCH 074/256] HostNodeTest: fix and improve test --- test/php/library/Businessprocess/HostNodeTest.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/php/library/Businessprocess/HostNodeTest.php b/test/php/library/Businessprocess/HostNodeTest.php index 7db45db..dd1c01a 100644 --- a/test/php/library/Businessprocess/HostNodeTest.php +++ b/test/php/library/Businessprocess/HostNodeTest.php @@ -17,10 +17,18 @@ class HostNodeTest extends BaseTestCase ); } - public function testReturnsCorrectAlias() + public function testReturnsCorrectIdentifierWhenCastedToString() { $this->assertEquals( 'localhost;Hoststatus', + (string) $this->localhost() + ); + } + + public function testReturnsCorrectAlias() + { + $this->assertEquals( + 'localhost', $this->localhost()->getAlias() ); } From d92198fa10c9cee4d68bf4c76d9b4d54994bc5d5 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 02:09:11 +0100 Subject: [PATCH 075/256] Img and Icon introduced, used in NodeTile --- library/Businessprocess/Html/Icon.php | 26 +++++++ library/Businessprocess/Html/Img.php | 72 +++++++++++++++++++ .../Renderer/TileRenderer/NodeTile.php | 27 ++++++- public/css/module.less | 30 +++++--- 4 files changed, 145 insertions(+), 10 deletions(-) create mode 100644 library/Businessprocess/Html/Icon.php create mode 100644 library/Businessprocess/Html/Img.php diff --git a/library/Businessprocess/Html/Icon.php b/library/Businessprocess/Html/Icon.php new file mode 100644 index 0000000..afe4c22 --- /dev/null +++ b/library/Businessprocess/Html/Icon.php @@ -0,0 +1,26 @@ +setAttributes($attributes); + $icon->attributes()->add('class', array('icon', 'icon-' . $name)); + return $icon; + } +} diff --git a/library/Businessprocess/Html/Img.php b/library/Businessprocess/Html/Img.php new file mode 100644 index 0000000..eccd4a8 --- /dev/null +++ b/library/Businessprocess/Html/Img.php @@ -0,0 +1,72 @@ + ''); + + protected function __construct() + { + } + + /** + * @param Url|string $url + * @param array $urlParams + * @param array $attributes + * + * @return static + */ + public static function create($url, $urlParams = null, array $attributes = null) + { + $img = new static(); + $img->setAttributes($attributes); + $img->attributes()->registerCallbackFor('src', array($img, 'getSrcAttribute')); + $img->setUrl($url, $urlParams); + return $img; + } + + public function setUrl($url, $urlParams) + { + if ($url instanceof WebUrl) { // Hint: Url is also a WebUrl + if ($urlParams !== null) { + $url->addParams($urlParams); + } + + $this->url = $url; + } else { + if ($urlParams === null) { + $this->url = Url::fromPath($url); + } else { + $this->url = Url::fromPath($url, $urlParams); + } + } + + $this->url->getParams(); + } + + /** + * @return Attribute + */ + public function getSrcAttribute() + { + return new Attribute('src', $this->getUrl()->getAbsoluteUrl('&')); + } + + /** + * @return Url + */ + public function getUrl() + { + // TODO: What if null? #? + return $this->url; + } +} diff --git a/library/Businessprocess/Renderer/TileRenderer/NodeTile.php b/library/Businessprocess/Renderer/TileRenderer/NodeTile.php index 9356c70..83f33fb 100644 --- a/library/Businessprocess/Renderer/TileRenderer/NodeTile.php +++ b/library/Businessprocess/Renderer/TileRenderer/NodeTile.php @@ -3,17 +3,28 @@ namespace Icinga\Module\Businessprocess\Renderer\TileRenderer; use Icinga\Module\Businessprocess\BpNode; +use Icinga\Module\Businessprocess\HostNode; use Icinga\Module\Businessprocess\Html\BaseElement; +use Icinga\Module\Businessprocess\Html\HtmlString; +use Icinga\Module\Businessprocess\Html\Icon; use Icinga\Module\Businessprocess\Html\Link; use Icinga\Module\Businessprocess\ImportedNode; use Icinga\Module\Businessprocess\MonitoredNode; use Icinga\Module\Businessprocess\Node; use Icinga\Module\Businessprocess\Renderer\TileRenderer; +use Icinga\Module\Businessprocess\ServiceNode; class NodeTile extends BaseElement { protected $tag = 'div'; + /** + * NodeTile constructor. + * @param TileRenderer $renderer + * @param $name + * @param Node $node + * @param null $path + */ public function __construct(TileRenderer $renderer, $name, Node $node, $path = null) { $attributes = $this->attributes(); @@ -43,7 +54,21 @@ class NodeTile extends BaseElement } } - $link = Link::create($node->getAlias(), $url); + if ($node instanceof ServiceNode) { + $link = Link::create( + Icon::create('service'), + $url + )->addContent($node->getHostname()) + ->addContent(HtmlString::create('
')) + ->addContent($node->getServiceDescription()); + } elseif ($node instanceof HostNode) { + $link = Link::create( + Icon::create('host'), + $url + )->addContent($node->getHostname()); + } else { + $link = Link::create($node->getAlias(), $url); + } $this->add($link); if ($node instanceof BpNode) { diff --git a/public/css/module.less b/public/css/module.less index 793d7f2..4471d5d 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -388,6 +388,7 @@ table.bp { .tiles > div > a { text-decoration: none; + font-size: 0.5em; color: inherit; vertical-align: middle; text-align: center; @@ -396,8 +397,17 @@ table.bp { font-weight: bold; word-wrap: break-word; width: 100%; - height: 6em; + height: 12em; box-sizing: border-box; + &:focus { + outline: none; + text-decoration: underline; + } + i { + float: left; + font-size: 2.5em; + margin-top: -0.1em; + } } .tiles > div > a:hover { @@ -459,10 +469,6 @@ table.bp { } } -.tiles > .monitored-node a { - font-size: 0.8em; -} - .tiles .missing a { pointer-events: none; cursor: default; @@ -571,14 +577,20 @@ table.bp { } .breadcrumb li:last-child a { padding-right: 1em; - pointer-events: none; cursor: default; } +.breadcrumb li:last-child a:hover { -.breadcrumb li a:hover, .breadcrumb li a:focus { background: @text-color; color: white; } -.breadcrumb li a:hover:after, .breadcrumb li a:focus:after { border-left-color: @text-color; } +} -/** END of breadcrumg **/ +.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 a:focus { + text-decoration: underline; +} + +/** END of breadcrumb **/ ul.error { From 9ec932adcdfb983bb1ff9536833eb9d44af883ec Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 02:09:45 +0100 Subject: [PATCH 076/256] Link: remove superfluous variable --- library/Businessprocess/Html/Link.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/library/Businessprocess/Html/Link.php b/library/Businessprocess/Html/Link.php index d02d049..052698f 100644 --- a/library/Businessprocess/Html/Link.php +++ b/library/Businessprocess/Html/Link.php @@ -9,9 +9,6 @@ class Link extends BaseElement { protected $tag = 'a'; - /** @var Renderable */ - protected $content; - /** @var Url */ protected $url; From 4f5969d4b7e813cd7e32fe5be3e80e42adb4cc92 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 02:10:19 +0100 Subject: [PATCH 077/256] AddNew: fix link --- library/Businessprocess/Renderer/TileRenderer/AddNewTile.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/Businessprocess/Renderer/TileRenderer/AddNewTile.php b/library/Businessprocess/Renderer/TileRenderer/AddNewTile.php index d61fa4a..cfc85f8 100644 --- a/library/Businessprocess/Renderer/TileRenderer/AddNewTile.php +++ b/library/Businessprocess/Renderer/TileRenderer/AddNewTile.php @@ -19,14 +19,14 @@ class AddNewTile extends BaseElement public function __construct(Renderer $renderer) { $bp = $renderer->getBusinessProcess(); - $path = $renderer->getMyPath(); + $path = $renderer->getCurrentPath(); $params = array( 'config' => $bp->getName() ); // Workaround for array issues - $url = Url::fromPath('businessprocess/process/create'); + $url = Url::fromPath('businessprocess/node/add'); $p = $url->getParams(); $p->mergeValues($params); if (! empty($path)) { From c4b0aa15782064db6e3b65e595b27a97b23fb6b1 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 02:11:21 +0100 Subject: [PATCH 078/256] SimulationForm: restructure, base on node props --- application/forms/SimulationForm.php | 56 ++++++++++++++++------------ 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/application/forms/SimulationForm.php b/application/forms/SimulationForm.php index 44ad3bf..2372f8a 100644 --- a/application/forms/SimulationForm.php +++ b/application/forms/SimulationForm.php @@ -2,56 +2,65 @@ namespace Icinga\Module\Businessprocess\Forms; -use Icinga\Module\Businessprocess\BpNode; -use Icinga\Module\Businessprocess\Web\Form\QuickForm; +use Icinga\Module\Businessprocess\MonitoredNode; use Icinga\Module\Businessprocess\Simulation; -use Icinga\Web\Request; +use Icinga\Module\Businessprocess\Web\Form\QuickForm; class SimulationForm extends QuickForm { + /** @var MonitoredNode */ protected $node; + /** @var MonitoredNode */ protected $simulatedNode; + /** @var Simulation */ protected $simulation; public function setup() { - $states = array( - null => sprintf( - $this->translate('Use current state (%s)'), - $this->translate($this->node->getStateName()) + $states = array_merge( + array( + null => sprintf( + $this->translate('Use current state (%s)'), + $this->translate($this->node->getStateName()) + ) ), - '0' => $this->translate('OK'), - '1' => $this->translate('WARNING'), - '2' => $this->translate('CRITICAL'), - '3' => $this->translate('UNKNOWN'), - '99' => $this->translate('PENDING'), + $this->node->enumStateNames() ); // TODO: Fetch state from object if ($this->simulatedNode) { - $states[$this->simulatedNode->getState()] . sprintf(' (%s)', $this->translate('Current simulation')); + $simulatedState = $this->simulatedNode->getState(); + $states[$simulatedState] = sprintf( + '%s (%s)', + $simulatedState, + $this->translate('Current simulation') + ); $node = $this->simulatedNode; } else { $node = $this->node; } + $this->addHtml('

Configure this simulation

'); + $this->addElement('select', 'state', array( 'label' => $this->translate('State'), 'multiOptions' => $states, + 'class' => 'autosubmit', 'value' => $this->simulatedNode ? $node->getState() : null, )); + if (ctype_digit($this->getSentValue('state'))) { + $this->addElement('checkbox', 'acknowledged', array( + 'label' => $this->translate('Acknowledged'), + 'value' => $node->isAcknowledged(), + )); - $this->addElement('checkbox', 'acknowledged', array( - 'label' => $this->translate('Acknowledged'), - 'value' => $node->isAcknowledged(), - )); - - $this->addElement('checkbox', 'in_downtime', array( - 'label' => $this->translate('In downtime'), - 'value' => $node->isInDowntime(), - )); + $this->addElement('checkbox', 'in_downtime', array( + 'label' => $this->translate('In downtime'), + 'value' => $node->isInDowntime(), + )); + } $this->setSubmitLabel($this->translate('Apply')); } @@ -62,7 +71,7 @@ class SimulationForm extends QuickForm return $this; } - public function setSimulation($simulation) + public function setSimulation(Simulation $simulation) { $this->simulation = $simulation; @@ -95,5 +104,6 @@ class SimulationForm extends QuickForm $this->notifySuccess($this->translate('Simulation has been removed')); } } + $this->redirectOnSuccess(); } } From bbcc6eaecf45494374f5ca003a6255798a500617 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 02:13:41 +0100 Subject: [PATCH 079/256] UploadForm: first import, still testing --- application/controllers/ProcessController.php | 4 + application/forms/BpUploadForm.php | 232 ++++++++++++++++++ .../views/scripts/process/upload.phtml | 5 +- 3 files changed, 237 insertions(+), 4 deletions(-) create mode 100644 application/forms/BpUploadForm.php diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index c13389b..fe08ea1 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -52,6 +52,10 @@ class ProcessController extends Controller { $this->setTitle($this->translate('Upload a business process config file')); $this->tabsForCreate()->activate('upload'); + $this->view->form = $this->loadForm('BpUpload') + ->setStorage($this->storage()) + ->setSuccessUrl('businessprocess/process/show') + ->handleRequest(); } /** diff --git a/application/forms/BpUploadForm.php b/application/forms/BpUploadForm.php new file mode 100644 index 0000000..ee24582 --- /dev/null +++ b/application/forms/BpUploadForm.php @@ -0,0 +1,232 @@ +addElement('text', 'name', array( + 'label' => $this->translate('Name'), + // 'required' => true, + 'description' => $this->translate( + 'This is the unique identifier of this process' + ), + )); + + $this->addElement('text', 'title', array( + 'label' => $this->translate('Title'), + 'description' => $this->translate( + 'Usually this title will be shown for this process. Equals name' + . ' if not given' + ), + )); + + $this->addElement('select', 'backend_name', array( + 'label' => $this->translate('Backend'), + 'description' => $this->translate( + 'Icinga Web Monitoring Backend where current object states for' + . ' this process should be retrieved from' + ), + 'multiOptions' => array( + null => $this->translate('Use the configured default backend'), + ) + $this->listAvailableBackends() + )); + + $this->addElement('select', 'state_type', array( + 'label' => $this->translate('State Type'), + 'required' => true, + 'description' => $this->translate( + 'Whether this process should be based on Icinga hard or soft states' + ), + 'multiOptions' => array( + 'hard' => $this->translate('Use HARD states'), + 'soft' => $this->translate('Use SOFT states'), + ) + )); + + $this->setAttrib('enctype', 'multipart/form-data'); + + $tmpdir = sys_get_temp_dir(); + + $this->addElement('file', 'uploaded_file', array( + 'label' => $this->translate('File'), + 'destination' => $tmpdir, + 'required' => true, + )); + + /** @var \Zend_Form_Element_File $el */ + $el = $this->getElement('uploaded_file'); + $el->setValueDisabled(true); + + if ($this->config === null) { + $this->setSubmitLabel( + $this->translate('Add') + ); + } else { + $config = $this->config; + + $this->getElement('name') + ->setValue($config->getName()) + ->setAttrib('readonly', true); + + if ($config->hasTitle()) { + $this->getElement('title')->setValue($config->getTitle()); + } + + if ($config->hasBackend()) { + $this->getElement('backend_name')->setValue( + $config->getBackend()->getName() + ); + } + if ($config->usesSoftStates()) { + $this->getElement('state_type')->setValue('soft'); + } else { + $this->getElement('state_type')->setValue('hard'); + } + + $this->setSubmitLabel( + $this->translate('Upload') + ); +/* + $label = $this->translate('Delete'); + $el = $this->createElement('submit', $label) + ->setLabel($label) + ->setDecorators(array('ViewHelper')); + $this->deleteButtonName = $el->getName(); + $this->addElement($el); +*/ + } + } + + protected function listAvailableBackends() + { + $keys = array_keys(Config::module('monitoring', 'backends')->toArray()); + return array_combine($keys, $keys); + } + + public function setStorage(LegacyStorage $storage) + { + $this->storage = $storage; + return $this; + } + + public function setProcessConfig(BusinessProcess $config) + { + $this->config = $config; + return $this; + } + + public function onSuccess() + { + + $tmpdir = sys_get_temp_dir(); + $tmpfile = tempnam($tmpdir, 'bpupload_'); + unlink($tmpfile); + $values = $this->getValues(); + /** @var \Zend_Form_Element_File $el */ + $el = $this->getElement('uploaded_file'); + var_dump($el->getFileName()); + var_dump($tmpfile); + $el->addFilter('Rename', $tmpfile); + if (!$el->receive()) { + print_r($el->file->getMessages()); + } + echo file_get_contents($tmpfile); + unlink($tmpfile); + echo "DONE\n"; + exit; + $name = $this->getValue('name'); + $title = $this->getValue('title'); + $backend = $this->getValue('backend'); + /* + onSuccess: + $uploadedData = $form->getValues(); + $fullFilePath = $form->file->getFileName(); + */ + var_dump($this->getValues()); + + exit; + + if ($this->config === null) { + // New config + $config = new BusinessProcess(); + $config->setName($name); + if ($title) { + $config->setTitle($title); + } + if ($backend) { + $config->setBackendName($backend); + } + if ($this->getValue('state_type') === 'soft') { + $config->useSoftStates(); + } else { + $config->useHardStates(); + } + $this->storage->storeProcess($config); + $config->clearAppliedChanges(); + $this->setSuccessUrl( + $this->getSuccessUrl()->setParams( + array('config' => $name, 'unlocked' => true) + ) + ); + + $this->redirectOnSuccess(sprintf('Process %s has been created', $name)); + } else { + $config = $this->config; + if ($title) { + $config->setTitle($title); + } + if ($backend) { + $config->setBackendName($backend); + } + if ($this->getValue('state_type') === 'soft') { + $config->useSoftStates(); + } else { + $config->useHardStates(); + } + + $this->storage->storeProcess($config); + $config->clearAppliedChanges(); + $this->getSuccessUrl()->setParam('config', $name); + Notification::success(sprintf('Process %s has been stored', $name)); + } + } + + public function hasDeleteButton() + { + return $this->deleteButtonName !== null; + } + + public function shouldBeDeleted() + { + if (! $this->hasDeleteButton()) { + return false; + } + + $name = $this->deleteButtonName; + return $this->getSentValue($name) === $this->getElement($name)->getLabel(); + } +} diff --git a/application/views/scripts/process/upload.phtml b/application/views/scripts/process/upload.phtml index 36262ad..7adf827 100644 --- a/application/views/scripts/process/upload.phtml +++ b/application/views/scripts/process/upload.phtml @@ -4,8 +4,5 @@
-
    -
  • translate('This has not been implemented yet') ?>
  • -
+form ?>
- From ee99549341e3213dc13ae6e784b7a66795abc13f Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 02:26:12 +0100 Subject: [PATCH 080/256] Breadcrumb: cleanup and externalize --- .../Businessprocess/Renderer/Breadcrumb.php | 57 +++++++++++++++++++ library/Businessprocess/Renderer/Renderer.php | 2 +- .../Businessprocess/Renderer/TileRenderer.php | 55 +----------------- 3 files changed, 61 insertions(+), 53 deletions(-) create mode 100644 library/Businessprocess/Renderer/Breadcrumb.php diff --git a/library/Businessprocess/Renderer/Breadcrumb.php b/library/Businessprocess/Renderer/Breadcrumb.php new file mode 100644 index 0000000..ab4d36d --- /dev/null +++ b/library/Businessprocess/Renderer/Breadcrumb.php @@ -0,0 +1,57 @@ + 'breadcrumb', + 'data-base-target' => '_main' + ); + + /** + * @param Renderer $renderer + * @return static + */ + public static function create(Renderer $renderer) + { + $bp = $renderer->getBusinessProcess(); + $breadcrumb = new static; + $breadcrumb->add(Element::create('li')->add( + Link::create($bp->getTitle(), $renderer->getBaseUrl()) + )); + $path = $renderer->getCurrentPath(); + + $parts = array(); + while ($node = array_pop($path)) { + array_unshift( + $parts, + static::renderNode($bp->getNode($node), $path, $renderer) + ); + } + $breadcrumb->addContent($parts); + + return $breadcrumb; + } + + /** + * @param BpNode $parent + * @return NodeTile + */ + protected static function renderNode(BpNode $node, $path, Renderer $renderer) + { + // TODO: something more generic than NodeTile? + $p = new NodeTile($renderer, (string) $node, $node, $path); + $p->attributes()->add('class', $renderer->getNodeClasses($node)); + $p->setTag('li'); + return $p; + } +} diff --git a/library/Businessprocess/Renderer/Renderer.php b/library/Businessprocess/Renderer/Renderer.php index d2f47ed..fd6fb21 100644 --- a/library/Businessprocess/Renderer/Renderer.php +++ b/library/Businessprocess/Renderer/Renderer.php @@ -157,7 +157,7 @@ abstract class Renderer extends Html return $this->path; } - public function getMyPath() + public function getCurrentPath() { $path = $this->getPath(); if ($this->rendersSubNode()) { diff --git a/library/Businessprocess/Renderer/TileRenderer.php b/library/Businessprocess/Renderer/TileRenderer.php index 1ae1b91..5e6123f 100644 --- a/library/Businessprocess/Renderer/TileRenderer.php +++ b/library/Businessprocess/Renderer/TileRenderer.php @@ -37,67 +37,18 @@ class TileRenderer extends Renderer $this->add(new AddNewTile($this)); } + $path = $this->getCurrentPath(); foreach ($nodes as $name => $node) { - $this->add(new NodeTile($this, $name, $node)); + $this->add(new NodeTile($this, $name, $node, $path)); } $nodesDiv->addContent($this->getContent()); - $this->setContent($this->renderBreadCrumb()) + $this->setContent(Breadcrumb::create($this)) ->addContent($nodesDiv); return parent::render(); } - public function renderBreadCrumb() - { - $breadcrumb = Element::create('ul', array( - 'class' => 'breadcrumb', - 'data-base-target' => '_main' - )); - - $breadcrumb->add(Element::create('li')->add( - Link::create($this->bp->getTitle(), $this->getBaseUrl()) - )); - $bp = $this->bp; - $path = $this->getMyPath(); - $max = 20; - $chosen = array(); - for ($i = 1; $i <= $max; $i++) { - if (! empty($path)) { - $chosen[] = array_pop($path); - } - } - $chosen = array_reverse($chosen); - $consumed = array(); - while ($parent = array_shift($chosen)) { - $breadcrumb->add($this->renderParent($bp->getNode($parent), $consumed)); - $consumed[] = $parent; - } - - return $breadcrumb; - } - - /** - * @param BpNode $parent - */ - public function renderParent(BpNode $parent, $path) - { - $p = new NodeTile($this, (string) $parent, $parent, $path); - $p->attributes()->add('class', $this->getNodeClasses($parent)); - $p->setTag('li'); - return $p; - } - - /** - * @return array - */ - public function getDefaultAttributes() - { - return array( - 'class' => 'tiles aaaa' . $this->howMany() - ); - } - /** * A CSS class giving a rough indication of how many nodes we have * From b109a46843b103e125fa015c9a3eb80feb3030c4 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 08:23:53 +0100 Subject: [PATCH 081/256] Renderer: remove some deps, now that we cleaned up --- library/Businessprocess/Renderer/Renderer.php | 1 - library/Businessprocess/Renderer/TileRenderer.php | 3 --- 2 files changed, 4 deletions(-) diff --git a/library/Businessprocess/Renderer/Renderer.php b/library/Businessprocess/Renderer/Renderer.php index fd6fb21..9a6ad32 100644 --- a/library/Businessprocess/Renderer/Renderer.php +++ b/library/Businessprocess/Renderer/Renderer.php @@ -9,7 +9,6 @@ use Icinga\Module\Businessprocess\Html\Container; use Icinga\Module\Businessprocess\Html\Element; use Icinga\Module\Businessprocess\Html\Html; use Icinga\Module\Businessprocess\Node; -use Icinga\Module\Businessprocess\Renderer\TileRenderer\NodeTile; use Icinga\Module\Businessprocess\Web\Url; use Icinga\Web\View; diff --git a/library/Businessprocess/Renderer/TileRenderer.php b/library/Businessprocess/Renderer/TileRenderer.php index 5e6123f..cc06413 100644 --- a/library/Businessprocess/Renderer/TileRenderer.php +++ b/library/Businessprocess/Renderer/TileRenderer.php @@ -2,10 +2,7 @@ namespace Icinga\Module\Businessprocess\Renderer; -use Icinga\Module\Businessprocess\BpNode; use Icinga\Module\Businessprocess\Html\Container; -use Icinga\Module\Businessprocess\Html\Element; -use Icinga\Module\Businessprocess\Html\Link; use Icinga\Module\Businessprocess\Renderer\TileRenderer\AddNewTile; use Icinga\Module\Businessprocess\Renderer\TileRenderer\NodeTile; From 3108d08ba3329a6f43b870dc0d76d2a59f52ec2e Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 16:00:55 +0100 Subject: [PATCH 082/256] IndexController: default to tile renderer --- application/controllers/IndexController.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/application/controllers/IndexController.php b/application/controllers/IndexController.php index e8d92ab..c43a044 100644 --- a/application/controllers/IndexController.php +++ b/application/controllers/IndexController.php @@ -11,20 +11,19 @@ class IndexController extends Controller */ public function indexAction() { - $this->tabs()->add('welcome', array( - 'label' => $this->translate('Business Processes'), - 'url' => $this->getRequest()->getUrl() - ))->activate('welcome'); - $configs = $this->storage()->listProcesses(); if (! empty($configs)) { // Redirect to show the first process if there is any $this->redirectNow( - 'businessprocess/process/show', + 'businessprocess/process/show?mode=tile', array('config' => key($configs)) ); } + $this->tabs()->add('welcome', array( + 'label' => $this->translate('Business Processes'), + 'url' => $this->getRequest()->getUrl() + ))->activate('welcome'); // Check back from time to time, maybe someone created a process $this->setAutorefreshInterval(30); From d716b97c25d0a3203062219d3960f7f80dfca78e Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 16:02:21 +0100 Subject: [PATCH 083/256] ProcessController: split logic, render breadcrumb --- application/controllers/ProcessController.php | 89 ++++--------------- .../Businessprocess/Renderer/TileRenderer.php | 3 +- 2 files changed, 18 insertions(+), 74 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index fe08ea1..d96f24d 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -4,12 +4,9 @@ namespace Icinga\Module\Businessprocess\Controllers; use Icinga\Module\Businessprocess\Controller; use Icinga\Module\Businessprocess\ConfigDiff; +use Icinga\Module\Businessprocess\Renderer\Breadcrumb; use Icinga\Module\Businessprocess\Renderer\TileRenderer; use Icinga\Module\Businessprocess\Simulation; -use Icinga\Module\Businessprocess\ProcessChanges; -use Icinga\Module\Businessprocess\Storage\LegacyStorage; -use Icinga\Module\Businessprocess\Forms\BpConfigForm; -use Icinga\Module\Businessprocess\Forms\DeleteConfigForm; use Icinga\Module\Businessprocess\Html\Link; use Icinga\Module\Businessprocess\Web\Url; use Icinga\Web\Notification; @@ -65,66 +62,7 @@ class ProcessController extends Controller { $mode = $this->params->get('mode'); $unlocked = (bool) $this->params->get('unlocked'); - - if ($mode === 'tile') { - $this->actions()->add( - Link::create( - $this->translate('Tree'), - 'businessprocess/process/show', - $this->currentProcessParams(), - array('class' => 'icon-sitemap') - ) - ); - } else { - $this->actions()->add( - Link::create( - $this->translate('Tiles'), - $this->url()->with('mode', 'tile'), - null, - array('class' => 'icon-dashboard') - ) - ); - } - - if ($unlocked) { - $this->actions()->add( - Link::create( - $this->translate('Lock'), - $this->url()->without('unlocked'), - null, - array( - 'class' => 'icon-lock', - 'title' => $this->translate('Lock this process'), - ) - ) - ); - } else { - $this->actions()->add( - Link::create( - $this->translate('Unlock'), - $this->url()->with('unlocked', true), - null, - array( - 'class' => 'icon-lock-open', - 'title' => $this->translate('Unlock this process'), - ) - ) - ); - } - - $this->actions()->add( - Link::create( - $this->translate('Store'), - 'businessprocess/process/config', - $this->currentProcessParams(), - array( - 'class' => 'icon-wrench', - 'title' => $this->translate('Modify this process'), - 'data-base-target' => '_next', - ) - ) - ); - + $this->prepareProcessActions(); $this->prepareProcess(); $this->redirectOnConfigSwitch(); @@ -135,8 +73,6 @@ class ProcessController extends Controller $bp = $this->loadBpConfig(); } - $this->setTitle('Business Process "%s"', $bp->getTitle()); - $this->tabsForShow()->activate('show'); // Do not lock empty configs if ($bp->isEmpty() && ! $this->view->compact && $bp->isLocked()) { @@ -162,16 +98,14 @@ class ProcessController extends Controller $this->simulationForm(); } - // TODO: ... - $renderer = new TileRenderer($this->view, $bp, $bpNode); - $renderer->setBaseUrl($this->url()) - ->setPath($this->params->getValues('path')); - $this->view->bpRenderer = $renderer; + + $this->setTitle('Business Process "%s"', $bp->getTitle()); + $this->tabsForShow()->activate('show'); if ($bp->isLocked()) { $this->tabs()->extend(new DashboardAction()); } else { - $renderer->unlock(); + $simulation = new Simulation($bp, $this->session()); if ($this->params->get('dismissSimulations')) { Notification::success( @@ -187,6 +121,17 @@ class ProcessController extends Controller $bp->applySimulation($simulation); } + // TODO: ... + $renderer = new TileRenderer($this->view, $bp, $bpNode); + $renderer->setBaseUrl($this->url()) + ->setPath($this->params->getValues('path')); + $this->view->bpRenderer = $renderer; + $this->view->breadcrumb = Breadcrumb::create($renderer); + + if (! $bp->isLocked()) { + $renderer->unlock(); + } + if ($this->isXhr()) { if ($this->params->get('addSimulation')) { $this->setAutorefreshInterval(30); diff --git a/library/Businessprocess/Renderer/TileRenderer.php b/library/Businessprocess/Renderer/TileRenderer.php index cc06413..8a3714c 100644 --- a/library/Businessprocess/Renderer/TileRenderer.php +++ b/library/Businessprocess/Renderer/TileRenderer.php @@ -40,8 +40,7 @@ class TileRenderer extends Renderer } $nodesDiv->addContent($this->getContent()); - $this->setContent(Breadcrumb::create($this)) - ->addContent($nodesDiv); + $this->setContent($nodesDiv); return parent::render(); } From 28f60263228f58bc7859f53b33a4d4808be83643 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 16:03:12 +0100 Subject: [PATCH 084/256] AddNewTile: render an icon --- application/views/scripts/process/bprenderer.phtml | 1 + application/views/scripts/process/show.phtml | 7 +++++-- .../Businessprocess/Renderer/TileRenderer/AddNewTile.php | 5 +++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/application/views/scripts/process/bprenderer.phtml b/application/views/scripts/process/bprenderer.phtml index e99a95d..ef66bf5 100644 --- a/application/views/scripts/process/bprenderer.phtml +++ b/application/views/scripts/process/bprenderer.phtml @@ -2,6 +2,7 @@
tabs ?>

title ?>

+ breadcrumb->render() ?> actions->render() ?>
diff --git a/application/views/scripts/process/show.phtml b/application/views/scripts/process/show.phtml index ffae377..e74bc4a 100644 --- a/application/views/scripts/process/show.phtml +++ b/application/views/scripts/process/show.phtml @@ -6,10 +6,13 @@ use Icinga\Module\Businessprocess\BusinessProcess; compact): ?>
tabs ?> -

+

escape($this->title) ?>

+ + breadcrumb->render() ?> actions ?>

diff --git a/library/Businessprocess/Renderer/TileRenderer/AddNewTile.php b/library/Businessprocess/Renderer/TileRenderer/AddNewTile.php index cfc85f8..84b0267 100644 --- a/library/Businessprocess/Renderer/TileRenderer/AddNewTile.php +++ b/library/Businessprocess/Renderer/TileRenderer/AddNewTile.php @@ -4,6 +4,7 @@ namespace Icinga\Module\Businessprocess\Renderer\TileRenderer; use Icinga\Module\Businessprocess\BusinessProcess; use Icinga\Module\Businessprocess\Html\BaseElement; +use Icinga\Module\Businessprocess\Html\Icon; use Icinga\Module\Businessprocess\Html\Link; use Icinga\Module\Businessprocess\Renderer\Renderer; use Icinga\Module\Businessprocess\Web\Url; @@ -35,13 +36,13 @@ class AddNewTile extends BaseElement $this->add( Link::create( - $this->translate('Add'), + Icon::create('plus'), $url, null, array( 'title' => $this->translate('Add a new business process node') ) - ) + )->addContent($this->translate('Add')) ); } } From 1955b4fccd0be1d4526111c852218df1533b2506 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 16:04:31 +0100 Subject: [PATCH 085/256] css: render tiles with borders, bg on hover --- public/css/module.less | 100 +++++++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 39 deletions(-) diff --git a/public/css/module.less b/public/css/module.less index 4471d5d..9dc16f0 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -355,24 +355,19 @@ table.bp { clear: both; } -.tiles.few { font-size: 3em; } -.tiles.normal { font-size: 2em; } -.tiles.many { font-size: 1.5em; } - -#layout.twocols, #layout.layout-minimal, .compact { - .tiles.few { font-size: 1.8em; } - .tiles.normal { font-size: 1.4em; } - .tiles.many { font-size: 0.9em; } -} - .tiles > div { width: 12em; height: 6em; float: left; margin-right: 0.2em; margin-bottom: 0.2em; - color: white; - background: #ccc; + color: @text-color; + border: 1px solid @text-color; + border-left: 0.5em solid @text-color; + a:hover { + background-color: @text-color; + color: white; + } } .tiles > div.parent::before { @@ -411,7 +406,7 @@ table.bp { } .tiles > div > a:hover { - color: @text-color; + color: white; } .badges { @@ -432,28 +427,42 @@ table.bp { .badge-missing { background: #ccc; } .tiles { - > .critical { background: @colorCritical; } - > .critical.handled { background: @colorCriticalHandled; } - > .down { background: @colorCritical; } - > .down.handled { background: @colorCriticalHandled; } - > .unknown { background: @colorUnknown; } - > .unknown.handled { background: @colorUnknownHandled; } - > .unreachable { background: @colorUnknown; } - > .unreachable.handled { background: @colorUnknownHandled; } - > .warning { background: @colorWarning; } - > .warning.handled { background: @colorWarningHandled; } - > .ok { background: @colorOk; } - > .pending { background: @colorPending; } - > .missing { background: #ccc; } + > .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; } + > .warning { border-color: @colorWarning; } + > .warning.handled { border-color: @colorWarningHandled; } + > .ok { border-color: @colorOk; } + > .pending { border-color: @colorPending; } + > .missing { border-color: #ccc; } +} + +.tiles { + > .critical a:hover { background: @colorCritical; } + > .critical.handled a:hover { background: @colorCriticalHandled; } + > .down a:hover { background: @colorCritical; } + > .down.handled a:hover { background: @colorCriticalHandled; } + > .unknown a:hover { background: @colorUnknown; } + > .unknown.handled a:hover { background: @colorUnknownHandled; } + > .unreachable a:hover { background: @colorUnknown; } + > .unreachable.handled a:hover { background: @colorUnknownHandled; } + > .warning a:hover { background: @colorWarning; } + > .warning.handled a:hover { background: @colorWarningHandled; } + > .ok a:hover { background: @colorOk; } + > .pending a:hover { background: @colorPending; } + > .missing a:hover { background: #ccc; } } .tiles > .addnew { - background: white; + border-color: @icinga-blue; a { color: @icinga-blue; - border: 0.2em solid @icinga-blue; &:hover { - color: white; background: @icinga-blue; } } @@ -474,6 +483,15 @@ table.bp { cursor: default; } +.tiles.few { font-size: 3em; } +.tiles.normal { font-size: 2em; } +.tiles.many { font-size: 1.5em; } + +#layout.twocols, #layout.layout-minimal, .compact { + .tiles.few { font-size: 1.8em; } + .tiles.normal { font-size: 1.4em; } + .tiles.many { font-size: 0.9em; } +} /** END of tiles **/ @@ -486,10 +504,9 @@ table.bp { .badges { display: inline-block; - padding: 0.02em 0 0.02em 0.5em; - margin-top: -0.2em; - + padding: 0 0 0 0.5em; .badge { + line-height: 1.25em; font-size: 0.8em; border: 1px solid white; margin: 0; @@ -538,7 +555,7 @@ table.bp { font-size: 1.2em; text-decoration: none; padding-left: 2em; - line-height: 3em; + line-height: 2.5em; background: @icinga-blue; position: relative; display: block; @@ -553,22 +570,22 @@ table.bp { display: block; width: 0; height: 0; - border-top: 1.5em solid transparent; - border-bottom: 1.5em solid transparent; + border-top: 1.3em solid transparent; + border-bottom: 1.2em solid transparent; position: absolute; - margin-top: -1.5em; + margin-top: -1.2em; top: 50%; left: 100%; } .breadcrumb li a:before { - border-left: 1.4em solid white; + border-left: 1.2em solid white; margin-left: 1px; z-index: 1; } .breadcrumb li a:after { - border-left: 1.4em solid @icinga-blue; + border-left: 1.2em solid @icinga-blue; z-index: 2; } @@ -576,7 +593,6 @@ table.bp { padding-left: 1em; } .breadcrumb li:last-child a { - padding-right: 1em; cursor: default; } .breadcrumb li:last-child a:hover { @@ -590,6 +606,12 @@ table.bp { text-decoration: underline; } +#layout.twocols, #layout.layout-minimal, .compact { + .breadcrumb { + font-size: 0.9em; + } +} + /** END of breadcrumb **/ From f2cf04d2477134594523fea94d13d85eebdf3cf6 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 16:17:09 +0100 Subject: [PATCH 086/256] .gitlab-ci.yml: initial import --- .gitlab-ci.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..7c8ae4c --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,11 @@ +stages: +- Unit-Tests + +Xenial: + stage: Unit-Tests + tags: + - xenial + - businessprocess + script: + - phpunit --testdox-text || phpunit --verbose + From 2bf4b8df19b6aa6c98b4cb0bbaaa1c838842d552 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 16:17:31 +0100 Subject: [PATCH 087/256] Url: fix compatibility with older Icinga Web 2 --- library/Businessprocess/Web/Url.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/library/Businessprocess/Web/Url.php b/library/Businessprocess/Web/Url.php index 4f508e6..01f2194 100644 --- a/library/Businessprocess/Web/Url.php +++ b/library/Businessprocess/Web/Url.php @@ -57,6 +57,12 @@ class Url extends WebUrl return $self; } + public function setBasePath($basePath) + { + $this->basePath = rtrim($basePath, '/ '); + return $this; + } + protected static function getRequest() { $app = Icinga::app(); From 657a103c96a7724d9e313ce56e55cde2c3fc4b99 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 16:40:45 +0100 Subject: [PATCH 088/256] Url: compatibility hack web2 v2.3.4 VS 2.4.0 --- library/Businessprocess/Web/Url.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/library/Businessprocess/Web/Url.php b/library/Businessprocess/Web/Url.php index 01f2194..cdd5efb 100644 --- a/library/Businessprocess/Web/Url.php +++ b/library/Businessprocess/Web/Url.php @@ -59,8 +59,11 @@ class Url extends WebUrl public function setBasePath($basePath) { - $this->basePath = rtrim($basePath, '/ '); - return $this; + if (property_exists($this, 'basePath')) { + parent::setBasePath($basePath); + } else { + return $this->setBaseUrl($basePath); + } } protected static function getRequest() From 1f5ad6fa1294bc36fffe602994c1e7d455c33489 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 16:52:57 +0100 Subject: [PATCH 089/256] Gitlab-CI: involve more systems --- .gitlab-ci.yml | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7c8ae4c..c3c8035 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -7,5 +7,28 @@ Xenial: - xenial - businessprocess script: - - phpunit --testdox-text || phpunit --verbose + - phpunit --testdox || phpunit --verbose +Debian Jessie: + stage: Unit-Tests + tags: + - jessie + - businessprocess + script: + - phpunit --testdox || phpunit --verbose + +CentOS 6: + stage: Unit-Tests + tags: + - centos6 + - businessprocess + script: + - phpunit --testdox || phpunit --verbose + +CentOS 7: + stage: Unit-Tests + tags: + - centos7 + - businessprocess + script: + - phpunit --testdox || phpunit --verbose From c90b0f0eb1f9cf66efc835636d9d6876f9781f0b Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 17:01:01 +0100 Subject: [PATCH 090/256] Attributes: fix PHP 5.3 compatibilty issue --- library/Businessprocess/Html/Attributes.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/library/Businessprocess/Html/Attributes.php b/library/Businessprocess/Html/Attributes.php index 3f9d677..77099cc 100644 --- a/library/Businessprocess/Html/Attributes.php +++ b/library/Businessprocess/Html/Attributes.php @@ -153,12 +153,15 @@ class Attributes /** * Callback must return an instance of Attribute * - * @param $name - * @param $callback + * @param string $name + * @param callable $callback * @return $this */ - public function registerCallbackFor($name, callable $callback) + public function registerCallbackFor($name, $callback) { + if (! is_callable($callback)) { + throw new ProgrammingError('registerCallBack expects a callable callback'); + } $this->callbacks[$name] = $callback; return $this; } From 11322d230984d96577f5f7dfefe787ef668b3782 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 17:28:58 +0100 Subject: [PATCH 091/256] ProcessController: add prepareProcessActions --- application/controllers/ProcessController.php | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index d96f24d..c03ada2 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -166,6 +166,71 @@ class ProcessController extends Controller } } + protected function prepareProcessActions() + { + $mode = $this->params->get('mode'); + $unlocked = (bool) $this->params->get('unlocked'); + + if ($mode === 'tile') { + $this->actions()->add( + Link::create( + $this->translate('Tree'), + 'businessprocess/process/show', + $this->currentProcessParams(), + array('class' => 'icon-sitemap') + ) + ); + } else { + $this->actions()->add( + Link::create( + $this->translate('Tiles'), + $this->url()->with('mode', 'tile'), + null, + array('class' => 'icon-dashboard') + ) + ); + } + + if ($unlocked) { + $this->actions()->add( + Link::create( + $this->translate('Lock'), + $this->url()->without('unlocked'), + null, + array( + 'class' => 'icon-lock', + 'title' => $this->translate('Lock this process'), + ) + ) + ); + } else { + $this->actions()->add( + Link::create( + $this->translate('Unlock'), + $this->url()->with('unlocked', true), + null, + array( + 'class' => 'icon-lock-open', + 'title' => $this->translate('Unlock this process'), + ) + ) + ); + } + + $this->actions()->add( + Link::create( + $this->translate('Store'), + 'businessprocess/process/config', + $this->currentProcessParams(), + array( + 'class' => 'icon-wrench', + 'title' => $this->translate('Modify this process'), + 'data-base-target' => '_next', + ) + ) + ); + } + protected function simulationForm() { $this->prepareProcess(); From e311d086ccb9f4c5dca95500a2543f2bb7a38c32 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 17:30:27 +0100 Subject: [PATCH 092/256] BpNode: add id helper --- library/Businessprocess/BpNode.php | 5 +++++ library/Businessprocess/Html/BaseElement.php | 1 - 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/library/Businessprocess/BpNode.php b/library/Businessprocess/BpNode.php index 52086e6..f57e2f5 100644 --- a/library/Businessprocess/BpNode.php +++ b/library/Businessprocess/BpNode.php @@ -286,6 +286,11 @@ class BpNode extends Node return $this->state; } + public function getHtmlId() + { + return 'businessprocess-' . preg_replace('/[\r\n\t\s]/', '_', (string) $this); + } + protected function invertSortingState($state) { return self::$sortStateInversionMap[$state >> self::SHIFT_FLAGS] << self::SHIFT_FLAGS; diff --git a/library/Businessprocess/Html/BaseElement.php b/library/Businessprocess/Html/BaseElement.php index 8fefed4..7043b24 100644 --- a/library/Businessprocess/Html/BaseElement.php +++ b/library/Businessprocess/Html/BaseElement.php @@ -27,7 +27,6 @@ abstract class BaseElement extends Html } else { $this->attributes = Attributes::wantAttributes($default); } - } return $this->attributes; From e1d0996cefc5c5af63db97b9a87c0fa3383cb888 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 22:12:56 +0100 Subject: [PATCH 093/256] NodeTile: accept any renderer --- library/Businessprocess/Renderer/TileRenderer/NodeTile.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/Businessprocess/Renderer/TileRenderer/NodeTile.php b/library/Businessprocess/Renderer/TileRenderer/NodeTile.php index 83f33fb..d323bf4 100644 --- a/library/Businessprocess/Renderer/TileRenderer/NodeTile.php +++ b/library/Businessprocess/Renderer/TileRenderer/NodeTile.php @@ -11,7 +11,7 @@ use Icinga\Module\Businessprocess\Html\Link; use Icinga\Module\Businessprocess\ImportedNode; use Icinga\Module\Businessprocess\MonitoredNode; use Icinga\Module\Businessprocess\Node; -use Icinga\Module\Businessprocess\Renderer\TileRenderer; +use Icinga\Module\Businessprocess\Renderer\Renderer; use Icinga\Module\Businessprocess\ServiceNode; class NodeTile extends BaseElement @@ -20,12 +20,12 @@ class NodeTile extends BaseElement /** * NodeTile constructor. - * @param TileRenderer $renderer + * @param Renderer $renderer * @param $name * @param Node $node * @param null $path */ - public function __construct(TileRenderer $renderer, $name, Node $node, $path = null) + public function __construct(Renderer $renderer, $name, Node $node, $path = null) { $attributes = $this->attributes(); $attributes->add('class', $renderer->getNodeClasses($node)); From d3eff54603b334e6670aa5700eb637bf892ccbd1 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 22:15:20 +0100 Subject: [PATCH 094/256] Controller: take over view tasks, move to parent --- application/controllers/NodeController.php | 3 +- application/controllers/ProcessController.php | 53 +++++++++++------- library/Businessprocess/Controller.php | 54 +++++++++++++++++++ 3 files changed, 89 insertions(+), 21 deletions(-) diff --git a/application/controllers/NodeController.php b/application/controllers/NodeController.php index 54d619d..c3d6a20 100644 --- a/application/controllers/NodeController.php +++ b/application/controllers/NodeController.php @@ -54,6 +54,7 @@ class NodeController extends Controller public function addAction() { + $this->defaultTab(); $bp = $this->loadBpConfig(); $url = Url::fromPath( @@ -64,7 +65,7 @@ class NodeController extends Controller $this->view->form = $this->loadForm('process') ->setProcess($bp) ->setSession($this->session()) - ->setRedirectUrl($url) + ->setSuccessUrl($url) ->handleRequest(); } diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index c03ada2..25071e5 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -4,8 +4,12 @@ namespace Icinga\Module\Businessprocess\Controllers; use Icinga\Module\Businessprocess\Controller; use Icinga\Module\Businessprocess\ConfigDiff; +use Icinga\Module\Businessprocess\Html\Element; +use Icinga\Module\Businessprocess\Html\HtmlString; +use Icinga\Module\Businessprocess\Html\Icon; use Icinga\Module\Businessprocess\Renderer\Breadcrumb; use Icinga\Module\Businessprocess\Renderer\TileRenderer; +use Icinga\Module\Businessprocess\Renderer\TreeRenderer; use Icinga\Module\Businessprocess\Simulation; use Icinga\Module\Businessprocess\Html\Link; use Icinga\Module\Businessprocess\Web\Url; @@ -14,18 +18,6 @@ use Icinga\Web\Widget\Tabextension\DashboardAction; class ProcessController extends Controller { - protected function currentProcessParams() - { - $params = array(); - foreach (array('config', 'node') as $name) { - if ($value = $this->params->get($name)) { - $params[$name] = $value; - } - } - - return $params; - } - /** * Create a new business process configuration */ @@ -56,7 +48,7 @@ class ProcessController extends Controller } /** - * Show a business process tree + * Show a business process */ public function showAction() { @@ -73,7 +65,6 @@ class ProcessController extends Controller $bp = $this->loadBpConfig(); } - // Do not lock empty configs if ($bp->isEmpty() && ! $this->view->compact && $bp->isLocked()) { $this->redirectNow($this->url()->with('unlocked', true)); @@ -98,8 +89,8 @@ class ProcessController extends Controller $this->simulationForm(); } - - $this->setTitle('Business Process "%s"', $bp->getTitle()); + $title = sprintf('Business Process "%s"', $bp->getTitle()); + $this->setTitle($title); $this->tabsForShow()->activate('show'); if ($bp->isLocked()) { @@ -121,12 +112,34 @@ class ProcessController extends Controller $bp->applySimulation($simulation); } - // TODO: ... - $renderer = new TileRenderer($this->view, $bp, $bpNode); + if ($mode === 'tile') { + $renderer = new TileRenderer($bp, $bpNode); + } else { + $renderer = new TreeRenderer($bp, $bpNode); + } $renderer->setBaseUrl($this->url()) ->setPath($this->params->getValues('path')); - $this->view->bpRenderer = $renderer; - $this->view->breadcrumb = Breadcrumb::create($renderer); + $this->content()->add($renderer); + $controls = $this->controls(); + + if ($this->showFullscreen) { + $controls->attributes()->add('class', 'want-fullscreen'); + $controls->add( + Link::create( + Icon::create('resize-small'), + $this->url()->without('showFullscreen')->without('view'), + null, + array('style' => 'float: right') + ) + ); + } else { + $controls->add(HtmlString::create($this->getTabs())); + } + $controls->add(Element::create('h1')->setContent($title)); + $controls->add(Breadcrumb::create($renderer)); + if (! $this->showFullscreen) { + $controls->add($this->actions()); + } if (! $bp->isLocked()) { $renderer->unlock(); diff --git a/library/Businessprocess/Controller.php b/library/Businessprocess/Controller.php index 1161806..da4b3ed 100644 --- a/library/Businessprocess/Controller.php +++ b/library/Businessprocess/Controller.php @@ -3,10 +3,13 @@ namespace Icinga\Module\Businessprocess; use Icinga\Application\Icinga; +use Icinga\Module\Businessprocess\Html\HtmlString; use Icinga\Module\Businessprocess\Modification\ProcessChanges; use Icinga\Module\Businessprocess\Storage\LegacyStorage; use Icinga\Module\Businessprocess\Storage\Storage; use Icinga\Module\Businessprocess\Web\Component\ActionBar; +use Icinga\Module\Businessprocess\Web\Component\Controls; +use Icinga\Module\Businessprocess\Web\Component\Content; use Icinga\Module\Businessprocess\Web\Form\FormLoader; use Icinga\Module\Businessprocess\Web\Url; use Icinga\Web\Controller as ModuleController; @@ -64,6 +67,18 @@ class Controller extends ModuleController return $this->url; } + protected function currentProcessParams() + { + $params = array(); + foreach (array('config', 'node') as $name) { + if ($value = $this->params->get($name)) { + $params[$name] = $value; + } + } + + return $params; + } + /** * @return ActionBar */ @@ -76,6 +91,43 @@ class Controller extends ModuleController return $this->view->actions; } + protected function controls() + { + if ($this->view->controls === null) { + $this->view->controls = Controls::create(); + } + + return $this->view->controls; + } + + protected function content() + { + if ($this->view->content === null) { + $this->view->content = Content::create(); + } + + return $this->view->content; + } + + protected function singleTab($label) + { + $tabs = Widget::create('tabs')->add( + 'tab', + array( + 'label' => $label, + 'url' => $this->getRequest()->getUrl() + ) + )->activate('tab'); + $this->controls()->add(HtmlString::create($tabs)); + + return $tabs; + } + + protected function defaultTab() + { + return $this->singleTab($this->translate('Business Process')); + } + protected function tabs() { if ($this->view->tabs === null) { @@ -93,6 +145,7 @@ class Controller extends ModuleController { $this->_helper->viewRenderer->setNoController(true); $this->_helper->viewRenderer->setScriptAction($name); + return $this; } protected function setTitle($title) @@ -100,6 +153,7 @@ class Controller extends ModuleController $args = func_get_args(); array_shift($args); $this->view->title = vsprintf($title, $args); + return $this; } protected function loadModifiedBpConfig() From cd13afd560c0b0ccff363a1f3fe1ebd0c4b1b70d Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 22:16:33 +0100 Subject: [PATCH 095/256] node/add: simplify view script --- application/views/scripts/node/add.phtml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/application/views/scripts/node/add.phtml b/application/views/scripts/node/add.phtml index a765433..ddc111d 100644 --- a/application/views/scripts/node/add.phtml +++ b/application/views/scripts/node/add.phtml @@ -1,10 +1,6 @@ -controls->render() ?> -use Icinga\Module\Businessprocess\HostNode; -use Icinga\Module\Businessprocess\ServiceNode; -use Icinga\Module\Businessprocess\BpNode; - -?>
+

translate('Add new process node') ?>

form ?>
From 3f9d0d781dd8f0d961487de87da95d1ae49b7aaa Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 22:18:43 +0100 Subject: [PATCH 096/256] process/show: reduce to a minimun, remove... ...bprenderer.phtml --- .../views/scripts/process/bprenderer.phtml | 13 --- application/views/scripts/process/show.phtml | 80 +------------------ 2 files changed, 2 insertions(+), 91 deletions(-) delete mode 100644 application/views/scripts/process/bprenderer.phtml diff --git a/application/views/scripts/process/bprenderer.phtml b/application/views/scripts/process/bprenderer.phtml deleted file mode 100644 index ef66bf5..0000000 --- a/application/views/scripts/process/bprenderer.phtml +++ /dev/null @@ -1,13 +0,0 @@ -compact && ! $this->showFullscreen): ?> -
-tabs ?> -

title ?>

- breadcrumb->render() ?> - actions->render() ?> -
- - -
- bpRenderer->render() ?> - render('warnings.phtml') ?> -
diff --git a/application/views/scripts/process/show.phtml b/application/views/scripts/process/show.phtml index e74bc4a..3e2cc59 100644 --- a/application/views/scripts/process/show.phtml +++ b/application/views/scripts/process/show.phtml @@ -1,78 +1,2 @@ - -compact): ?> -
-tabs ?> -

escape($this->title) ?>

- - breadcrumb->render() ?> -actions ?> -
- - -
-
form ?>
-bpconfig->isLocked()): ?> -qlink('Add new node', 'businessprocess/node/add', array('config' => $this->configName)) ?> - -bpconfig->hasErrors() || $this->bpconfig->hasChanges() || $this->bpconfig->hasSimulations()): ?> -
    -bpconfig->getErrors() as $error): ?> -
  • escape($error) ?>
  • - -bpconfig->hasChanges()): ?> -
  • translate('This process has %d pending change(s).'), - $this->bpconfig->countChanges() -) ?> qlink( - $this->translate('Store'), - 'businessprocess/process/config', - array('config' => $this->configName) -) ?> qlink( - $this->translate('Dismiss'), - $this->url()->with('dismissChanges', true), - null -) ?>
  • - -bpconfig->hasSimulations()): ?> -
  • translate('This process shows %d simulated state(s).'), - $this->bpconfig->countSimulations() -) ?> qlink( - $this->translate('Dismiss'), - $this->url()->with('dismissSimulations', true), - null -) ?>
  • - -
- -
-bp->renderHtml($this); -} catch (Exception $e) { - printf( - '
  • %s: %s
', - $this->translate('Error'), - $this->escape($e->getMessage()) - ); -} - - -?> -bpconfig->isLocked()): ?> -bp instanceof BusinessProcess): /* do not render when showing subtree */ ?> -bp->renderUnbound($this) ?> - - -
-render('warnings.phtml') ?> -
+controls->render() ?> +content->render() ?> From 162c19c023fd096323115aa216526fadbe5eca2f Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 22:20:01 +0100 Subject: [PATCH 097/256] Content, Controls: add new convenience classes --- library/Businessprocess/Web/Component/Content.php | 12 ++++++++++++ library/Businessprocess/Web/Component/Controls.php | 12 ++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 library/Businessprocess/Web/Component/Content.php create mode 100644 library/Businessprocess/Web/Component/Controls.php diff --git a/library/Businessprocess/Web/Component/Content.php b/library/Businessprocess/Web/Component/Content.php new file mode 100644 index 0000000..f8336ce --- /dev/null +++ b/library/Businessprocess/Web/Component/Content.php @@ -0,0 +1,12 @@ + 'content'); +} diff --git a/library/Businessprocess/Web/Component/Controls.php b/library/Businessprocess/Web/Component/Controls.php new file mode 100644 index 0000000..861a852 --- /dev/null +++ b/library/Businessprocess/Web/Component/Controls.php @@ -0,0 +1,12 @@ + 'controls'); +} From 78f02ad40370b9eaf7aae9af60c7d83cdf81c220 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 23:50:13 +0100 Subject: [PATCH 098/256] Html: move translate one level deeper --- library/Businessprocess/Html/BaseElement.php | 6 ------ library/Businessprocess/Html/Html.php | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/library/Businessprocess/Html/BaseElement.php b/library/Businessprocess/Html/BaseElement.php index 7043b24..15fd81e 100644 --- a/library/Businessprocess/Html/BaseElement.php +++ b/library/Businessprocess/Html/BaseElement.php @@ -89,12 +89,6 @@ abstract class BaseElement extends Html ); } - protected function translate($msg) - { - // TODO: Not so nice - return mt('businessprocess', $msg); - } - /** * Whether the given something can be rendered * diff --git a/library/Businessprocess/Html/Html.php b/library/Businessprocess/Html/Html.php index eafd2e7..70c5def 100644 --- a/library/Businessprocess/Html/Html.php +++ b/library/Businessprocess/Html/Html.php @@ -101,6 +101,12 @@ class Html implements Renderable return implode($this->contentSeparator, $html); } + protected function translate($msg) + { + // TODO: Not so nice + return mt('businessprocess', $msg); + } + public static function element($name, $attributes = null) { // TODO: This might be anything here, add a better check From c56c66d19387ebf0085a4a51c5510fe0b34400d9 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 28 Nov 2016 23:50:29 +0100 Subject: [PATCH 099/256] BaseElement: add createElement helper --- library/Businessprocess/Html/BaseElement.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/library/Businessprocess/Html/BaseElement.php b/library/Businessprocess/Html/BaseElement.php index 15fd81e..b00c42a 100644 --- a/library/Businessprocess/Html/BaseElement.php +++ b/library/Businessprocess/Html/BaseElement.php @@ -68,6 +68,21 @@ abstract class BaseElement extends Html return $this->tag; } + /** + * Container constructor. + * + * @param string $tag + * @param Attributes|array $attributes + * + * @return Element + */ + public function createElement($tag, $attributes = null) + { + $element = Element::create($tag, $attributes); + $this->add($element); + return $element; + } + public function renderContent() { return parent::render(); From 081073823592cb5054f405a568ae61162df15e93 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 00:21:19 +0100 Subject: [PATCH 100/256] css: remove obsolete normalization --- public/css/module.less | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/public/css/module.less b/public/css/module.less index 9dc16f0..589246d 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -1,33 +1,4 @@ -/* Normalize CSS */ - -.content { - a { - font-weight: normal; - color: inherit; - } - - .text-small { - color: inherit; - font-size: inherit; - } - - p a { - color: @icinga-blue; - text-decoration: underline; - } - -} -.controls h1 a { - color: inherit; - float: right; - font-weight: normal; -} - -.controls h1 form { - display: inline; -} - .action-bar a { color: @icinga-blue; &:hover::before { @@ -36,10 +7,6 @@ margin-right: 1em; } -h1 a:focus { - outline: none; -} - div.bp { margin-bottom: 4px; } From 60b66e89b03faa4bdea750a4e56a54b74c4e5c62 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 00:34:28 +0100 Subject: [PATCH 101/256] TreeRenderer: new renderer, LOTS of cleanup --- library/Businessprocess/BpNode.php | 17 -- library/Businessprocess/BusinessProcess.php | 51 ----- library/Businessprocess/HostNode.php | 31 --- library/Businessprocess/ImportedNode.php | 152 ++++++++----- library/Businessprocess/MonitoredNode.php | 16 -- library/Businessprocess/Node.php | 160 ++----------- library/Businessprocess/Renderer/Renderer.php | 48 +++- .../Businessprocess/Renderer/TileRenderer.php | 6 + .../Businessprocess/Renderer/TreeRenderer.php | 211 ++++++++++++++++++ library/Businessprocess/ServiceNode.php | 32 --- 10 files changed, 364 insertions(+), 360 deletions(-) create mode 100644 library/Businessprocess/Renderer/TreeRenderer.php diff --git a/library/Businessprocess/BpNode.php b/library/Businessprocess/BpNode.php index f57e2f5..7682ae0 100644 --- a/library/Businessprocess/BpNode.php +++ b/library/Businessprocess/BpNode.php @@ -430,23 +430,6 @@ class BpNode extends Node } } - protected function getActionIcons($view) - { - $icons = array(); - if (! $this->bp->isLocked() && $this->name !== '__unbound__') { - $icons[] = $this->actionIcon( - $view, - 'wrench', - $view->url('businessprocess/node/edit', array( - 'config' => $this->bp->getName(), - 'node' => $this->name - )), - mt('businessprocess', 'Modify this node') - ); - } - return $icons; - } - public function toLegacyConfigString(& $rendered = array()) { $cfg = ''; diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index 526c135..a3176ea 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -99,13 +99,6 @@ class BusinessProcess */ protected $simulation; - /** - * Whether we are in edit mode - * - * @var boolean - */ - protected $editMode = false; - protected $locked = true; protected $changeCount = 0; @@ -261,17 +254,6 @@ class BusinessProcess return $this->simulationCount; } - public function setEditMode($mode = true) - { - $this->editMode = (bool) $mode; - return $this; - } - - public function isEditMode() - { - return $this->editMode; - } - public function clearAppliedChanges() { if ($this->appliedChanges !== null) { @@ -735,37 +717,4 @@ class BusinessProcess { return $this->countChildren() === 0; } - - public function renderHtml($view) - { - $html = ''; - foreach ($this->getRootNodes() as $name => $node) { - $html .= $node->renderHtml($view); - } - return $html; - } - - public function renderUnbound($view) - { - $html = ''; - - $unbound = $this->getUnboundNodes(); - if (empty($unbound)) { - return $html; - } - - $parent = new BpNode($this, (object) array( - 'name' => '__unbound__', - 'operator' => '|', - 'child_names' => array_keys($unbound) - )); - $parent->getState(); - $parent->setMissing() - ->setDowntime(false) - ->setAck(false) - ->setAlias('Unbound nodes'); - - $html .= $parent->renderHtml($view); - return $html; - } } diff --git a/library/Businessprocess/HostNode.php b/library/Businessprocess/HostNode.php index 3f9f540..d28ec2e 100644 --- a/library/Businessprocess/HostNode.php +++ b/library/Businessprocess/HostNode.php @@ -49,28 +49,6 @@ class HostNode extends MonitoredNode return $this->getHostname(); } - protected function getActionIcons($view) - { - $icons = array(); - - if (! $this->bp->isLocked()) { - - $url = Url::fromPath( 'businessprocess/node/simulate', array( - 'config' => $this->bp->getName(), - 'node' => $this->name - )); - - $icons[] = $this->actionIcon( - $view, - 'magic', - $url, - 'Simulation' - ); - } - - return $icons; - } - public function getHostname() { return $this->hostname; @@ -93,13 +71,4 @@ class HostNode extends MonitoredNode { return Link::create($this->hostname, $this->getUrl()); } - - public function renderLink($view) - { - if ($this->isMissing()) { - return '' . $view->escape($this->hostname) . ''; - } - - return $this->getLink()->render(); - } } diff --git a/library/Businessprocess/ImportedNode.php b/library/Businessprocess/ImportedNode.php index f829b92..c099d43 100644 --- a/library/Businessprocess/ImportedNode.php +++ b/library/Businessprocess/ImportedNode.php @@ -3,20 +3,23 @@ namespace Icinga\Module\Businessprocess; use Icinga\Application\Config; -use Icinga\Web\Url; +use Icinga\Module\Businessprocess\Html\Link; use Icinga\Module\Businessprocess\Storage\LegacyStorage; use Exception; class ImportedNode extends Node { + /** @var string */ protected $configName; - protected $importedBp; - - protected $importedNode; + /** @var BpNode */ + private $node; protected $className = 'subtree'; + /** + * @inheritdoc + */ public function __construct(BusinessProcess $bp, $object) { $this->name = $object->name; @@ -29,11 +32,17 @@ class ImportedNode extends Node } } + /** + * @return string + */ public function getConfigName() { return $this->configName; } + /** + * @inheritdoc + */ public function getState() { if ($this->state === null) { @@ -42,17 +51,26 @@ class ImportedNode extends Node return $this->state; } + /** + * @inheritdoc + */ public function getAlias() { return $this->importedNode()->getAlias(); } + /** + * @inheritdoc + */ public function isMissing() { - return $this->importedNode()->isMissing(); // TODO: WHY? return $this->getState() === null; + return $this->importedNode()->isMissing(); } + /** + * @inheritdoc + */ public function isInDowntime() { if ($this->downtime === null) { @@ -61,6 +79,9 @@ class ImportedNode extends Node return $this->downtime; } + /** + * @inheritdoc + */ public function isAcknowledged() { if ($this->ack === null) { @@ -69,68 +90,83 @@ class ImportedNode extends Node return $this->ack; } + /** + * @return BpNode + */ protected function importedNode() { - if ($this->importedNode === null) { - $storage = new LegacyStorage( - Config::module('businessprocess')->getSection('global') - ); - try { - $this->importedBp = $storage->loadProcess($this->configName); - if ($this->bp->usesSoftStates()) { - $this->importedBp->useSoftStates(); - } else { - $this->importedBp->useHardStates(); - } - $this->importedBp->retrieveStatesFromBackend(); - $this->importedNode = $this->importedBp->getNode($this->name); - } catch (Exception $e) { + if ($this->node === null) { + $this->node = $this->loadImportedNode(); + } + return $this->node; + } - $node = new BpNode($this->bp, (object) array( - 'name' => $this->name, - 'operator' => '&', - 'child_names' => array() - )); - $node->setState(2); - $node->setMissing(false) - ->setDowntime(false) - ->setAck(false) - ->setAlias($e->getMessage()); - - $this->importedNode = $node; + /** + * @return BpNode + */ + protected function loadImportedNode() + { + try { + $import = $this->storage()->loadProcess($this->configName); + if ($this->bp->usesSoftStates()) { + $import->useSoftStates(); + } else { + $import->useHardStates(); } + + $import->retrieveStatesFromBackend(); + + return $import->getNode($this->name); + } catch (Exception $e) { + + return $this->createFailedNode($e); } - return $this->importedNode; - } - - protected function getActionIcons($view) - { - $icons = array(); - - if (! $this->bp->isLocked()) { - - $url = Url::fromPath( 'businessprocess/node/simulate', array( - 'config' => $this->bp->getName(), - 'node' => $this->name - )); - - $icons[] = $this->actionIcon( - $view, - 'magic', - $url, - 'Simulation' - ); - } - - return $icons; } - public function renderLink($view) + /** + * @return LegacyStorage + */ + protected function storage() { - return $view->qlink($this->getAlias(), 'businessprocess/process/show', array( - 'config' => $this->configName, - 'process' => $this->name + return new LegacyStorage( + Config::module('businessprocess')->getSection('global') + ); + } + + /** + * @param Exception $e + * + * @return BpNode + */ + protected function createFailedNode(Exception $e) + { + $node = new BpNode($this->bp, (object) array( + 'name' => $this->name, + 'operator' => '&', + 'child_names' => array() )); + $node->setState(2); + $node->setMissing(false) + ->setDowntime(false) + ->setAck(false) + ->setAlias($e->getMessage()); + + return $node; + } + + /** + * @inheritdoc + */ + public function getLink() + { + return Link::create( + $this->getAlias(), + 'businessprocess/process/show', + array( + 'config' => $this->configName, + 'process' => $this->name + ) + ); } } diff --git a/library/Businessprocess/MonitoredNode.php b/library/Businessprocess/MonitoredNode.php index aa5e773..68e4866 100644 --- a/library/Businessprocess/MonitoredNode.php +++ b/library/Businessprocess/MonitoredNode.php @@ -2,7 +2,6 @@ namespace Icinga\Module\Businessprocess; -use Icinga\Module\Businessprocess\Html\Container; use Icinga\Module\Businessprocess\Html\Link; abstract class MonitoredNode extends Node @@ -17,19 +16,4 @@ abstract class MonitoredNode extends Node return Link::create($this->getAlias(), $this->getUrl()); } } - - protected function prepareActions(Container $actions) - { - $actions->add( - $url = Link::create( - $actions->view()->translate('Simulate a specific state'), - 'businessprocess/process/show?addSimulation&unlocked', - array( - 'config' => $this->bp->getName(), - 'simulationNode' => $this->name - ), - array('class' => 'icon-magic') - ) - ); - } } diff --git a/library/Businessprocess/Node.php b/library/Businessprocess/Node.php index 693b6e8..7e2e2b5 100644 --- a/library/Businessprocess/Node.php +++ b/library/Businessprocess/Node.php @@ -2,10 +2,8 @@ namespace Icinga\Module\Businessprocess; -use Icinga\Web\Url; use Icinga\Exception\ProgrammingError; -use Icinga\Data\Filter\Filter; -use Exception; +use Icinga\Module\Businessprocess\Html\Link; abstract class Node { @@ -253,16 +251,6 @@ abstract class Node return $this->ack; } - public function isSimulationMode() - { - return $this->bp->isSimulationMode(); - } - - public function isEditMode() - { - return $this->bp->isEditMode(); - } - public function getChildren($filter = null) { return array(); @@ -323,142 +311,17 @@ abstract class Node throw new ProgrammingError('Got invalid sorting state %s', $sortState); } - protected function renderHtmlForChildren($view) - { - $html = ''; - if ($this->hasChildren()) { - foreach ($this->getChildren() as $name => $child) { - $html .= '' - . $child->renderHtml($view) - . ''; - } - } - - return $html; - } - - protected function getId($prefix = '') - { - return md5($prefix . (string) $this); - } - - protected function getObjectClassName() + public function getObjectClassName() { return $this->className; } - protected function getStateClassNames() + /** + * @return Link + */ + public function getLink() { - $state = strtolower($this->getStateName()); - - if ($this->isMissing()) { - return array('missing'); - } elseif ($state === 'ok') { - if ($this->hasMissingChildren()) { - return array('ok', 'missing-children'); - } else { - return array('ok'); - } - } else { - return array('problem', $state); - } - } - - public function renderHtml($view, $prefix = '') - { - $id = $this->getId($prefix); - $handled = $this->isAcknowledged() || $this->isInDowntime(); - - $html = sprintf( - '', - implode(' ', $this->getStateClassNames()), - $handled ? ' handled' : '', - ($this->hasChildren() ? ' operator ' : ' node '), - $this->getObjectClassName(), - $id - ); - - if ($this->hasChildren()) { - $html .= sprintf( - '%s', - sprintf(' rowspan="%d"', $this->countChildren() + 1), - $this->operatorHtml() - ); - } - - - $title = preg_replace( - '~()~', - implode('', $this->getIcons($view)) . '$1', - $this->renderLink($view) - ); - - $title = preg_replace('##', ' ' . $view->timeSince($this->getLastStateChange()) . '', $title); - $icons = array(); - - foreach ($this->getActionIcons($view) as $icon) { - $icons[] = $icon; - } - - if ($this->hasInfoUrl()) { - $url = $this->getInfoUrl(); - $icons[] = $this->actionIcon( - $view, - 'help', - $url, - sprintf('%s: %s', mt('businessprocess', 'More information'), $url) - ); - } - $title = implode("\n", $icons) . $title; - - $html .= sprintf( - '', - $title - ); - foreach ($this->getChildren() as $name => $child) { - $html .= ''; - } - $html .= "
%s
' . $child->renderHtml($view, $id . '-') . '
\n"; - return $html; - } - - protected function getActionIcons($view) - { - return array(); - } - - protected function actionIcon($view, $icon, $url, $title) - { - if ($url instanceof Url || ! preg_match('~^https?://~', $url)) { - $target = ''; - } else { - $target = ' target="_blank"'; - } - - return sprintf( - ' %s', - $url, - $target, - $view->escape($title), - $view->icon($icon) - ); - } - - public function renderLink($view) - { - return '' . ($this->hasAlias() ? $this->getAlias() : $this->name) . ''; - } - - public function getIcons($view) - { - $icons = array(); - if ($this->isInDowntime()) { - $icons[] = $view->icon('moon'); - } - if ($this->isAcknowledged()) { - $icons[] = $view->icon('ok'); - } - return $icons; + return Link::create($this->getAlias(), '#'); } public function operatorHtml() @@ -473,13 +336,18 @@ abstract class Node return ''; } - public function __toString() + public function getName() { return $this->name; } + public function __toString() + { + return $this->getName(); + } + public function __destruct() { - $this->parents = array(); + unset($this->parents); } } diff --git a/library/Businessprocess/Renderer/Renderer.php b/library/Businessprocess/Renderer/Renderer.php index 9a6ad32..ba3088a 100644 --- a/library/Businessprocess/Renderer/Renderer.php +++ b/library/Businessprocess/Renderer/Renderer.php @@ -2,21 +2,21 @@ namespace Icinga\Module\Businessprocess\Renderer; +use Icinga\Date\DateFormatter; use Icinga\Exception\ProgrammingError; use Icinga\Module\Businessprocess\BpNode; use Icinga\Module\Businessprocess\BusinessProcess; use Icinga\Module\Businessprocess\Html\Container; use Icinga\Module\Businessprocess\Html\Element; use Icinga\Module\Businessprocess\Html\Html; +use Icinga\Module\Businessprocess\Html\HtmlString; +use Icinga\Module\Businessprocess\Html\Text; use Icinga\Module\Businessprocess\Node; use Icinga\Module\Businessprocess\Web\Url; -use Icinga\Web\View; +use Icinga\Web\Request; abstract class Renderer extends Html { - /** @var View */ - protected $view; - /** @var BusinessProcess */ protected $bp; @@ -35,15 +35,13 @@ abstract class Renderer extends Html /** * Renderer constructor. * - * @param View $view * @param BusinessProcess $bp * @param BpNode|null $parent */ - public function __construct(View $view, BusinessProcess $bp, BpNode $parent = null) + public function __construct(BusinessProcess $bp, BpNode $parent = null) { $this->bp = $bp; $this->parent = $parent; - $this->view = $view; } /** @@ -117,7 +115,6 @@ abstract class Renderer extends Html ) )->setContent($cnt) ); - } return $container; @@ -213,6 +210,40 @@ abstract class Renderer extends Html $this->locked = false; return $this; } + + public function timeSince($time, $timeOnly = false) + { + if (! $time) { + return HtmlString::create(''); + } + + return Element::create( + 'span', + array( + 'class' => array('relative-time', 'time-since'), + 'title' => DateFormatter::formatDateTime($time), + ) + )->setContent(DateFormatter::timeSince($time, $timeOnly)); + } + + protected function createUnboundParent(BusinessProcess $bp) + { + $unbound = $bp->getUnboundNodes(); + + $parent = new BpNode($bp, (object) array( + 'name' => '__unbound__', + 'operator' => '|', + 'child_names' => array_keys($unbound) + )); + $parent->getState(); + $parent->setMissing() + ->setDowntime(false) + ->setAck(false) + ->setAlias('Unbound nodes'); + + return $parent; + } + /** * Just to be on the safe side */ @@ -220,6 +251,5 @@ abstract class Renderer extends Html { unset($this->parent); unset($this->bp); - unset($this->view); } } diff --git a/library/Businessprocess/Renderer/TileRenderer.php b/library/Businessprocess/Renderer/TileRenderer.php index 8a3714c..afaf858 100644 --- a/library/Businessprocess/Renderer/TileRenderer.php +++ b/library/Businessprocess/Renderer/TileRenderer.php @@ -39,6 +39,12 @@ class TileRenderer extends Renderer $this->add(new NodeTile($this, $name, $node, $path)); } + $unbound = $this->createUnboundParent($bp); + if ($unbound->hasChildren()) { + $name = $unbound->getAlias(); + $this->add($this->add(new NodeTile($this, $name, $unbound))); + } + $nodesDiv->addContent($this->getContent()); $this->setContent($nodesDiv); diff --git a/library/Businessprocess/Renderer/TreeRenderer.php b/library/Businessprocess/Renderer/TreeRenderer.php new file mode 100644 index 0000000..ce2c297 --- /dev/null +++ b/library/Businessprocess/Renderer/TreeRenderer.php @@ -0,0 +1,211 @@ +bp; + $this->add(Container::create( + array( + 'id' => $bp->getHtmlId(), + 'class' => 'bp' + ), + HtmlString::create($this->renderBp($bp)) + )); + + return parent::render(); + } + + /** + * @param BusinessProcess $bp + * @return string + */ + public function renderBp(BusinessProcess $bp) + { + $html = ''; + foreach ($bp->getRootNodes() as $name => $node) { + $html .= $this->renderNode($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()); + + if ($node->isMissing()) { + return array('missing'); + } elseif ($state === 'ok') { + if ($node->hasMissingChildren()) { + return array('ok', 'missing-children'); + } else { + return array('ok'); + } + } else { + return array('problem', $state); + } + } + + /** + * @param Node $node + * @return Icon[] + */ + public function getNodeIcons(Node $node) + { + $icons = array(); + if ($node->isInDowntime()) { + $icons[] = Icon::create('moon'); + } + if ($node->isAcknowledged()) { + $icons[] = Icon::create('ok'); + } + return $icons; + } + + /** + * @param Node $node + * @return string + */ + public function renderNode(BusinessProcess $bp, Node $node, $path = array()) + { + $table = Element::create( + 'table', + array( + 'id' => $this->getId($node, $path), + 'class' => array( + 'bp', + $node->getObjectClassName() + ) + ) + ); + $attributes = $table->attributes(); + $attributes->add('class', $this->getStateClassNames($node)); + if ($node->isHandled()) { + $attributes->add('class', 'handled'); + } + if ($node->hasChildren()) { + $attributes->add('class', 'operator'); + } else { + $attributes->add('class', 'node'); + } + + $tbody = $table->createElement('tbody'); + $tr = $tbody->createElement('tr'); + + if ($node->hasChildren()) { + $tr->createElement( + 'th', + array( + 'rowspan' => $node->countChildren() + 1 + ) + )->createElement( + 'span', + array('class' => 'op') + )->setContent($node->operatorHtml()); + } + $td = $tr->createElement('td'); + $td->addContent($this->getActionIcons($bp, $node)); + + if ($node->hasInfoUrl()) { + $td->add($this->createInfoAction($node)); + } + + if (! $this->bp->isLocked()) { + $td->addContent($this->getActionIcons($bp, $node)); + } + + $link = $node->getLink(); + $link->addContent($this->getNodeIcons($node)); + $link->addContent($this->timeSince($node->getLastStateChange())); + $td->addContent($link); + + foreach ($node->getChildren() as $name => $child) { + $tbody->createElement('tr')->createElement('td')->setContent( + $this->renderNode($bp, $child, $this->getCurrentPath()) + ); + } + + return $table; + } + + protected function getActionIcons(BusinessProcess $bp, Node $node) + { + if ($node instanceof BpNode) { + return $this->createEditAction($bp, $node); + } else { + return $this->createSimulationAction($bp, $node); + } + } + + protected function createEditAction(BusinessProcess $bp, BpNode $node) + { + return $this->actionIcon( + 'wrench', + Url::fromPath('businessprocess/node/edit', array( + 'config' => $bp->getName(), + 'node' => $node->getName() + )), + $this->translate('Modify this node') + ); + } + + protected function createSimulationAction(BusinessProcess $bp, Node $node) + { + return $this->actionIcon( + 'magic', + Url::fromPath('businessprocess/process/show?addSimulation&unlocked', array( + 'config' => $bp->getName(), + 'node' => $node->getName() + )), + $this->translate('Simulate a specific state') + ); + } + + protected function createInfoAction(BpNode $node) + { + $url = $node->getInfoUrl(); + return $this->actionIcon( + 'help', + $url, + sprintf('%s: %s', $this->translate('More information'), $url) + ); + } + protected function actionIcon($icon, $url, $title) + { + return Link::create( + Icon::create($icon), + $url, + null, + array( + 'title' => $title, + 'style' => 'float: right', + ) + ); + } +} diff --git a/library/Businessprocess/ServiceNode.php b/library/Businessprocess/ServiceNode.php index 52eea76..2a582e7 100644 --- a/library/Businessprocess/ServiceNode.php +++ b/library/Businessprocess/ServiceNode.php @@ -2,7 +2,6 @@ namespace Icinga\Module\Businessprocess; -use Icinga\Module\Businessprocess\Html\Link; use Icinga\Module\Businessprocess\Web\Url; class ServiceNode extends MonitoredNode @@ -26,28 +25,6 @@ class ServiceNode extends MonitoredNode } } - protected function getActionIcons($view) - { - $icons = array(); - - if (! $this->bp->isLocked()) { - - $url = Url::fromPath('businessprocess/node/simulate', array( - 'config' => $this->bp->getName(), - 'node' => $this->name - )); - - $icons[] = $this->actionIcon( - $view, - 'magic', - $url, - 'Simulation' - ); - } - - return $icons; - } - public function getHostname() { return $this->hostname; @@ -76,13 +53,4 @@ class ServiceNode extends MonitoredNode return Url::fromPath('monitoring/service/show', $params); } - - public function renderLink($view) - { - if ($this->isMissing()) { - return '' . $view->escape($this->getAlias()) . ''; - } - - return $this->getLink()->render(); - } } From b256488cba2857d35a776d2e31f21b2ba5101a1d Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 00:36:05 +0100 Subject: [PATCH 102/256] Host/ServiceNodeTest: adjust tests... ...legacy methods have been removed --- test/php/library/Businessprocess/HostNodeTest.php | 2 +- test/php/library/Businessprocess/ServiceNodeTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/php/library/Businessprocess/HostNodeTest.php b/test/php/library/Businessprocess/HostNodeTest.php index dd1c01a..58f6a22 100644 --- a/test/php/library/Businessprocess/HostNodeTest.php +++ b/test/php/library/Businessprocess/HostNodeTest.php @@ -38,7 +38,7 @@ class HostNodeTest extends BaseTestCase $this->assertEquals( '' . 'localhost', - $this->localhost()->renderLink(new View()) + $this->localhost()->getLink()->render() ); } diff --git a/test/php/library/Businessprocess/ServiceNodeTest.php b/test/php/library/Businessprocess/ServiceNodeTest.php index a9a4d4d..30e730c 100644 --- a/test/php/library/Businessprocess/ServiceNodeTest.php +++ b/test/php/library/Businessprocess/ServiceNodeTest.php @@ -38,7 +38,7 @@ class ServiceNodeTest extends BaseTestCase $this->assertEquals( '' . 'localhost: ping <> pong', - $this->pingOnLocalhost()->renderLink(new View()) + $this->pingOnLocalhost()->getLink()->render() ); } From 5156c3fb934d1591715c5dd0c6bba8db0615b1cf Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 00:42:27 +0100 Subject: [PATCH 103/256] css: remove duplicate badges, hide transitions --- public/css/module.less | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/public/css/module.less b/public/css/module.less index 589246d..01c0b9d 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -289,7 +289,7 @@ table.bp table.bp table.bp table.bp table.bp { font-size: 1em; } /* Transitions */ -table.bp { +div.knightrider table.bp { // That's ugly, I know .transition(@val1, @val2) { @@ -635,14 +635,3 @@ table.sourcecode { background-color: #dfd; } } - -.badges { - display: block; - padding: 0.5em; - - .badge { - border: 1px solid white; - margin: 0; - margin-right: 1px; - } -} From aba771f3aa9eba0c56d9bdf7d0b3eb2830cb5c8b Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 00:50:20 +0100 Subject: [PATCH 104/256] js, Controller: allow to toggle fullscreen mode --- application/controllers/ProcessController.php | 13 ++++++++ library/Businessprocess/Controller.php | 2 +- public/css/module.less | 4 +++ public/js/module.js | 33 +++++++++++++++++-- 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 25071e5..9f1ed46 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -242,6 +242,19 @@ class ProcessController extends Controller ) ) ); + + $this->actions()->add( + Link::create( + $this->translate('Fullscreen'), + $this->url()->with('showFullscreen', true), + null, + array( + 'class' => 'icon-resize-full-alt', + 'title' => $this->translate('Switch to fullscreen mode'), + 'data-base-target' => '_main', + ) + ) + ); } protected function simulationForm() diff --git a/library/Businessprocess/Controller.php b/library/Businessprocess/Controller.php index da4b3ed..44da2bb 100644 --- a/library/Businessprocess/Controller.php +++ b/library/Businessprocess/Controller.php @@ -32,7 +32,7 @@ class Controller extends ModuleController private $storage; /** @var bool */ - private $showFullscreen; + protected $showFullscreen; /** @var Url */ private $url; diff --git a/public/css/module.less b/public/css/module.less index 01c0b9d..784db3f 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -460,6 +460,10 @@ div.knightrider table.bp { .tiles.many { font-size: 0.9em; } } +#layout.fullscreen-layout .controls { + padding: 0 1em; +} + /** END of tiles **/ /** BEGIN breadcrumb **/ diff --git a/public/js/module.js b/public/js/module.js index 09372dd..0504318 100644 --- a/public/js/module.js +++ b/public/js/module.js @@ -22,7 +22,7 @@ * Tell Icinga about our event handlers */ this.module.on('beforerender', this.rememberOpenedBps); - this.module.on('rendered', this.fixOpenedBps); + 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); @@ -35,6 +35,12 @@ this.module.icinga.logger.debug('BP module loaded'); }, + onRendered: function (event) { + var $container = $(event.currentTarget); + this.fixFullscreen($container); + this.fixOpenedBps($container); + }, + processTitleClick: function (event) { event.stopPropagation(); var $el = $(event.currentTarget).closest('table.bp'); @@ -133,8 +139,29 @@ });*/ }, - fixOpenedBps: function(event) { - var $bpDiv = $(event.currentTarget).find('div.bp'); + fixFullscreen: function($container) { + var $controls = $container.find('div.controls'); + var $layout = $('#layout'); + var icinga = this.module.icinga; + if ($controls.hasClass('want-fullscreen')) { + if (!$layout.hasClass('fullscreen-layout')) { + + $layout.addClass('fullscreen-layout'); + $controls.removeAttr('style'); + $container.find('.fake-controls').remove(); + icinga.ui.currentLayout = 'fullscreen'; + } + } else { + if ($layout.hasClass('fullscreen-layout')) { + $layout.removeClass('fullscreen-layout'); + icinga.ui.layoutHasBeenChanged(); + icinga.ui.initializeControls($container); + } + } + }, + + fixOpenedBps: function($container) { + var $bpDiv = $container.find('div.bp'); var bpName = $bpDiv.attr('id'); if (typeof this.idCache[bpName] === 'undefined') { From 39fd28daad57e441d7e4c019d76396fc867a0818 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 00:56:12 +0100 Subject: [PATCH 105/256] ProcessController: remove last trace of old script --- application/controllers/ProcessController.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 9f1ed46..4956715 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -157,10 +157,6 @@ class ProcessController extends Controller // decide to use autorefreshInterval for HTML meta-refreshes also. $this->setAutorefreshInterval(1); } - - if ($mode === 'tile') { - $this->setViewScript('process/bprenderer'); - } } protected function prepareProcess() From 3a9cefce989369ce2be18abd5c8df89d189730c9 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 01:47:05 +0100 Subject: [PATCH 106/256] Html: __toString everywhere, add prependContent() --- library/Businessprocess/Html/BaseElement.php | 21 ------------- library/Businessprocess/Html/Html.php | 31 ++++++++++++++++++++ library/Businessprocess/Html/Text.php | 21 +++++++++++++ 3 files changed, 52 insertions(+), 21 deletions(-) diff --git a/library/Businessprocess/Html/BaseElement.php b/library/Businessprocess/Html/BaseElement.php index b00c42a..564fc22 100644 --- a/library/Businessprocess/Html/BaseElement.php +++ b/library/Businessprocess/Html/BaseElement.php @@ -114,25 +114,4 @@ abstract class BaseElement extends Html { return is_string($any) || is_int($any) || is_null($any); } - - /** - * @param Exception|string $error - * @return string - */ - protected function renderError($error) - { - return Util::renderError($error); - } - - /** - * @return string - */ - public function __toString() - { - try { - return $this->render(); - } catch (Exception $e) { - return $this->renderError($e); - } - } } diff --git a/library/Businessprocess/Html/Html.php b/library/Businessprocess/Html/Html.php index 70c5def..d4e46f6 100644 --- a/library/Businessprocess/Html/Html.php +++ b/library/Businessprocess/Html/Html.php @@ -51,6 +51,16 @@ class Html implements Renderable return $this; } + /** + * @param Renderable|array|string $content + * @return $this + */ + public function prependContent($content) + { + array_unshift($this->content, static::escape($content)); + return $this; + } + /** * return Html */ @@ -123,4 +133,25 @@ class Html implements Renderable return $element; } + + /** + * @param Exception|string $error + * @return string + */ + protected function renderError($error) + { + return Util::renderError($error); + } + + /** + * @return string + */ + public function __toString() + { + try { + return $this->render(); + } catch (Exception $e) { + return $this->renderError($e); + } + } } diff --git a/library/Businessprocess/Html/Text.php b/library/Businessprocess/Html/Text.php index 994ab47..0f36098 100644 --- a/library/Businessprocess/Html/Text.php +++ b/library/Businessprocess/Html/Text.php @@ -58,4 +58,25 @@ class Text implements Renderable return Util::escapeForHtml($this->string); } } + + /** + * @param Exception|string $error + * @return string + */ + protected function renderError($error) + { + return Util::renderError($error); + } + + /** + * @return string + */ + public function __toString() + { + try { + return $this->render(); + } catch (Exception $e) { + return $this->renderError($e); + } + } } From e821ccb7a009cc809e86c40f9815b92e9003d7ce Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 01:52:44 +0100 Subject: [PATCH 107/256] TreeRenderer: improve layout, add badges --- .../Businessprocess/Renderer/TreeRenderer.php | 28 ++++++++++++++++--- public/css/module.less | 16 +++++++---- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/library/Businessprocess/Renderer/TreeRenderer.php b/library/Businessprocess/Renderer/TreeRenderer.php index ce2c297..9ea0ccb 100644 --- a/library/Businessprocess/Renderer/TreeRenderer.php +++ b/library/Businessprocess/Renderer/TreeRenderer.php @@ -38,7 +38,13 @@ class TreeRenderer extends Renderer public function renderBp(BusinessProcess $bp) { $html = ''; - foreach ($bp->getRootNodes() as $name => $node) { + if ($this->wantsRootNodes()) { + $nodes = $bp->getChildren(); + } else { + $nodes = $this->parent->getChildren(); + } + + foreach ($nodes as $name => $node) { $html .= $this->renderNode($bp, $node); } @@ -89,12 +95,15 @@ class TreeRenderer extends Renderer } /** + * @param BusinessProcess $bp * @param Node $node + * @param array $path + * * @return string */ public function renderNode(BusinessProcess $bp, Node $node, $path = array()) { - $table = Element::create( + $table = Element::create( 'table', array( 'id' => $this->getId($node, $path), @@ -132,7 +141,7 @@ class TreeRenderer extends Renderer $td = $tr->createElement('td'); $td->addContent($this->getActionIcons($bp, $node)); - if ($node->hasInfoUrl()) { + if ($node instanceof BpNode && $node->hasInfoUrl()) { $td->add($this->createInfoAction($node)); } @@ -142,7 +151,18 @@ class TreeRenderer extends Renderer $link = $node->getLink(); $link->addContent($this->getNodeIcons($node)); - $link->addContent($this->timeSince($node->getLastStateChange())); + + if ($node->hasChildren()) { + $link->addContent($this->renderStateBadges($node->getStateSummary())); + } + + if ($time = $node->getLastStateChange()) { + $since = $this->timeSince($time)->prependContent( + sprintf(' (%s ', $node->getStateName()) + )->addContent(')'); + $link->addContent($since); + } + $td->addContent($link); foreach ($node->getChildren() as $name => $child) { diff --git a/public/css/module.less b/public/css/module.less index 784db3f..c6a4c91 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -22,7 +22,7 @@ table.bp { width: 100%; margin: 0; padding: 0; - color: #0a0a0a; + color: @text-color; border-collapse: collapse; border-spacing: 0; box-sizing: border-box; @@ -143,7 +143,7 @@ table.bp .icon { table.bp.node { td:before { font-family: ifont; - z-index: 10; + z-index: 1; font-size: 1.25em; position: absolute; margin-left: 1.25em; @@ -177,7 +177,8 @@ table.bp tr, table.bp tbody, table.bp th, table.bp td, table.bp.node td > a, tab } table.bp td > a, table.node.missing td > span { - line-height: 2em; + height: 2.5em; + line-height: 2.5em; padding-left: 0.5em; display: block; } @@ -210,7 +211,7 @@ table.bp.handled > tbody > tr > th, table.bp.ok > tbody > tr > th { /* Operator: upper line */ table.bp.operator > tbody > tr:first-child > * { border-top-width: 1px; - border-top-style: dotted; + border-top-style: solid; } table.bp.operator.hovered > tbody > tr:first-child > * { @@ -281,7 +282,7 @@ table.bp { /* Reduce font size after the 3rd level... */ table.bp table.bp table.bp table.bp { - font-size: 0.9em; + font-size: 0.95em; } /* ...and keep it constant afterwards */ @@ -387,6 +388,11 @@ div.knightrider table.bp { } } +div.bp .badges { + display: inline-block; + padding-top: 0; +} + .badge-critical, .badge-down { background: @colorCritical; } .badge-unknown, .badge-unreachable { background: @colorUnknown; } .badge-warning { background: @colorWarning; } From 370558f7407ffcd41b59b3ee54c101f8532e5e6d Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 09:16:17 +0100 Subject: [PATCH 108/256] ProcessController: improve rendering in dasbboard --- application/controllers/ProcessController.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 4956715..9647aaa 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -132,12 +132,14 @@ class ProcessController extends Controller array('style' => 'float: right') ) ); - } else { + } elseif (! $this->view->compact) { $controls->add(HtmlString::create($this->getTabs())); } - $controls->add(Element::create('h1')->setContent($title)); + if (! $this->view->compact) { + $controls->add(Element::create('h1')->setContent($title)); + } $controls->add(Breadcrumb::create($renderer)); - if (! $this->showFullscreen) { + if (! $this->showFullscreen && ! $this->view->compact) { $controls->add($this->actions()); } From cbf8db307354c9717d1a4202e11ca384f329c0be Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 11:11:43 +0100 Subject: [PATCH 109/256] BpNode: free loop detection from side-effects --- library/Businessprocess/BpNode.php | 25 ++---------- library/Businessprocess/BusinessProcess.php | 45 ++++++++++++++++----- 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/library/Businessprocess/BpNode.php b/library/Businessprocess/BpNode.php index 7682ae0..90054d4 100644 --- a/library/Businessprocess/BpNode.php +++ b/library/Businessprocess/BpNode.php @@ -45,8 +45,6 @@ class BpNode extends Node 0 => 4 ); - protected static $loopDetection = array(); - protected $className = 'process'; public function __construct(BusinessProcess $bp, $object) @@ -349,32 +347,15 @@ class BpNode extends Node return $this; } - protected function beginLoopDetection() - { - $name = $this->name; - if (array_key_exists($name, self::$loopDetection)) { - $loop = array_keys(self::$loopDetection); - $loop[] = $name; - self::$loopDetection = array(); - throw new NestingError('Loop detected: %s', implode(' -> ', $loop)); - } - - self::$loopDetection[$name] = true; - } - - protected function endLoopDetection() - { - unset(self::$loopDetection[$this->name]); - } - public function checkForLoops() { + $bp = $this->bp; foreach ($this->getChildren() as $child) { - $this->beginLoopDetection(); + $bp->beginLoopDetection($this->name); if ($child instanceof BpNode) { $child->checkForLoops(); } - $this->endLoopDetection(); + $bp->endLoopDetection($this->name); } return $this; diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index a3176ea..001451c 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -3,7 +3,9 @@ namespace Icinga\Module\Businessprocess; use Icinga\Application\Benchmark; +use Icinga\Exception\IcingaException; use Icinga\Exception\ProgrammingError; +use Icinga\Module\Businessprocess\Exception\NestingError; use Icinga\Module\Businessprocess\Modification\ProcessChanges; use Icinga\Module\Monitoring\Backend\MonitoringBackend; use Icinga\Data\Filter\Filter; @@ -92,6 +94,11 @@ class BusinessProcess */ protected $hosts = array(); + /** @var bool Whether catchable errors should be thrown nonetheless */ + protected $throwErrors = false; + + protected $loopDetection = array(); + /** * Applied state simulation * @@ -309,7 +316,7 @@ class BusinessProcess try { $this->reallyRetrieveStatesFromBackend(); } catch (Exception $e) { - $this->error( + $this->addError( $this->translate('Could not retrieve process state: %s'), $e->getMessage() ); @@ -664,18 +671,19 @@ class BusinessProcess { $args = func_get_args(); array_shift($args); - $this->errors[] = vsprintf($msg, $args); + $msg = vsprintf($msg, $args); + if ($this->throwErrors) { + throw new IcingaException($msg); + } + + $this->errors[] = $msg; return $this; } - /** - * @deprecated - */ - protected function error($msg) + public function throwErrors($throw = true) { - $args = func_get_args(); - array_shift($args); - $this->errors[] = vsprintf($msg, $args); + $this->throwErrors = $throw; + return $this; } public function toLegacyConfigString() @@ -713,6 +721,25 @@ class BusinessProcess return $conf . "\n"; } + public function beginLoopDetection($name) + { + // echo "Begin loop $name\n"; + if (array_key_exists($name, $this->loopDetection)) { + $loop = array_keys($this->loopDetection); + $loop[] = $name; + $this->loopDetection = array(); + throw new NestingError('Loop detected: %s', implode(' -> ', $loop)); + } + + $this->loopDetection[$name] = true; + } + + public function endLoopDetection($name) + { + // echo "End loop $this->name\n"; + unset($this->loopDetection[$name]); + } + public function isEmpty() { return $this->countChildren() === 0; From 7511d0f4c52deea957f90045785a06c2af6260c6 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 11:17:42 +0100 Subject: [PATCH 110/256] Tests: add some more basic tests --- .../library/Businessprocess/HostNodeTest.php | 11 ++++- .../Operators/AndOperatorTest.php | 41 +++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/test/php/library/Businessprocess/HostNodeTest.php b/test/php/library/Businessprocess/HostNodeTest.php index 58f6a22..72678c5 100644 --- a/test/php/library/Businessprocess/HostNodeTest.php +++ b/test/php/library/Businessprocess/HostNodeTest.php @@ -5,7 +5,6 @@ namespace Tests\Icinga\Module\Businessprocess\Operator; use Icinga\Module\Businessprocess\BusinessProcess; use Icinga\Module\Businessprocess\HostNode; use Icinga\Module\Businessprocess\Test\BaseTestCase; -use Icinga\Web\View; class HostNodeTest extends BaseTestCase { @@ -42,6 +41,16 @@ class HostNodeTest extends BaseTestCase ); } + /** + * @expectedException \Icinga\Exception\ConfigurationError + */ + public function testWhetherSettingAnInvalidStateFails() + { + $bp = new BusinessProcess(); + $host = $bp->createHost('localhost')->setState(98); + $bp->createBp('p')->addChild($host)->getState(); + } + /** * @return HostNode */ diff --git a/test/php/library/Businessprocess/Operators/AndOperatorTest.php b/test/php/library/Businessprocess/Operators/AndOperatorTest.php index fbd3791..2969ac2 100644 --- a/test/php/library/Businessprocess/Operators/AndOperatorTest.php +++ b/test/php/library/Businessprocess/Operators/AndOperatorTest.php @@ -102,6 +102,47 @@ class AndOperatorTest extends BaseTestCase ); } + public function testWhetherSimpleAndOperationWorks() + { + $bp = new BusinessProcess(); + $bp->throwErrors(); + $host = $bp->createHost('localhost')->setState(1); + $service = $bp->createService('localhost', 'ping')->setState(1); + $p = $bp->createBp('p'); + $p->addChild($host); + $p->addChild($service); + + $this->assertEquals( + 'DOWN', + $host->getStateName() + ); + + $this->assertEquals( + 'WARNING', + $service->getStateName() + ); + + $this->assertEquals( + 'CRITICAL', + $p->getStateName() + ); + } + + public function testWhetherSimpleOrOperationWorks() + { + $bp = new BusinessProcess(); + $bp->throwErrors(); + $host = $bp->createHost('localhost')->setState(1); + $service = $bp->createService('localhost', 'ping')->setState(1); + $p = $bp->createBp('p', '|'); + $p->addChild($host); + $p->addChild($service); + + $this->assertEquals('DOWN',$host->getStateName()); + $this->assertEquals('WARNING', $service->getStateName()); + $this->assertEquals('WARNING', $p->getStateName()); + } + /** * @return BusinessProcess */ From ca16f22a622ae99879e13b9963a930e5cd8df9fc Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 11:20:54 +0100 Subject: [PATCH 111/256] BusinessProcess/Nodes: some cleanup --- library/Businessprocess/BpNode.php | 2 +- library/Businessprocess/BusinessProcess.php | 6 +++--- library/Businessprocess/Node.php | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/library/Businessprocess/BpNode.php b/library/Businessprocess/BpNode.php index 90054d4..32eb7e5 100644 --- a/library/Businessprocess/BpNode.php +++ b/library/Businessprocess/BpNode.php @@ -112,7 +112,7 @@ class BpNode extends Node ); } $this->children[$name] = $node; - $this->children[] = $name; + $this->childNames[] = $name; $node->addParent($this); return $this; } diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index 001451c..995ba96 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -331,7 +331,6 @@ class BusinessProcess // Separate "parse-logic" from "retrieve-state-logic" // Allow DB-based backend // Use IcingaWeb2 Multi-Backend-Support - $check_results = array(); $hostFilter = array_keys($this->hosts); if ($this->state_type === self::HARD_STATE) { @@ -509,8 +508,9 @@ class BusinessProcess if ($pos !== false) { $host = substr($name, 0, $pos); $service = substr($name, $pos + 1); - if ($service === 'Hoststatus') { - return $this->create($host); + // TODO: deactivated, this scares me, test it + if (false && $service === 'Hoststatus') { + return $this->createHost($host); } else { return $this->createService($host, $service); } diff --git a/library/Businessprocess/Node.php b/library/Businessprocess/Node.php index 7e2e2b5..4c99d35 100644 --- a/library/Businessprocess/Node.php +++ b/library/Businessprocess/Node.php @@ -193,6 +193,7 @@ abstract class Node ) ); } + return $this->state; } @@ -297,7 +298,7 @@ abstract class Node return static::$stateToSortStateMap[$state]; } - throw new ProgrammingError('Got invalid state %s', $sort_state); + throw new ProgrammingError('Got invalid state: %s', var_export($state, 1)); } protected function sortStateTostate($sortState) From b954a67c798ee80361d8152334d2f795e2d95fb5 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 11:21:10 +0100 Subject: [PATCH 112/256] TreeRenderer: remove duplicate actions --- library/Businessprocess/Renderer/TreeRenderer.php | 1 - 1 file changed, 1 deletion(-) diff --git a/library/Businessprocess/Renderer/TreeRenderer.php b/library/Businessprocess/Renderer/TreeRenderer.php index 9ea0ccb..01b172c 100644 --- a/library/Businessprocess/Renderer/TreeRenderer.php +++ b/library/Businessprocess/Renderer/TreeRenderer.php @@ -139,7 +139,6 @@ class TreeRenderer extends Renderer )->setContent($node->operatorHtml()); } $td = $tr->createElement('td'); - $td->addContent($this->getActionIcons($bp, $node)); if ($node instanceof BpNode && $node->hasInfoUrl()) { $td->add($this->createInfoAction($node)); From 857913172b87f7c399335291aaa2c0253797e07c Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 11:22:44 +0100 Subject: [PATCH 113/256] BpNode: use modified loop detection everywhere --- library/Businessprocess/BpNode.php | 9 +++++++-- library/Businessprocess/BusinessProcess.php | 1 + 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/library/Businessprocess/BpNode.php b/library/Businessprocess/BpNode.php index 32eb7e5..31adbf0 100644 --- a/library/Businessprocess/BpNode.php +++ b/library/Businessprocess/BpNode.php @@ -299,18 +299,23 @@ class BpNode extends Node */ public function reCalculateState() { + $bp = $this->bp; + $sort_states = array(); $lastStateChange = 0; + if (!$this->hasChildren()) { + // TODO: delegate this to operators, should mostly fail $this->state = 0; + $this->setMissing(); return $this; } foreach ($this->getChildren() as $child) { - $this->beginLoopDetection(); + $bp->beginLoopDetection($this->name); $sort_states[] = $child->getSortingState(); $lastStateChange = max($lastStateChange, $child->getLastStateChange()); - $this->endLoopDetection(); + $bp->endLoopDetection($this->name); } $this->setLastStateChange($lastStateChange); diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index 995ba96..555d48f 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -112,6 +112,7 @@ class BusinessProcess protected $simulationCount = 0; + /** @var ProcessChanges */ protected $appliedChanges; public function __construct() From 962faab99258b314aa9cb1d1986f299b1ee93d59 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 11:23:11 +0100 Subject: [PATCH 114/256] AndOperatorTest: test pending fixes #8727 --- .../library/Businessprocess/HostNodeTest.php | 2 +- .../Operators/AndOperatorTest.php | 54 +++++++++++++++++++ .../Businessprocess/ServiceNodeTest.php | 1 - 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/test/php/library/Businessprocess/HostNodeTest.php b/test/php/library/Businessprocess/HostNodeTest.php index 72678c5..bc5716c 100644 --- a/test/php/library/Businessprocess/HostNodeTest.php +++ b/test/php/library/Businessprocess/HostNodeTest.php @@ -42,7 +42,7 @@ class HostNodeTest extends BaseTestCase } /** - * @expectedException \Icinga\Exception\ConfigurationError + * @expectedException \Icinga\Exception\ProgrammingError */ public function testWhetherSettingAnInvalidStateFails() { diff --git a/test/php/library/Businessprocess/Operators/AndOperatorTest.php b/test/php/library/Businessprocess/Operators/AndOperatorTest.php index 2969ac2..9436184 100644 --- a/test/php/library/Businessprocess/Operators/AndOperatorTest.php +++ b/test/php/library/Businessprocess/Operators/AndOperatorTest.php @@ -143,6 +143,60 @@ class AndOperatorTest extends BaseTestCase $this->assertEquals('WARNING', $p->getStateName()); } + public function testWhetherPendingIsAccepted() + { + $bp = new BusinessProcess(); + $host = $bp->createHost('localhost')->setState(99); + $service = $bp->createService('localhost', 'ping')->setState(99); + $p = $bp->createBp('p') + ->addChild($host) + ->addChild($service); + + $this->assertEquals( + 'PENDING', + $p->getStateName() + ); + } + + public function testWhetherWarningIsWorseThanPending() + { + $bp = new BusinessProcess(); + $host = $bp->createHost('localhost')->setState(99); + $service = $bp->createService('localhost', 'ping')->setState(1); + $p = $bp->createBp('p') + ->addChild($host) + ->addChild($service); + + $this->assertEquals( + 'WARNING', + $p->getStateName() + ); + } + + public function testWhetherPendingIsWorseThanUpOrOk() + { + $bp = new BusinessProcess(); + $host = $bp->createHost('localhost')->setState(99); + $service = $bp->createService('localhost', 'ping')->setState(0); + $p = $bp->createBp('p') + ->addChild($host) + ->addChild($service); + + $this->assertEquals( + 'PENDING', + $p->getStateName() + ); + + $p->clearState(); + $host->setState(0); + $service->setState(99); + + $this->assertEquals( + 'PENDING', + $p->getStateName() + ); + } + /** * @return BusinessProcess */ diff --git a/test/php/library/Businessprocess/ServiceNodeTest.php b/test/php/library/Businessprocess/ServiceNodeTest.php index 30e730c..39fe0f3 100644 --- a/test/php/library/Businessprocess/ServiceNodeTest.php +++ b/test/php/library/Businessprocess/ServiceNodeTest.php @@ -5,7 +5,6 @@ namespace Tests\Icinga\Module\Businessprocess\Operator; use Icinga\Module\Businessprocess\BusinessProcess; use Icinga\Module\Businessprocess\ServiceNode; use Icinga\Module\Businessprocess\Test\BaseTestCase; -use Icinga\Web\View; class ServiceNodeTest extends BaseTestCase { From 9cadf78c0016556253019330b347562879b1f23a Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 15:04:11 +0100 Subject: [PATCH 115/256] TreeRenderer: do not cast to string early --- library/Businessprocess/Renderer/TreeRenderer.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/library/Businessprocess/Renderer/TreeRenderer.php b/library/Businessprocess/Renderer/TreeRenderer.php index 01b172c..a967085 100644 --- a/library/Businessprocess/Renderer/TreeRenderer.php +++ b/library/Businessprocess/Renderer/TreeRenderer.php @@ -6,7 +6,6 @@ use Icinga\Module\Businessprocess\BpNode; use Icinga\Module\Businessprocess\BusinessProcess; use Icinga\Module\Businessprocess\Html\Container; use Icinga\Module\Businessprocess\Html\Element; -use Icinga\Module\Businessprocess\Html\HtmlString; use Icinga\Module\Businessprocess\Html\Icon; use Icinga\Module\Businessprocess\Html\Link; use Icinga\Module\Businessprocess\Node; @@ -25,7 +24,7 @@ class TreeRenderer extends Renderer 'id' => $bp->getHtmlId(), 'class' => 'bp' ), - HtmlString::create($this->renderBp($bp)) + $this->renderBp($bp) )); return parent::render(); @@ -37,7 +36,7 @@ class TreeRenderer extends Renderer */ public function renderBp(BusinessProcess $bp) { - $html = ''; + $html = array(); if ($this->wantsRootNodes()) { $nodes = $bp->getChildren(); } else { @@ -45,7 +44,7 @@ class TreeRenderer extends Renderer } foreach ($nodes as $name => $node) { - $html .= $this->renderNode($bp, $node); + $html[] = $this->renderNode($bp, $node); } return $html; From a47e78209d84ba2c6d6d2c100a87a1e21446f674 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 15:06:24 +0100 Subject: [PATCH 116/256] AddNodeForm: initial commit --- application/forms/AddNodeForm.php | 326 ++++++++++++++++++++++++++++++ 1 file changed, 326 insertions(+) create mode 100644 application/forms/AddNodeForm.php diff --git a/application/forms/AddNodeForm.php b/application/forms/AddNodeForm.php new file mode 100644 index 0000000..efc89d6 --- /dev/null +++ b/application/forms/AddNodeForm.php @@ -0,0 +1,326 @@ +selectNodeType(); + switch ($type) { + case 'host': + $this->selectHost(); + break; + case 'service': + $this->selectService(); + break; + case 'process': + $this->selectProcess(); + break; + case null: + $this->setSubmitLabel($this->translate('Next')); + return; + } + } + + protected function addNewProcess() + { + $this->addElement('text', 'name', array( + 'label' => $this->translate('Name'), + 'required' => 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', 'url', array( + 'label' => $this->translate('Info URL'), + 'description' => $this->translate( + 'URL pointing to more information about this node' + ) + )); + } + + /** + * @return string|null + */ + protected function selectNodeType() + { + $types = array(); + if ($this->hasParentNode()) { + $types['host'] = $this->translate('Host'); + $types['service'] = $this->translate('Service'); + } + $types['process'] = $this->translate('Process'); + + $this->addElement('select', 'node_type', array( + 'label' => $this->translate('Node type'), + 'required' => true, + 'description' => $this->translate( + 'The node type you want to add' + ), + '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' => 14, + '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); + } + } + + protected function addHostElement() + { + $this->addElement('select', 'host', array( + 'label' => $this->translate('Host'), + 'required' => true, + 'ignore' => true, + 'class' => 'autosubmit', + 'multiOptions' => $this->optionalEnum($this->enumHostList()), + )); + } + + protected function addServicesElement($host) + { + $this->addElement('multiselect', 'children', array( + 'label' => $this->translate('Services'), + 'required' => true, + 'size' => 14, + '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' => 14, + '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 BusinessProcess $process + * @return $this + */ + public function setProcess(BusinessProcess $process) + { + $this->bp = $process; + $this->setBackend($process->getBackend()); + return $this; + } + + /** + * @param BpNode $node + * @return $this + */ + public function setParentNode(BpNode $node) + { + $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 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 + return array_combine((array) $names, (array) $names); + } + + 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 enumProcesses() + { + $list = array(); + + foreach ($this->bp->getNodes() as $node) { + if ($node instanceof BpNode) { + // TODO: Blacklist parents + $list[(string) $node] = (string) $node; // display name? + } + } + + natsort($list); + return $list; + } + + 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; + } + + public function onSuccess() + { + $changes = ProcessChanges::construct($this->bp, $this->session); + $changes->addChildrenToNode($this->parent, $this->bp); + parent::onSuccess(); + } +} From f820dcfec099c5df0d9f7fc10ed777d6f24c9609 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 15:06:38 +0100 Subject: [PATCH 117/256] NodeAddChildrenAction: new modification action --- .../Modification/NodeAddChildrenAction.php | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 library/Businessprocess/Modification/NodeAddChildrenAction.php diff --git a/library/Businessprocess/Modification/NodeAddChildrenAction.php b/library/Businessprocess/Modification/NodeAddChildrenAction.php new file mode 100644 index 0000000..77b7080 --- /dev/null +++ b/library/Businessprocess/Modification/NodeAddChildrenAction.php @@ -0,0 +1,66 @@ +getNodeName(); + + if (! $bp->hasNode($name)) { + return false; + } + + return $bp->getNode($name) instanceof BpNode; + } + + /** + * @inheritdoc + */ + public function applyTo(BusinessProcess $bp) + { + /** @var BpNode $node */ + $node = $bp->getNode($this->getNodeName()); + $existing = $node->getChildNames(); + foreach ($this->children as $name) { + if (! in_array($name, $existing)) { + $existing[] = $name; + } + } + $node->setChildNames($existing); + + return $this; + } + + /** + * @param array|string $children + * @return $this + */ + public function setChildren($children) + { + if (is_string($children)) { + $children = array($children); + } + $this->children = $children; + return $this; + } + + /** + * @return array + */ + public function getChildren() + { + return $this->children; + } +} From decc788d2ad94935ef68a59e02ec1d9792f8725e Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 15:08:45 +0100 Subject: [PATCH 118/256] LICENSE: renamed --- COPYING => LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename COPYING => LICENSE (99%) diff --git a/COPYING b/LICENSE similarity index 99% rename from COPYING rename to LICENSE index ecbc059..d159169 100644 --- a/COPYING +++ b/LICENSE @@ -336,4 +336,4 @@ This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. \ No newline at end of file +Public License instead of this License. From 991d9f9b05888409bb7615b9b54bc5b31554c36f Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 15:20:13 +0100 Subject: [PATCH 119/256] TileRenderer: incorporate AddNewTile --- .../Businessprocess/Renderer/TileRenderer.php | 23 ++++++++- .../Renderer/TileRenderer/AddNewTile.php | 48 ------------------- 2 files changed, 21 insertions(+), 50 deletions(-) delete mode 100644 library/Businessprocess/Renderer/TileRenderer/AddNewTile.php diff --git a/library/Businessprocess/Renderer/TileRenderer.php b/library/Businessprocess/Renderer/TileRenderer.php index afaf858..62e8e6c 100644 --- a/library/Businessprocess/Renderer/TileRenderer.php +++ b/library/Businessprocess/Renderer/TileRenderer.php @@ -3,7 +3,9 @@ namespace Icinga\Module\Businessprocess\Renderer; use Icinga\Module\Businessprocess\Html\Container; -use Icinga\Module\Businessprocess\Renderer\TileRenderer\AddNewTile; +use Icinga\Module\Businessprocess\Html\Element; +use Icinga\Module\Businessprocess\Html\Icon; +use Icinga\Module\Businessprocess\Html\Link; use Icinga\Module\Businessprocess\Renderer\TileRenderer\NodeTile; class TileRenderer extends Renderer @@ -31,7 +33,7 @@ class TileRenderer extends Renderer } if (! $this->isLocked()) { - $this->add(new AddNewTile($this)); + $this->add($this->addNewNode()); } $path = $this->getCurrentPath(); @@ -72,4 +74,21 @@ class TileRenderer extends Renderer return $howMany; } + + protected function addNewNode() + { + return Element::create( + 'div', + array('class' => 'addnew') + )->add( + Link::create( + Icon::create('plus'), + $this->getUrl()->with('action', 'add'), + null, + array( + 'title' => $this->translate('Add a new business process node') + ) + )->addContent($this->translate('Add')) + ); + } } diff --git a/library/Businessprocess/Renderer/TileRenderer/AddNewTile.php b/library/Businessprocess/Renderer/TileRenderer/AddNewTile.php deleted file mode 100644 index 84b0267..0000000 --- a/library/Businessprocess/Renderer/TileRenderer/AddNewTile.php +++ /dev/null @@ -1,48 +0,0 @@ - 'addnew'); - - public function __construct(Renderer $renderer) - { - $bp = $renderer->getBusinessProcess(); - $path = $renderer->getCurrentPath(); - - $params = array( - 'config' => $bp->getName() - ); - - // Workaround for array issues - $url = Url::fromPath('businessprocess/node/add'); - $p = $url->getParams(); - $p->mergeValues($params); - if (! empty($path)) { - $p->addValues('path', $path); - } - - $this->add( - Link::create( - Icon::create('plus'), - $url, - null, - array( - 'title' => $this->translate('Add a new business process node') - ) - )->addContent($this->translate('Add')) - ); - } -} From c11a1817011c95dfd6bfc597661e51e496ce8cf8 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 15:32:56 +0100 Subject: [PATCH 120/256] Process: add and use new form --- application/controllers/ProcessController.php | 27 +++++++++++++++++-- application/forms/AddNodeForm.php | 2 +- application/forms/BpConfigForm.php | 1 + library/Businessprocess/Controller.php | 3 +++ .../Modification/NodeCreateAction.php | 1 - .../Modification/ProcessChanges.php | 13 +++++++++ library/Businessprocess/Node.php | 2 +- library/Businessprocess/Renderer/Renderer.php | 20 +++++++++++++- .../Operators/AndOperatorTest.php | 2 +- 9 files changed, 64 insertions(+), 7 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 9647aaa..8225297 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -53,6 +53,7 @@ class ProcessController extends Controller public function showAction() { $mode = $this->params->get('mode'); + $action = $this->params->get('action'); $unlocked = (bool) $this->params->get('unlocked'); $this->prepareProcessActions(); $this->prepareProcess(); @@ -117,7 +118,7 @@ class ProcessController extends Controller } else { $renderer = new TreeRenderer($bp, $bpNode); } - $renderer->setBaseUrl($this->url()) + $renderer->setUrl($this->url()) ->setPath($this->params->getValues('path')); $this->content()->add($renderer); $controls = $this->controls(); @@ -147,6 +148,28 @@ class ProcessController extends Controller $renderer->unlock(); } + if ($action === 'add') { + $this->content()->add(HtmlString::create( + (string) $this->loadForm('AddNode') + ->setProcess($bp) + ->setParentNode($bp->getNode($node)) + ->setSession($this->session()) + ->handleRequest() + )); + } elseif ($action === 'simulation') { + $this->content()->add(HtmlString::create( + $this->loadForm('simulation') + ->setSimulation(new Simulation($bp, $this->session())) + ->setNode($node) + ->handleRequest() + )); + } + + if ($action) { + // No autorefresh when showing forms + return; + } + if ($this->isXhr()) { if ($this->params->get('addSimulation')) { $this->setAutorefreshInterval(30); @@ -206,7 +229,7 @@ class ProcessController extends Controller $this->actions()->add( Link::create( $this->translate('Lock'), - $this->url()->without('unlocked'), + $this->url()->without('unlocked')->without('action'), null, array( 'class' => 'icon-lock', diff --git a/application/forms/AddNodeForm.php b/application/forms/AddNodeForm.php index efc89d6..1627a1b 100644 --- a/application/forms/AddNodeForm.php +++ b/application/forms/AddNodeForm.php @@ -30,7 +30,7 @@ class AddNodeForm extends QuickForm public function setup() { - $type = $this->selectNodeType(); + $type = $this->selectNodeType(); switch ($type) { case 'host': $this->selectHost(); diff --git a/application/forms/BpConfigForm.php b/application/forms/BpConfigForm.php index abf3111..5382673 100644 --- a/application/forms/BpConfigForm.php +++ b/application/forms/BpConfigForm.php @@ -167,6 +167,7 @@ class BpConfigForm extends QuickForm $this->redirectOnSuccess(sprintf('Process %s has been created', $name)); } else { + $config = $this->config; if ($title) { $config->setTitle($title); } diff --git a/library/Businessprocess/Controller.php b/library/Businessprocess/Controller.php index 44da2bb..0ec129c 100644 --- a/library/Businessprocess/Controller.php +++ b/library/Businessprocess/Controller.php @@ -100,6 +100,9 @@ class Controller extends ModuleController return $this->view->controls; } + /** + * @return Content + */ protected function content() { if ($this->view->content === null) { diff --git a/library/Businessprocess/Modification/NodeCreateAction.php b/library/Businessprocess/Modification/NodeCreateAction.php index b47dacb..4d970dc 100644 --- a/library/Businessprocess/Modification/NodeCreateAction.php +++ b/library/Businessprocess/Modification/NodeCreateAction.php @@ -5,7 +5,6 @@ namespace Icinga\Module\Businessprocess\Modification; use Icinga\Module\Businessprocess\BusinessProcess; use Icinga\Module\Businessprocess\BpNode; use Icinga\Module\Businessprocess\Node; -use stdClass; class NodeCreateAction extends NodeAction { diff --git a/library/Businessprocess/Modification/ProcessChanges.php b/library/Businessprocess/Modification/ProcessChanges.php index 452bd4a..2adcb25 100644 --- a/library/Businessprocess/Modification/ProcessChanges.php +++ b/library/Businessprocess/Modification/ProcessChanges.php @@ -64,6 +64,19 @@ class ProcessChanges return $this->push($action); } + /** + * @param Node $node + * @param $properties + * + * @return $this + */ + public function addChildrenToNode(Node $node, $children) + { + $action = new NodeAddChildrenAction($node); + $action->setChildren($node, $children); + return $this->push($action); + } + /** * @param Node|string $nodeName * @param object $properties diff --git a/library/Businessprocess/Node.php b/library/Businessprocess/Node.php index 4c99d35..8ba84f1 100644 --- a/library/Businessprocess/Node.php +++ b/library/Businessprocess/Node.php @@ -298,7 +298,7 @@ abstract class Node return static::$stateToSortStateMap[$state]; } - throw new ProgrammingError('Got invalid state: %s', var_export($state, 1)); + throw new ProgrammingError('Got invalid state: %s', var_export($state, 1)); } protected function sortStateTostate($sortState) diff --git a/library/Businessprocess/Renderer/Renderer.php b/library/Businessprocess/Renderer/Renderer.php index ba3088a..6b87432 100644 --- a/library/Businessprocess/Renderer/Renderer.php +++ b/library/Businessprocess/Renderer/Renderer.php @@ -10,7 +10,6 @@ use Icinga\Module\Businessprocess\Html\Container; use Icinga\Module\Businessprocess\Html\Element; use Icinga\Module\Businessprocess\Html\Html; use Icinga\Module\Businessprocess\Html\HtmlString; -use Icinga\Module\Businessprocess\Html\Text; use Icinga\Module\Businessprocess\Node; use Icinga\Module\Businessprocess\Web\Url; use Icinga\Web\Request; @@ -26,6 +25,9 @@ abstract class Renderer extends Html /** @var bool Administrative actions are hidden unless unlocked */ protected $locked = true; + /** @var Url */ + protected $url; + /** @var Url */ protected $baseUrl; @@ -162,6 +164,17 @@ abstract class Renderer extends Html return $path; } + /** + * @param Url $url + * @return $this + */ + public function setUrl(Url $url) + { + $this->url = clone($url); + $this->setBaseUrl($url); + return $this; + } + /** * @param Url $url * @return $this @@ -172,6 +185,11 @@ abstract class Renderer extends Html return $this; } + public function getUrl() + { + return $this->url; + } + /** * @return Url * @throws ProgrammingError diff --git a/test/php/library/Businessprocess/Operators/AndOperatorTest.php b/test/php/library/Businessprocess/Operators/AndOperatorTest.php index 9436184..8e88c39 100644 --- a/test/php/library/Businessprocess/Operators/AndOperatorTest.php +++ b/test/php/library/Businessprocess/Operators/AndOperatorTest.php @@ -138,7 +138,7 @@ class AndOperatorTest extends BaseTestCase $p->addChild($host); $p->addChild($service); - $this->assertEquals('DOWN',$host->getStateName()); + $this->assertEquals('DOWN', $host->getStateName()); $this->assertEquals('WARNING', $service->getStateName()); $this->assertEquals('WARNING', $p->getStateName()); } From 8825b0e34322d3708da80c242832512f84f48f16 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 29 Nov 2016 16:58:03 +0100 Subject: [PATCH 121/256] ProcessController: restructured --- application/controllers/ProcessController.php | 223 +++++++++--------- .../Businessprocess/Renderer/TreeRenderer.php | 21 +- 2 files changed, 135 insertions(+), 109 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 8225297..bf663b3 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -2,12 +2,15 @@ namespace Icinga\Module\Businessprocess\Controllers; +use Icinga\Module\Businessprocess\BusinessProcess; use Icinga\Module\Businessprocess\Controller; use Icinga\Module\Businessprocess\ConfigDiff; use Icinga\Module\Businessprocess\Html\Element; use Icinga\Module\Businessprocess\Html\HtmlString; use Icinga\Module\Businessprocess\Html\Icon; +use Icinga\Module\Businessprocess\Node; use Icinga\Module\Businessprocess\Renderer\Breadcrumb; +use Icinga\Module\Businessprocess\Renderer\Renderer; use Icinga\Module\Businessprocess\Renderer\TileRenderer; use Icinga\Module\Businessprocess\Renderer\TreeRenderer; use Icinga\Module\Businessprocess\Simulation; @@ -18,6 +21,9 @@ use Icinga\Web\Widget\Tabextension\DashboardAction; class ProcessController extends Controller { + /** @var Renderer */ + protected $renderer; + /** * Create a new business process configuration */ @@ -52,75 +58,26 @@ class ProcessController extends Controller */ public function showAction() { - $mode = $this->params->get('mode'); - $action = $this->params->get('action'); - $unlocked = (bool) $this->params->get('unlocked'); - $this->prepareProcessActions(); - $this->prepareProcess(); + $bp = $this->prepareProcess(); + $node = $this->getNode($bp); + $this->prepareActionBar(); $this->redirectOnConfigSwitch(); - - if ($unlocked) { - $bp = $this->loadModifiedBpConfig(); - $bp->unlock(); - } else { - $bp = $this->loadBpConfig(); - } - - // Do not lock empty configs - if ($bp->isEmpty() && ! $this->view->compact && $bp->isLocked()) { - $this->redirectNow($this->url()->with('unlocked', true)); - } - - if ($node = $this->params->get('node')) { - // Render a specific node - - $this->view->nodeName = $node; - $bpNode = $this->view->bp = $bp->getNode($node); - } else { - // Render a single process - $this->view->bp = $bp; - if ($bp->hasWarnings()) { - $this->view->warnings = $bp->getWarnings(); - } - $bpNode = null; - } - $bp->retrieveStatesFromBackend(); - if ($this->params->get('addSimulation')) { - $this->simulationForm(); - } + $this->handleSimulations($bp); - $title = sprintf('Business Process "%s"', $bp->getTitle()); - $this->setTitle($title); - $this->tabsForShow()->activate('show'); + $this->setTitle('Business Process "%s"', $bp->getTitle()); - if ($bp->isLocked()) { - $this->tabs()->extend(new DashboardAction()); - } else { - - $simulation = new Simulation($bp, $this->session()); - if ($this->params->get('dismissSimulations')) { - Notification::success( - sprintf( - $this->translate('%d applied simulation(s) have been dropped'), - $simulation->count() - ) - ); - $simulation->clear(); - $this->redirectNow($this->url()->without('dismissSimulations')->without('unlocked')); - } - - $bp->applySimulation($simulation); - } - - if ($mode === 'tile') { - $renderer = new TileRenderer($bp, $bpNode); - } else { - $renderer = new TreeRenderer($bp, $bpNode); - } - $renderer->setUrl($this->url()) - ->setPath($this->params->getValues('path')); + $renderer = $this->prepareRenderer($bp, $node); + $this->prepareControls($bp, $renderer); + // if (! $action) { $this->content()->add($renderer); + // } + $this->loadActionForm($bp, $node); + $this->setDynamicAutorefresh(); + } + + protected function prepareControls($bp, $renderer) + { $controls = $this->controls(); if ($this->showFullscreen) { @@ -133,40 +90,109 @@ class ProcessController extends Controller array('style' => 'float: right') ) ); - } elseif (! $this->view->compact) { - $controls->add(HtmlString::create($this->getTabs())); } + + $this->addProcessTabs($bp); if (! $this->view->compact) { - $controls->add(Element::create('h1')->setContent($title)); + $controls->add(Element::create('h1')->setContent($this->view->title)); } $controls->add(Breadcrumb::create($renderer)); if (! $this->showFullscreen && ! $this->view->compact) { $controls->add($this->actions()); } + } + protected function getNode($bp) + { + if ($nodeName = $this->params->get('node')) { + return $bp->getNode($nodeName); + } else { + return null; + } + } + + protected function prepareRenderer($bp, $node) + { + if ($this->renderer === null) { + + if ($this->params->get('mode') === 'tile') { + $renderer = new TileRenderer($bp, $node); + } else { + $renderer = new TreeRenderer($bp, $node); + } + $renderer->setUrl($this->url()) + ->setPath($this->params->getValues('path')); + + + if (!$bp->isLocked()) { + $renderer->unlock(); + } + + $this->renderer = $renderer; + } + + return $this->renderer; + } + + protected function addProcessTabs($bp) + { + if ($this->showFullscreen || $this->view->compact) { + return; + } + + $tabs = $this->defaultTab(); if (! $bp->isLocked()) { - $renderer->unlock(); + $tabs->extend(new DashboardAction()); + } + } + + protected function handleSimulations(BusinessProcess $bp) + { + if (! $bp->isLocked()) { + return; } + $simulation = new Simulation($bp, $this->session()); + + if ($this->params->get('dismissSimulations')) { + Notification::success( + sprintf( + $this->translate('%d applied simulation(s) have been dropped'), + $simulation->count() + ) + ); + $simulation->clear(); + $this->redirectNow($this->url()->without('dismissSimulations')->without('unlocked')); + } + + $bp->applySimulation($simulation); + } + + protected function loadActionForm(BusinessProcess $bp, Node $node = null) + { + $action = $this->params->get('action'); + $form = null; if ($action === 'add') { - $this->content()->add(HtmlString::create( - (string) $this->loadForm('AddNode') - ->setProcess($bp) - ->setParentNode($bp->getNode($node)) - ->setSession($this->session()) - ->handleRequest() - )); + $form =$this->loadForm('AddNode') + ->setProcess($bp) + ->setParentNode($node) + ->setSession($this->session()) + ->handleRequest(); } elseif ($action === 'simulation') { - $this->content()->add(HtmlString::create( - $this->loadForm('simulation') - ->setSimulation(new Simulation($bp, $this->session())) - ->setNode($node) - ->handleRequest() - )); + $form = $this->loadForm('simulation') + ->setSimulation(new Simulation($bp, $this->session())) + ->setNode($node) + ->handleRequest(); } - if ($action) { - // No autorefresh when showing forms + if ($form) { + $this->content()->prependContent(HtmlString::create((string) $form)); + } + } + + protected function setDynamicAutorefresh() + { + if ($this->params->get('action')) { return; } @@ -186,21 +212,19 @@ class ProcessController extends Controller protected function prepareProcess() { + $bp = $this->loadModifiedBpConfig(); if ($this->params->get('unlocked')) { - $bp = $this->loadModifiedBpConfig(); $bp->unlock(); - } else { - $bp = $this->loadBpConfig(); } - if ($node = $this->params->get('node')) { - // Render a specific node - $this->view->nodeName = $node; - $this->view->bp = $bp->getNode($node); + if ($bp->isEmpty() && $bp->isLocked()) { + $this->redirectNow($this->url()->with('unlocked', true)); } + + return $bp; } - protected function prepareProcessActions() + protected function prepareActionBar() { $mode = $this->params->get('mode'); $unlocked = (bool) $this->params->get('unlocked'); @@ -278,23 +302,6 @@ class ProcessController extends Controller ); } - protected function simulationForm() - { - $this->prepareProcess(); - $bp = $this->loadBpConfig(); - $nodename = $this->getParam('simulationNode'); - $node = $bp->getNode($nodename); - - $url = $this->getRequest()->getUrl()->without('addSimulation')->without('simulationNode'); - $this->view->form = $this->loadForm('simulation') - ->setSimulation(new Simulation($bp, $this->session())) - ->setNode($node) - ->setSuccessUrl($url) - ->handleRequest(); - - $this->view->node = $node; - } - /** * Show the source code for a process */ diff --git a/library/Businessprocess/Renderer/TreeRenderer.php b/library/Businessprocess/Renderer/TreeRenderer.php index a967085..1b00fd5 100644 --- a/library/Businessprocess/Renderer/TreeRenderer.php +++ b/library/Businessprocess/Renderer/TreeRenderer.php @@ -130,7 +130,7 @@ class TreeRenderer extends Renderer $tr->createElement( 'th', array( - 'rowspan' => $node->countChildren() + 1 + 'rowspan' => $node->countChildren() + 1 + ($this->isLocked() ? 0 : 1) ) )->createElement( 'span', @@ -169,6 +169,12 @@ class TreeRenderer extends Renderer ); } + if (! $this->isLocked() && $node instanceof BpNode) { + $tbody->createElement('tr')->createElement('td')->setContent( + $this->renderAddNewNode($node) + ); + } + return $table; } @@ -226,4 +232,17 @@ class TreeRenderer extends Renderer ) ); } + + protected function renderAddNewNode($parent) + { + return Link::create( + $this->translate('Add'), + $this->getUrl()->with('action', 'add')->with('node', $parent->getName()), + null, + array( + 'class' => 'addnew icon-plus', + 'title' => $this->translate('Add a new business process node') + ) + ); + } } From 064e6934fb1ce2b82105bc48c63080e6625f7ea4 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 30 Nov 2016 14:35:13 +0100 Subject: [PATCH 122/256] TreeRenderer: fix simulation link --- library/Businessprocess/Renderer/TreeRenderer.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/library/Businessprocess/Renderer/TreeRenderer.php b/library/Businessprocess/Renderer/TreeRenderer.php index 1b00fd5..1ef036d 100644 --- a/library/Businessprocess/Renderer/TreeRenderer.php +++ b/library/Businessprocess/Renderer/TreeRenderer.php @@ -203,9 +203,10 @@ class TreeRenderer extends Renderer { return $this->actionIcon( 'magic', - Url::fromPath('businessprocess/process/show?addSimulation&unlocked', array( - 'config' => $bp->getName(), - 'node' => $node->getName() + $this->getUrl()->with(array( + //'config' => $bp->getName(), + 'action' => 'simulation', + 'simulationnode' => $node->getName() )), $this->translate('Simulate a specific state') ); From fbfb99171eb84af4195463473b08ccdf0eb2b824 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 30 Nov 2016 14:35:58 +0100 Subject: [PATCH 123/256] SimulationForm: state type fixes --- application/forms/SimulationForm.php | 54 +++++++++++++++++++--------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/application/forms/SimulationForm.php b/application/forms/SimulationForm.php index 2372f8a..23a4313 100644 --- a/application/forms/SimulationForm.php +++ b/application/forms/SimulationForm.php @@ -19,30 +19,34 @@ class SimulationForm extends QuickForm public function setup() { - $states = array_merge( - array( - null => sprintf( - $this->translate('Use current state (%s)'), - $this->translate($this->node->getStateName()) - ) - ), - $this->node->enumStateNames() - ); + $states = $this->enumStateNames(); // TODO: Fetch state from object if ($this->simulatedNode) { $simulatedState = $this->simulatedNode->getState(); $states[$simulatedState] = sprintf( '%s (%s)', - $simulatedState, + $this->node->getStateName($simulatedState), $this->translate('Current simulation') ); $node = $this->simulatedNode; + $hasSimulation = true; } else { + $hasSimulation = false; $node = $this->node; } - $this->addHtml('

Configure this simulation

'); + $view = $this->getView(); + if ($hasSimulation) { + $title = $this->translate('Modify simulation for %s'); + } else { + $title = $this->translate('Add simulation for %s'); + } + $this->addHtml( + '

' + . $view->escape(sprintf($title, $node->getAlias())) + . '

' + ); $this->addElement('select', 'state', array( 'label' => $this->translate('State'), @@ -50,7 +54,10 @@ class SimulationForm extends QuickForm 'class' => 'autosubmit', 'value' => $this->simulatedNode ? $node->getState() : null, )); - if (ctype_digit($this->getSentValue('state'))) { + if (in_array($this->getSentValue('state'), array('0', '99'))) { + return; + } + if ($hasSimulation || ctype_digit($this->getSentValue('state'))) { $this->addElement('checkbox', 'acknowledged', array( 'label' => $this->translate('Acknowledged'), 'value' => $node->isAcknowledged(), @@ -75,10 +82,10 @@ class SimulationForm extends QuickForm { $this->simulation = $simulation; - $nodeName = (string) $this->node; - if ($simulation->hasNode($nodeName)) { + $name = $this->node->getName(); + if ($simulation->hasNode($name)) { $this->simulatedNode = clone($this->node); - $s = $simulation->getNode($nodeName); + $s = $simulation->getNode($name); $this->simulatedNode->setState($s->state) ->setAck($s->acknowledged) ->setDowntime($s->in_downtime) @@ -90,7 +97,7 @@ class SimulationForm extends QuickForm public function onSuccess() { - $nodeName = (string) $this->node; + $nodeName = $this->node->getName(); if (ctype_digit($this->getValue('state'))) { $this->notifySuccess($this->translate('Simulation has been set')); @@ -106,4 +113,19 @@ class SimulationForm extends QuickForm } $this->redirectOnSuccess(); } + + /** + * @return array + */ + protected function enumStateNames() + { + $states = array( + null => sprintf( + $this->translate('Use current state (%s)'), + $this->translate($this->node->getStateName()) + ) + ) + $this->node->enumStateNames(); + + return $states; + } } From 414d17afbf7913829115ad5801fbd3b4417592a0 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 30 Nov 2016 14:37:07 +0100 Subject: [PATCH 124/256] ProcessController: show hints and errors --- application/controllers/ProcessController.php | 88 +++++++++++++++---- 1 file changed, 69 insertions(+), 19 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index bf663b3..14ff6c7 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -69,9 +69,9 @@ class ProcessController extends Controller $renderer = $this->prepareRenderer($bp, $node); $this->prepareControls($bp, $renderer); - // if (! $action) { + $this->content()->addContent($this->showHints($bp)); + $this->content()->addContent($this->showWarnings($bp)); $this->content()->add($renderer); - // } $this->loadActionForm($bp, $node); $this->setDynamicAutorefresh(); } @@ -148,10 +148,6 @@ class ProcessController extends Controller protected function handleSimulations(BusinessProcess $bp) { - if (! $bp->isLocked()) { - return; - } - $simulation = new Simulation($bp, $this->session()); if ($this->params->get('dismissSimulations')) { @@ -180,8 +176,8 @@ class ProcessController extends Controller ->handleRequest(); } elseif ($action === 'simulation') { $form = $this->loadForm('simulation') + ->setNode($bp->getNode($this->params->get('simulationnode'))) ->setSimulation(new Simulation($bp, $this->session())) - ->setNode($node) ->handleRequest(); } @@ -192,21 +188,75 @@ class ProcessController extends Controller protected function setDynamicAutorefresh() { - if ($this->params->get('action')) { - return; - } - - if ($this->isXhr()) { - if ($this->params->get('addSimulation')) { - $this->setAutorefreshInterval(30); - } else { - $this->setAutorefreshInterval(10); - } - } else { + if (! $this->isXhr()) { // This will trigger the very first XHR refresh immediately on page // load. Please not that this may hammer the server in case we would // decide to use autorefreshInterval for HTML meta-refreshes also. $this->setAutorefreshInterval(1); + return; + } + + if ($this->params->get('action')) { + $this->setAutorefreshInterval(45); + } else { + $this->setAutorefreshInterval(10); + } + } + + protected function showWarnings(BusinessProcess $bp) + { + if ($bp->hasWarnings()) { + $ul = Element::create('ul', array('class' => 'warning')); + foreach ($bp->getWarnings() as $warning) { + $ul->createElement('li')->addContent($warning); + } + + return $ul; + } else { + return null; + } + } + + + public function showHints(BusinessProcess $bp) + { + $ul = Element::create('ul', array('class' => 'error')); + foreach ($bp->getErrors() as $error) { + $ul->createElement('li')->addContent($error); + } + if ($bp->hasChanges()) { + $ul->createElement('li')->setSeparator(' ')->addContent(sprintf( + $this->translate('This process has %d pending change(s).'), + $bp->countChanges() + ))->addContent( + Link::create( + $this->translate('Store'), + 'businessprocess/process/config', + array('config' => $bp->getName()) + ) + )->addContent( + Link::create( + $this->translate('Dismiss'), + $this->url()->with('dismissChanges', true), + null + ) + ); + } + + if ($bp->hasSimulations()) { + $ul->createElement('li')->setSeparator(' ')->addContent(sprintf( + $this->translate('This process shows %d simulated state(s).'), + $bp->countSimulations() + ))->addContent(Link::create( + $this->translate('Dismiss'), + $this->url()->with('dismissSimulations', true) + )); + } + + if ($ul->hasContent()) { + return $ul; + } else { + return null; } } @@ -277,7 +327,7 @@ class ProcessController extends Controller $this->actions()->add( Link::create( - $this->translate('Store'), + $this->translate('Config'), 'businessprocess/process/config', $this->currentProcessParams(), array( From f8a9c082323b4b9fed70e4794685e9ef436f21de Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 30 Nov 2016 14:38:17 +0100 Subject: [PATCH 125/256] css: add form css --- public/css/module.less | 231 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 227 insertions(+), 4 deletions(-) diff --git a/public/css/module.less b/public/css/module.less index c6a4c91..ce1b038 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -1,3 +1,10 @@ +a:focus { + outline: none; + text-decoration: underline; + &::before { + text-decoration: none; + } +} .action-bar a { color: @icinga-blue; @@ -362,10 +369,7 @@ div.knightrider table.bp { width: 100%; height: 12em; box-sizing: border-box; - &:focus { - outline: none; - text-decoration: underline; - } + i { float: left; font-size: 2.5em; @@ -645,3 +649,222 @@ table.sourcecode { background-color: #dfd; } } + +/** Forms stolen from director **/ +.content form { + margin-bottom: 2em; +} + +form input[type=submit] { + .button(); + border-width: 1px; + margin-top: 0.5em; +} + +form input[type=submit]:first-of-type { + border-width: 2px; +} + +form p.description { + display: none; + padding-bottom: 1em; + color: #888; + padding-left: 32%; + font-style: italic; +} + +form ul.form-errors { + margin-bottom: 0.5em; + ul.errors li { + background: @color-critical; + font-weight: bold; + padding: 0.5em 1em; + color: white; + } +} + +fieldset { + margin: 0; + padding: 0; + border: none; + + legend { + margin: 0 0 0.5em 0; + font-size: 1.2em; + color: @icinga-blue; + border-bottom: 1px solid @gray-light; + display: block; + width: 100%; + } +} + +select::-ms-expand, input::-ms-expand, textarea::-ms-expand { /* for IE 11 */ + display: none; +} + +select { + border: 1px solid #ddd; + cursor: pointer; + background: none; +} + +input[type=text], textarea, select { + max-width: 36em; + min-width: 20em; + width: 63%; + padding-left: 0.5em; + border-style: solid; + border-color: transparent; + border-bottom-color: @gray-light; + border-width: 1px 1px 1px 3px; + + &.search { + background-color: transparent; + padding-left: 2em; + } +} + +select::-moz-focus-inner { border: 0; } + +select, input[type=text], textarea { + &:hover { + border-style: dotted solid dotted solid; + border-color: @gray-light; + } + + &:focus, &:focus:hover { + border-style: solid; + border-color: @icinga-blue; + outline: none; + } +} + +select[value=""] { + color: blue; + border: 1px solid #666; +} + +select option { + color: inherit; + padding-left: 0.5em; +} + +select option[value=""] { + color: #aaa; +} + + +fieldset { + legend { + padding-left: 1em; + cursor: pointer; + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + + &:hover { + border-style: dotted; + } + + &::before { + // icon: down-dir + font-family: 'ifont'; + content: '\e81d'; + margin-left: -1em; + padding-top: 0.25em; + float: left; + color: inherit; + } + } + + &.collapsed { + + legend { + margin: 0; + } + + dd, dt { + display: none; + } + + legend::before { + // icon: right-dir + content: '\e820'; + } + + } +} + +form dt label { + width: auto; + font-weight: normal; + font-size: inherit; + + &.required { + &::after { + content: '*' + } + } +} + +form dd { + display: inline; + min-height: 2.5em; + vertical-align: top; + margin: 0; +} + +form dt { + display: inline-block; + vertical-align: top; + min-width: 12em; + min-height: 2.5em; + width: 30%; +} + +form dd:after { + display: block; + content: ''; +} + +form textarea { + height: auto; +} + +form dd ul.errors { + list-style-type: none; + padding-left: 0.3em; + font-size: 0.857em; + + li { + color: @colorCritical; + padding: 0.3em; + } +} + +form { + #_FAKE_SUBMIT { + position: absolute; + left: -100%; + } +} + +form div.hint { + padding: 1em; + background-color: #f2f4fd; + border: 1px solid lightgrey; + margin: 1em 0; + + pre { + font-style: normal; + background-color: white; + font-size: 1.25em; + margin: 0; + padding: 1em; + } +} + + + +/** End of forms **/ From a5eaa19bcee622b32b5209ec560bf169368671d3 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 30 Nov 2016 14:51:09 +0100 Subject: [PATCH 126/256] ProcessController: fix tree view link --- application/controllers/ProcessController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 14ff6c7..d7d84e7 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -283,8 +283,8 @@ class ProcessController extends Controller $this->actions()->add( Link::create( $this->translate('Tree'), - 'businessprocess/process/show', - $this->currentProcessParams(), + $this->url()->with('mode', 'tree'), + null, array('class' => 'icon-sitemap') ) ); From 03605cac527afc0a96436944a6404d81202f6309 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 30 Nov 2016 14:53:40 +0100 Subject: [PATCH 127/256] TreeRenderer: open nodes in left container --- library/Businessprocess/Renderer/TreeRenderer.php | 1 + 1 file changed, 1 insertion(+) diff --git a/library/Businessprocess/Renderer/TreeRenderer.php b/library/Businessprocess/Renderer/TreeRenderer.php index 1ef036d..ee85465 100644 --- a/library/Businessprocess/Renderer/TreeRenderer.php +++ b/library/Businessprocess/Renderer/TreeRenderer.php @@ -148,6 +148,7 @@ class TreeRenderer extends Renderer } $link = $node->getLink(); + $link->attributes()->set('data-base-target', '_next'); $link->addContent($this->getNodeIcons($node)); if ($node->hasChildren()) { From a784b384f450fb3a95524237a21a19d02a7285da Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 7 Dec 2016 16:02:06 +0100 Subject: [PATCH 128/256] NodeCreateAction: allow to add root nodes --- application/forms/AddNodeForm.php | 32 +++++++++++++++++-- .../Modification/NodeCreateAction.php | 17 ++++++---- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/application/forms/AddNodeForm.php b/application/forms/AddNodeForm.php index 1627a1b..ecb583c 100644 --- a/application/forms/AddNodeForm.php +++ b/application/forms/AddNodeForm.php @@ -41,6 +41,9 @@ class AddNodeForm extends QuickForm case 'process': $this->selectProcess(); break; + case 'new-process': + $this->addNewProcess(); + break; case null: $this->setSubmitLabel($this->translate('Next')); return; @@ -98,7 +101,7 @@ class AddNodeForm extends QuickForm ) )); - $this->addElement('text', 'url', array( + $this->addElement('text', 'infoUrl', array( 'label' => $this->translate('Info URL'), 'description' => $this->translate( 'URL pointing to more information about this node' @@ -116,7 +119,12 @@ class AddNodeForm extends QuickForm $types['host'] = $this->translate('Host'); $types['service'] = $this->translate('Service'); } - $types['process'] = $this->translate('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'), @@ -124,6 +132,7 @@ class AddNodeForm extends QuickForm 'description' => $this->translate( 'The node type you want to add' ), + 'ignore' => true, 'class' => 'autosubmit', 'multiOptions' => $this->optionalEnum($types) )); @@ -269,6 +278,11 @@ class AddNodeForm extends QuickForm return $services; } + protected function hasProcesses() + { + return count($this->enumProcesses()) > 0; + } + protected function enumProcesses() { $list = array(); @@ -320,7 +334,19 @@ class AddNodeForm extends QuickForm public function onSuccess() { $changes = ProcessChanges::construct($this->bp, $this->session); - $changes->addChildrenToNode($this->parent, $this->bp); + 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']); + $changes->createNode($this->getValue('name'), $properties); + break; + } + parent::onSuccess(); } } diff --git a/library/Businessprocess/Modification/NodeCreateAction.php b/library/Businessprocess/Modification/NodeCreateAction.php index 4d970dc..04ab474 100644 --- a/library/Businessprocess/Modification/NodeCreateAction.php +++ b/library/Businessprocess/Modification/NodeCreateAction.php @@ -58,12 +58,12 @@ class NodeCreateAction extends NodeAction } /** - * @param stdClass $properties + * @param array $properties * @return $this */ - public function setProperties(stdClass $properties) + public function setProperties($properties) { - $this->properties = $properties; + $this->properties = (array) $properties; return $this; } @@ -82,11 +82,16 @@ class NodeCreateAction extends NodeAction { $name = $this->getNodeName(); - $node = new BpNode($bp, (object) array( + $properties = array( 'name' => $name, 'operator' => $this->properties['operator'], - 'child_names' => $this->properties['childNames'] - )); + ); + if (array_key_exists('childNames', $this->properties)) { + $properties['child_names'] = $this->properties['childNames']; + } else { + $properties['child_names'] = array(); + } + $node = new BpNode($bp, (object) $properties); foreach ($this->getProperties() as $key => $val) { $func = 'set' . ucfirst($key); From a7e9d815ebe794a74dc5e220a626bcaa6bdf16e2 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 7 Dec 2016 22:10:35 +0100 Subject: [PATCH 129/256] Node: try to avoid side-effects when... ...dealing with state mappings --- library/Businessprocess/HostNode.php | 6 +++--- library/Businessprocess/Node.php | 28 ++++++++++++++++------------ 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/library/Businessprocess/HostNode.php b/library/Businessprocess/HostNode.php index d28ec2e..b40e0f3 100644 --- a/library/Businessprocess/HostNode.php +++ b/library/Businessprocess/HostNode.php @@ -7,21 +7,21 @@ use Icinga\Module\Businessprocess\Web\Url; class HostNode extends MonitoredNode { - protected static $sortStateToStateMap = array( + protected $sortStateToStateMap = array( 4 => self::ICINGA_DOWN, 3 => self::ICINGA_UNREACHABLE, 1 => self::ICINGA_PENDING, 0 => self::ICINGA_UP ); - protected static $stateToSortStateMap = array( + protected $stateToSortStateMap = array( self::ICINGA_PENDING => 1, self::ICINGA_UNREACHABLE => 3, self::ICINGA_DOWN => 4, self::ICINGA_UP => 0, ); - protected static $state_names = array( + protected $stateNames = array( 'UP', 'DOWN', 'UNREACHABLE', diff --git a/library/Businessprocess/Node.php b/library/Businessprocess/Node.php index 8ba84f1..e8109ea 100644 --- a/library/Businessprocess/Node.php +++ b/library/Businessprocess/Node.php @@ -22,7 +22,7 @@ abstract class Node const ICINGA_UNREACHABLE = 2; const ICINGA_PENDING = 99; - protected static $sortStateToStateMap = array( + protected $sortStateToStateMap = array( 4 => self::ICINGA_CRITICAL, 3 => self::ICINGA_UNKNOWN, 2 => self::ICINGA_WARNING, @@ -30,7 +30,7 @@ abstract class Node 0 => self::ICINGA_OK ); - protected static $stateToSortStateMap = array( + protected $stateToSortStateMap = array( self::ICINGA_PENDING => 1, self::ICINGA_UNKNOWN => 3, self::ICINGA_CRITICAL => 4, @@ -94,7 +94,7 @@ abstract class Node protected $className = 'unknown'; - protected static $state_names = array( + protected $stateNames = array( 'OK', 'WARNING', 'CRITICAL', @@ -171,16 +171,17 @@ abstract class Node public function getStateName($state = null) { + $states = $this->enumStateNames(); if ($state === null) { - return static::$state_names[ $this->getState() ]; + return $states[ $this->getState() ]; } else { - return static::$state_names[ $state ]; + return $states[ $state ]; } } public function enumStateNames() { - return static::$state_names; + return $this->stateNames; } public function getState() @@ -294,19 +295,22 @@ abstract class Node protected function stateToSortState($state) { - if (array_key_exists($state, static::$stateToSortStateMap)) { - return static::$stateToSortStateMap[$state]; + if (array_key_exists($state, $this->stateToSortStateMap)) { + return $this->stateToSortStateMap[$state]; } - throw new ProgrammingError('Got invalid state: %s', var_export($state, 1)); + throw new ProgrammingError( + 'Got invalid state for node %s: %s', + $this->getName(), + var_export($state, 1) . var_export($this->stateToSortStateMap, 1) + ); } protected function sortStateTostate($sortState) { $sortState = $sortState >> self::SHIFT_FLAGS; - - if (array_key_exists($sortState, static::$sortStateToStateMap)) { - return static::$sortStateToStateMap[$sortState]; + if (array_key_exists($sortState, $this->sortStateToStateMap)) { + return $this->sortStateToStateMap[$sortState]; } throw new ProgrammingError('Got invalid sorting state %s', $sortState); From 654f905cf8d8e023e3c8632f493136a59d1d54c1 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 7 Dec 2016 23:11:15 +0100 Subject: [PATCH 130/256] ProcessChanges: adjust method signature --- library/Businessprocess/Modification/ProcessChanges.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/Businessprocess/Modification/ProcessChanges.php b/library/Businessprocess/Modification/ProcessChanges.php index 2adcb25..0fb52ae 100644 --- a/library/Businessprocess/Modification/ProcessChanges.php +++ b/library/Businessprocess/Modification/ProcessChanges.php @@ -70,10 +70,10 @@ class ProcessChanges * * @return $this */ - public function addChildrenToNode(Node $node, $children) + public function addChildrenToNode($children, Node $node = null) { $action = new NodeAddChildrenAction($node); - $action->setChildren($node, $children); + $action->setChildren($children); return $this->push($action); } From 46c94ef68f1b6158b4b69aa01cbca428e43107d3 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 7 Dec 2016 23:12:11 +0100 Subject: [PATCH 131/256] AddNodeForm: use dedicated enums for different... ...host lists --- application/forms/AddNodeForm.php | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/application/forms/AddNodeForm.php b/application/forms/AddNodeForm.php index ecb583c..17be12b 100644 --- a/application/forms/AddNodeForm.php +++ b/application/forms/AddNodeForm.php @@ -169,7 +169,7 @@ class AddNodeForm extends QuickForm 'required' => true, 'ignore' => true, 'class' => 'autosubmit', - 'multiOptions' => $this->optionalEnum($this->enumHostList()), + 'multiOptions' => $this->optionalEnum($this->enumHostForServiceList()), )); } @@ -223,10 +223,10 @@ class AddNodeForm extends QuickForm } /** - * @param BpNode $node + * @param BpNode|null $node * @return $this */ - public function setParentNode(BpNode $node) + public function setParentNode(BpNode $node = null) { $this->parent = $node; return $this; @@ -250,6 +250,18 @@ class AddNodeForm extends QuickForm 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( @@ -258,7 +270,13 @@ class AddNodeForm extends QuickForm // fetchPairs doesn't seem to work when using the same column with // different aliases twice - return array_combine((array) $names, (array) $names); + $res = array(); + $suffix = ';Hoststatus'; + foreach ($names as $name) { + $res[$name . $suffix] = $name; + } + + return $res; } protected function enumServiceList($host) From a2ea908480e54e53a0af06c79b15806ab6c044f4 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 7 Dec 2016 23:15:12 +0100 Subject: [PATCH 132/256] Controller: just some comments --- library/Businessprocess/Controller.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/library/Businessprocess/Controller.php b/library/Businessprocess/Controller.php index 0ec129c..0d9e119 100644 --- a/library/Businessprocess/Controller.php +++ b/library/Businessprocess/Controller.php @@ -16,6 +16,7 @@ use Icinga\Web\Controller as ModuleController; use Icinga\Web\Notification; use Icinga\Web\View; use Icinga\Web\Widget; +use Icinga\Web\Widget\Tabs; class Controller extends ModuleController { @@ -91,6 +92,9 @@ class Controller extends ModuleController return $this->view->actions; } + /** + * @return Controls + */ protected function controls() { if ($this->view->controls === null) { @@ -112,6 +116,10 @@ class Controller extends ModuleController return $this->view->content; } + /** + * @param $label + * @return Tabs + */ protected function singleTab($label) { $tabs = Widget::create('tabs')->add( @@ -126,6 +134,9 @@ class Controller extends ModuleController return $tabs; } + /** + * @return Tabs + */ protected function defaultTab() { return $this->singleTab($this->translate('Business Process')); From 7f3c16466a979a3022ca4f8c2e342ed23c6a305e Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 7 Dec 2016 23:15:56 +0100 Subject: [PATCH 133/256] NodeAction: make node optional This is required for root nodes --- library/Businessprocess/Modification/NodeAction.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/library/Businessprocess/Modification/NodeAction.php b/library/Businessprocess/Modification/NodeAction.php index 98329bd..c895145 100644 --- a/library/Businessprocess/Modification/NodeAction.php +++ b/library/Businessprocess/Modification/NodeAction.php @@ -32,9 +32,11 @@ abstract class NodeAction * * @param Node|string $node */ - public function __construct($node) + public function __construct($node = null) { - $this->nodeName = (string) $node; + if ($node !== null) { + $this->nodeName = (string) $node; + } } /** @@ -63,6 +65,11 @@ abstract class NodeAction return $this->nodeName; } + public function hasNode() + { + return $this->nodeName !== null; + } + /** * Whether this is an instance of a given action name * From 1980c42ca62e57c92d986a8fa8b99e6ca2ebc726 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 7 Dec 2016 23:17:01 +0100 Subject: [PATCH 134/256] BusinessProcess: do not use deprecated method... ...from the monitoring module --- library/Businessprocess/BusinessProcess.php | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index 555d48f..875954c 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -218,7 +218,7 @@ class BusinessProcess public function getBackend() { if ($this->backend === null) { - $this->backend = MonitoringBackend::createBackend( + $this->backend = MonitoringBackend::instance( $this->getBackendName() ); } @@ -393,6 +393,7 @@ class BusinessProcess } else { $key .= ';Hoststatus'; } + // We fetch more states than we need, so skip unknown ones if (! $this->hasNode($key)) { return; @@ -542,7 +543,7 @@ class BusinessProcess return $this; } - public function addNode($name, Node $node) + public function addNode($name, BpNode $node) { if (array_key_exists($name, $this->nodes)) { $this->warn( @@ -651,22 +652,15 @@ class BusinessProcess { $args = func_get_args(); array_shift($args); - if (isset($this->parsing_line_number)) { - $this->warnings[] = sprintf( - $this->translate('Parser waring on %s:%s: %s'), - $this->filename, - $this->parsing_line_number, - vsprintf($msg, $args) - ); - } else { - $this->warnings[] = vsprintf($msg, $args); - } + $this->warnings[] = vsprintf($msg, $args); } /** * @param string $msg,... * * @return $this + * + * @throws IcingaException */ public function addError($msg) { From 590bdd35b152188bfadcc46937f79a51fc2e1796 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 7 Dec 2016 23:20:52 +0100 Subject: [PATCH 135/256] AddNodeForm: workaround for session shutdown issue --- application/forms/AddNodeForm.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/application/forms/AddNodeForm.php b/application/forms/AddNodeForm.php index 17be12b..322c293 100644 --- a/application/forms/AddNodeForm.php +++ b/application/forms/AddNodeForm.php @@ -365,6 +365,10 @@ class AddNodeForm extends QuickForm break; } + // Trigger session desctruction 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(); } } From 0380e46552eea939467c385edd77bc4adda6a4b6 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 8 Dec 2016 10:11:46 +0100 Subject: [PATCH 136/256] Form: upgrade to latest forms from Director --- application/views/helpers/FormSimpleNote.php | 15 + .../Web/Form/Element/FormElement.php | 9 + .../Web/Form/Element/SimpleNote.php | 22 ++ .../Web/Form/QuickBaseForm.php | 166 +++++++++++ .../Businessprocess/Web/Form/QuickForm.php | 260 +++++++++++------- public/css/module.less | 253 ++++++++++------- 6 files changed, 531 insertions(+), 194 deletions(-) create mode 100644 application/views/helpers/FormSimpleNote.php create mode 100644 library/Businessprocess/Web/Form/Element/FormElement.php create mode 100644 library/Businessprocess/Web/Form/Element/SimpleNote.php create mode 100644 library/Businessprocess/Web/Form/QuickBaseForm.php diff --git a/application/views/helpers/FormSimpleNote.php b/application/views/helpers/FormSimpleNote.php new file mode 100644 index 0000000..d8315f4 --- /dev/null +++ b/application/views/helpers/FormSimpleNote.php @@ -0,0 +1,15 @@ +_getInfo($name, $value); + extract($info); // name, value, attribs, options, listsep, disable + return $value; + } +} diff --git a/library/Businessprocess/Web/Form/Element/FormElement.php b/library/Businessprocess/Web/Form/Element/FormElement.php new file mode 100644 index 0000000..7647a5e --- /dev/null +++ b/library/Businessprocess/Web/Form/Element/FormElement.php @@ -0,0 +1,9 @@ +callZfConstructor($this->handleOptions($options)) + ->initializePrefixPaths(); + } + + protected function callZfConstructor($options = null) + { + parent::__construct($options); + return $this; + } + + protected function initializePrefixPaths() + { + $this->addPrefixPathsForBusinessprocess(); + if ($this->icingaModule && $this->icingaModuleName !== 'businessprocess') { + $this->addPrefixPathsForModule($this->icingaModule); + } + } + + protected function addPrefixPathsForBusinessprocess() + { + $module = Icinga::app() + ->getModuleManager() + ->loadModule('businessprocess') + ->getModule('businessprocess'); + + $this->addPrefixPathsForModule($module); + } + + public function addPrefixPathsForModule(Module $module) + { + $basedir = sprintf( + '%s/%s/Web/Form', + $module->getLibDir(), + ucfirst($module->getName()) + ); + + $this->addPrefixPaths(array( + array( + 'prefix' => __NAMESPACE__ . '\\Element\\', + 'path' => $basedir . '/Element', + 'type' => static::ELEMENT + ) + )); + + return $this; + } + + public function addHidden($name, $value = null) + { + $this->addElement('hidden', $name); + $el = $this->getElement($name); + $el->setDecorators(array('ViewHelper')); + if ($value !== null) { + $this->setDefault($name, $value); + $el->setValue($value); + } + + return $this; + } + + // TODO: Should be an element + public function addHtmlHint($html, $options = array()) + { + return $this->addHtml('
' . $html . '
', $options); + } + + public function addHtml($html, $options = array()) + { + if (array_key_exists('name', $options)) { + $name = $options['name']; + unset($options['name']); + } else { + $name = '_HINT' . ++$this->hintCount; + } + + $this->addElement('simpleNote', $name, $options); + $this->getElement($name) + ->setValue($html) + ->setIgnore(true) + ->setDecorators(array('ViewHelper')); + + return $this; + } + + public function optionalEnum($enum, $nullLabel = null) + { + if ($nullLabel === null) { + $nullLabel = $this->translate('- please choose -'); + } + + return array(null => $nullLabel) + $enum; + } + + protected function handleOptions($options = null) + { + if ($options === null) { + return $options; + } + + if (array_key_exists('icingaModule', $options)) { + /** @var Module icingaModule */ + $this->icingaModule = $options['icingaModule']; + $this->icingaModuleName = $this->icingaModule->getName(); + unset($options['icingaModule']); + } + + return $options; + } + + public function setIcingaModule(Module $module) + { + $this->icingaModule = $module; + return $this; + } + + protected function loadForm($name, Module $module = null) + { + if ($module === null) { + $module = $this->icingaModule; + } + + return FormLoader::load($name, $module); + } + + protected function valueIsEmpty($value) + { + if (is_array($value)) { + return empty($value); + } + + return strlen($value) === 0; + } + + public function translate($string) + { + if ($this->icingaModuleName === null) { + return t($string); + } else { + return mt($this->icingaModuleName, $string); + } + } +} diff --git a/library/Businessprocess/Web/Form/QuickForm.php b/library/Businessprocess/Web/Form/QuickForm.php index 861a4db..c657745 100644 --- a/library/Businessprocess/Web/Form/QuickForm.php +++ b/library/Businessprocess/Web/Form/QuickForm.php @@ -3,18 +3,17 @@ namespace Icinga\Module\Businessprocess\Web\Form; use Icinga\Application\Icinga; -use Icinga\Application\Modules\Module; use Icinga\Exception\ProgrammingError; use Icinga\Web\Notification; use Icinga\Web\Request; +use Icinga\Web\Response; use Icinga\Web\Url; use Exception; -use Zend_Form; /** * QuickForm wants to be a base class for simple forms */ -abstract class QuickForm extends Zend_Form +abstract class QuickForm extends QuickBaseForm { const ID = '__FORM_NAME'; @@ -53,28 +52,37 @@ abstract class QuickForm extends Zend_Form protected $submitButtonName; + protected $deleteButtonName; + + protected $fakeSubmitButtonName; + /** * Whether form elements have already been created */ protected $didSetup = false; - /** - * The Icinga module this form belongs to. Usually only set if the - * form is initialized through the FormLoader - */ - protected $icingaModule; - - protected $icingaModuleName; - - protected $hintCount = 0; + protected $isApiRequest = false; public function __construct($options = null) { - parent::__construct($this->handleOptions($options)); + parent::__construct($options); + $this->setMethod('post'); + $this->getActionFromRequest() + ->createIdElement() + ->regenerateCsrfToken() + ->setPreferredDecorators(); + } + + protected function getActionFromRequest() + { $this->setAction(Url::fromRequest()); - $this->createIdElement(); - $this->regenerateCsrfToken(); + return $this; + } + + protected function setPreferredDecorators() + { + $this->setAttrib('class', 'autofocus'); $this->setDecorators( array( 'Description', @@ -83,31 +91,41 @@ abstract class QuickForm extends Zend_Form 'Form' ) ); - } - protected function handleOptions($options = null) - { - if ($options === null) { - return $options; - } - - if (array_key_exists('icingaModule', $options)) { - $this->icingaModule = $options['icingaModule']; - $this->icingaModuleName = $this->icingaModule->getName(); - unset($options['icingaModule']); - } - - return $options; + return $this; } protected function addSubmitButtonIfSet() { - if (false !== ($label = $this->getSubmitLabel())) { - $el = $this->createElement('submit', $label)->setLabel($label)->setDecorators(array('ViewHelper')); - $this->submitButtonName = $el->getName(); - $this->addElement($el); + if (false === ($label = $this->getSubmitLabel())) { + return; } + if ($this->submitButtonName && $el = $this->getElement($this->submitButtonName)) { + return; + } + + $el = $this->createElement('submit', $label) + ->setLabel($label) + ->setDecorators(array('ViewHelper')); + $this->submitButtonName = $el->getName(); + $this->addElement($el); + + $fakeEl = $this->createElement('submit', '_FAKE_SUBMIT') + ->setLabel($label) + ->setDecorators(array('ViewHelper')); + $this->fakeSubmitButtonName = $fakeEl->getName(); + $this->addElement($fakeEl); + + $this->addDisplayGroup( + array($this->fakeSubmitButtonName), + 'fake_button', + array( + 'decorators' => array('FormElements'), + 'order' => 1, + ) + ); + $grp = array( $this->submitButtonName, $this->deleteButtonName @@ -115,17 +133,32 @@ abstract class QuickForm extends Zend_Form $this->addDisplayGroup($grp, 'buttons', array( 'decorators' => array( 'FormElements', + array('HtmlTag', array('tag' => 'dl')), 'DtDdWrapper', ), 'order' => 1000, )); } + protected function addSimpleDisplayGroup($elements, $name, $options) + { + if (! array_key_exists('decorators', $options)) { + $options['decorators'] = array( + 'FormElements', + array('HtmlTag', array('tag' => 'dl')), + 'Fieldset', + ); + } + return $this->addDisplayGroup($elements, $name, $options); + + } + protected function createIdElement() { $this->detectName(); $this->addHidden(self::ID, $this->getName()); $this->getElement(self::ID)->setIgnore(true); + return $this; } public function getSentValue($name, $default = null) @@ -153,13 +186,15 @@ abstract class QuickForm extends Zend_Form return $this; } - protected function loadForm($name, Module $module = null) + public function setApiRequest($isApiRequest = true) { - if ($module === null) { - $module = $this->icingaModule; - } + $this->isApiRequest = $isApiRequest; + return $this; + } - return FormLoader::load($name, $module); + public function isApiRequest() + { + return $this->isApiRequest; } public function regenerateCsrfToken() @@ -179,43 +214,6 @@ abstract class QuickForm extends Zend_Form return $this; } - public function addHidden($name, $value = null) - { - $this->addElement('hidden', $name); - $el = $this->getElement($name); - $el->setDecorators(array('ViewHelper')); - if ($value !== null) { - $this->setDefault($name, $value); - $el->setValue($value); - } - - return $this; - } - - public function addHtmlHint($html, $options = array()) - { - return $this->addHtml('
' . $html . '
', $options); - } - - public function addHtml($html, $options = array()) - { - $name = '_HINT' . ++$this->hintCount; - $this->addElement('note', $name, $options); - $this->getElement($name) - ->setValue($html) - ->setIgnore(true) - ->removeDecorator('Label'); - - return $this; - } - - public function optionalEnum($enum) - { - return array( - null => $this->translate('- please choose -') - ) + $enum; - } - public function setSuccessUrl($url, $params = null) { if (! $url instanceof Url) { @@ -238,6 +236,10 @@ abstract class QuickForm extends Zend_Form return $url; } + protected function beforeSetup() + { + } + public function setup() { } @@ -255,35 +257,49 @@ abstract class QuickForm extends Zend_Form return parent::setAction($action); } - public function setIcingaModule(Module $module) - { - $this->icingaModule = $module; - return $this; - } - public function hasBeenSubmitted() { if ($this->hasBeenSubmitted === null) { $req = $this->getRequest(); if ($req->isPost()) { - $post = $req->getPost(); - $name = $this->submitButtonName; - - if ($name === null) { - $this->hasBeenSubmitted = $this->hasBeenSent(); - } else { - $el = $this->getElement($name); - $this->hasBeenSubmitted = array_key_exists($name, $post) - && $post[$name] === $this->getSubmitLabel(); + if (! $this->hasSubmitButton()) { + return $this->hasBeenSubmitted = $this->hasBeenSent(); } + + $this->hasBeenSubmitted = $this->pressedButton( + $this->fakeSubmitButtonName, + $this->getSubmitLabel() + ) || $this->pressedButton( + $this->submitButtonName, + $this->getSubmitLabel() + ); } else { - $this->hasBeenSubmitted === false; + $this->hasBeenSubmitted = false; } } return $this->hasBeenSubmitted; } + protected function hasSubmitButton() + { + return $this->submitButtonName !== null; + } + + protected function pressedButton($name, $label) + { + $req = $this->getRequest(); + if (! $req->isPost()) { + return false; + } + + $req = $this->getRequest(); + $post = $req->getPost(); + + return array_key_exists($name, $post) + && $post[$name] === $label; + } + protected function beforeValidation($data = array()) { } @@ -291,6 +307,7 @@ abstract class QuickForm extends Zend_Form public function prepareElements() { if (! $this->didSetup) { + $this->beforeSetup(); $this->setup(); $this->addSubmitButtonIfSet(); $this->onSetup(); @@ -302,19 +319,23 @@ abstract class QuickForm extends Zend_Form public function handleRequest(Request $request = null) { - if ($request !== null) { + if ($request === null) { + $request = $this->getRequest(); + } else { $this->setRequest($request); } + $this->prepareElements(); + if ($this->hasBeenSent()) { - $post = $this->getRequest()->getPost(); + $post = $request->getPost(); if ($this->hasBeenSubmitted()) { $this->beforeValidation($post); if ($this->isValid($post)) { try { $this->onSuccess(); } catch (Exception $e) { - $this->addError($e->getMessage()); + $this->addException($e); $this->onFailure(); } } else { @@ -330,12 +351,21 @@ abstract class QuickForm extends Zend_Form return $this; } - public function translate($string) + public function addException(Exception $e, $elementName = null) { - if ($this->icingaModuleName === null) { - return t($string); + $file = preg_split('/[\/\\\]/', $e->getFile(), -1, PREG_SPLIT_NO_EMPTY); + $file = array_pop($file); + $msg = sprintf( + '%s (%s:%d)', + $e->getMessage(), + $file, + $e->getLine() + ); + + if ($el = $this->getElement($elementName)) { + $el->addError($msg); } else { - return mt($this->icingaModuleName, $string); + $this->addError($msg); } } @@ -363,6 +393,12 @@ abstract class QuickForm extends Zend_Form public function redirectOnSuccess($message = null) { + if ($this->isApiRequest()) { + // TODO: Set the status line message? + $this->successMessage = $this->getSuccessMessage($message); + return; + } + $url = $this->getSuccessUrl(); $this->notifySuccess($this->getSuccessMessage($message)); $this->redirectAndExit($url); @@ -389,7 +425,15 @@ abstract class QuickForm extends Zend_Form protected function redirectAndExit($url) { - Icinga::app()->getFrontController()->getResponse()->redirectAndExit($url); + /** @var Response $response */ + $response = Icinga::app()->getFrontController()->getResponse(); + $response->redirectAndExit($url); + } + + protected function setHttpResponseCode($code) + { + Icinga::app()->getFrontController()->getResponse()->setHttpResponseCode($code); + return $this; } protected function onRequest() @@ -408,10 +452,15 @@ abstract class QuickForm extends Zend_Form return $this; } + /** + * @return Request + */ public function getRequest() { if ($this->request === null) { - $this->setRequest(Icinga::app()->getFrontController()->getRequest()); + /** @var Request $request */ + $request = Icinga::app()->getFrontController()->getRequest(); + $this->setRequest($request); } return $this->request; } @@ -419,13 +468,20 @@ abstract class QuickForm extends Zend_Form public function hasBeenSent() { if ($this->hasBeenSent === null) { - $req = $this->getRequest(); + + /** @var Request $req */ + if ($this->request === null) { + $req = Icinga::app()->getFrontController()->getRequest(); + } else { + $req = $this->request; + } + if ($req->isPost()) { $post = $req->getPost(); $this->hasBeenSent = array_key_exists(self::ID, $post) && $post[self::ID] === $this->getName(); } else { - $this->hasBeenSent === false; + $this->hasBeenSent = false; } } diff --git a/public/css/module.less b/public/css/module.less index ce1b038..df742d3 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -652,29 +652,69 @@ table.sourcecode { /** Forms stolen from director **/ .content form { - margin-bottom: 2em; + margin-bottom: 2em; } +.content form.inline { + margin: 0; +} + +.invisible { + position: absolute; + left: -100%; +} + +form input[type=file] { + background-color: white; + padding-right: 1em; +} + + form input[type=submit] { - .button(); - border-width: 1px; - margin-top: 0.5em; + .button(); + border-width: 1px; + margin-top: 0.5em; + + &:disabled { + border-color: @gray-light; + background-color: @gray-light; + color: #fff; + } } form input[type=submit]:first-of-type { border-width: 2px; } +form input[type=submit].link-button { + color: @icinga-blue; + background: none; + border: none; + padding: 0; + + text-align: left; + + &:hover { + text-decoration: underline; + } +} + form p.description { - display: none; - padding-bottom: 1em; - color: #888; - padding-left: 32%; - font-style: italic; + padding: 1em 1em; + margin: 0; + font-style: italic; + width: 100%; +} + +input, select, select option, textarea { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; } form ul.form-errors { margin-bottom: 0.5em; + ul.errors li { background: @color-critical; font-weight: bold; @@ -683,21 +723,6 @@ form ul.form-errors { } } -fieldset { - margin: 0; - padding: 0; - border: none; - - legend { - margin: 0 0 0.5em 0; - font-size: 1.2em; - color: @icinga-blue; - border-bottom: 1px solid @gray-light; - display: block; - width: 100%; - } -} - select::-ms-expand, input::-ms-expand, textarea::-ms-expand { /* for IE 11 */ display: none; } @@ -708,24 +733,54 @@ select { background: none; } -input[type=text], textarea, select { +input[type=text], input[type=password], textarea, select { max-width: 36em; min-width: 20em; - width: 63%; + width: 100%; + line-height: 2em; + height: 2.4em; padding-left: 0.5em; border-style: solid; border-color: transparent; - border-bottom-color: @gray-light; + border-bottom-color: @gray-lighter; border-width: 1px 1px 1px 3px; + background-color: white; &.search { - background-color: transparent; + background: transparent url("../img/icons/search.png") no-repeat scroll 0.5em center / 1em 1em; padding-left: 2em; } } +select[multiple] { + height: auto; +} + +select option { + height: 2em; + padding-top: 0.3em; +} + +select[multiple=multiple] { + height: auto; +} + +label { + line-height: 2em; +} + +form dl { + margin: 0; + padding: 0; +} + select::-moz-focus-inner { border: 0; } +select:-moz-focusring { + color: transparent; + text-shadow: 0 0 0 #000; +} + select, input[type=text], textarea { &:hover { border-style: dotted solid dotted solid; @@ -742,58 +797,18 @@ select, input[type=text], textarea { select[value=""] { color: blue; border: 1px solid #666; + background-color: white; } select option { color: inherit; padding-left: 0.5em; + background-color: white; } select option[value=""] { color: #aaa; -} - - -fieldset { - legend { - padding-left: 1em; - cursor: pointer; - user-select: none; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - - &:hover { - border-style: dotted; - } - - &::before { - // icon: down-dir - font-family: 'ifont'; - content: '\e81d'; - margin-left: -1em; - padding-top: 0.25em; - float: left; - color: inherit; - } - } - - &.collapsed { - - legend { - margin: 0; - } - - dd, dt { - display: none; - } - - legend::before { - // icon: right-dir - content: '\e820'; - } - - } + background-color: white; } form dt label { @@ -806,21 +821,77 @@ form dt label { content: '*' } } + + &:hover { + text-decoration: underline; + cursor: pointer; + } +} + +form fieldset { + min-width: 36em; +} + +form dd input.related-action[type='submit'] { + display: none; +} + +form dd.active li.active input.related-action[type='submit'] { + display: inline-block; +} + +form dd.active { + p.description { + color: inherit; + font-style: normal; + } } form dd { - display: inline; - min-height: 2.5em; - vertical-align: top; + padding: 0.3em 0.5em; margin: 0; } +form dt { + padding: 0.5em 0.5em; + margin: 0; +} + +form dt.active, form dd.active { + background-color: @tr-active-color; +} + form dt { display: inline-block; vertical-align: top; min-width: 12em; min-height: 2.5em; width: 30%; + &.errors label { + color: @color-critical; + } +} + +form .errors label { + color: @color-critical; +} + +form dd { + display: inline-block; + width: 63%; + min-height: 2.5em; + vertical-align: top; + margin: 0; + &.errors { + input[type=text], select { + border-color: @color-critical; + } + } + + &.full-width { + padding: 0.5em; + width: 100%; + } } form dd:after { @@ -835,7 +906,6 @@ form textarea { form dd ul.errors { list-style-type: none; padding-left: 0.3em; - font-size: 0.857em; li { color: @colorCritical; @@ -843,6 +913,22 @@ form dd ul.errors { } } +form div.hint { + padding: 1em; + background-color: @tr-hover-color; + margin: 1em 0; + max-width: 65em; + font-size: 1em; + + pre { + font-style: normal; + background-color: white; + margin: 0; + padding: 1em; + } +} + + form { #_FAKE_SUBMIT { position: absolute; @@ -850,21 +936,4 @@ form { } } -form div.hint { - padding: 1em; - background-color: #f2f4fd; - border: 1px solid lightgrey; - margin: 1em 0; - - pre { - font-style: normal; - background-color: white; - font-size: 1.25em; - margin: 0; - padding: 1em; - } -} - - - /** End of forms **/ From 9712065a6bb71df30a575f661672c595fb696867 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 8 Dec 2016 10:12:43 +0100 Subject: [PATCH 137/256] AddNodeForm: show next when not completed --- application/forms/AddNodeForm.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/application/forms/AddNodeForm.php b/application/forms/AddNodeForm.php index 322c293..55abfc2 100644 --- a/application/forms/AddNodeForm.php +++ b/application/forms/AddNodeForm.php @@ -52,6 +52,8 @@ class AddNodeForm extends QuickForm protected function addNewProcess() { + $this->addHtml('

Add a new node

'); + $this->addElement('text', 'name', array( 'label' => $this->translate('Name'), 'required' => true, @@ -159,6 +161,8 @@ class AddNodeForm extends QuickForm $this->addHostElement(); if ($host = $this->getSentValue('host')) { $this->addServicesElement($host); + } else { + $this->setSubmitLabel($this->translate('Next')); } } From 803b2aa34dec7075458487e0fd51854f3b7819db Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 8 Dec 2016 10:16:56 +0100 Subject: [PATCH 138/256] js: add Director-related JavaScript helpers --- public/js/module.js | 54 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/public/js/module.js b/public/js/module.js index 0504318..197a983 100644 --- a/public/js/module.js +++ b/public/js/module.js @@ -26,19 +26,22 @@ 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.icinga.logger.debug('BP module loaded'); + this.module.icinga.logger.debug('BP module initialized'); }, onRendered: function (event) { var $container = $(event.currentTarget); this.fixFullscreen($container); this.fixOpenedBps($container); + this.highlightFormErrors($container); + this.hideInactiveFormDescriptions($container); }, processTitleClick: function (event) { @@ -70,6 +73,10 @@ } }, + hideInactiveFormDescriptions: function($container) { + $container.find('dd').not('.active').find('p.description').hide(); + }, + /** * Add 'hovered' class to hovered title elements * @@ -202,7 +209,52 @@ } this.idCache[$bpName] = ids; + }, + + /** BEGIN Form handling, borrowed from Director **/ + formElementFocus: function(ev) + { + var $input = $(ev.currentTarget); + var $dd = $input.closest('dd'); + $dd.find('p.description').show(); + if ($dd.attr('id') && $dd.attr('id').match(/button/)) { + return; + } + var $li = $input.closest('li'); + var $dt = $dd.prev(); + var $form = $dd.closest('form'); + + $form.find('dt, dd, li').removeClass('active'); + $li.addClass('active'); + $dt.addClass('active'); + $dd.addClass('active'); + $dd.find('p.description.fading-out') + .stop(true) + .removeClass('fading-out') + .fadeIn('fast'); + + $form.find('dd').not($dd) + .find('p.description') + .not('.fading-out') + .addClass('fading-out') + .delay(2000) + .fadeOut('slow', function() { + $(this).removeClass('fading-out').hide() + }); + }, + + highlightFormErrors: function($container) + { + $container.find('dd ul.errors').each(function(idx, ul) { + var $ul = $(ul); + var $dd = $ul.closest('dd'); + var $dt = $dd.prev(); + + $dt.addClass('errors'); + $dd.addClass('errors'); + }); } + /** END Form handling **/ }; Icinga.availableModules.businessprocess = Bp; From f41e4c34c24069ac55a924c090ac1ff605bd4a07 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 09:53:36 +0100 Subject: [PATCH 139/256] Controller: moved to Web --- application/controllers/IndexController.php | 2 +- application/controllers/NodeController.php | 2 +- application/controllers/ProcessController.php | 3 +- .../Businessprocess/{ => Web}/Controller.php | 32 +++++++++++++++---- 4 files changed, 29 insertions(+), 10 deletions(-) rename library/Businessprocess/{ => Web}/Controller.php (92%) diff --git a/application/controllers/IndexController.php b/application/controllers/IndexController.php index c43a044..106e6f5 100644 --- a/application/controllers/IndexController.php +++ b/application/controllers/IndexController.php @@ -2,7 +2,7 @@ namespace Icinga\Module\Businessprocess\Controllers; -use Icinga\Module\Businessprocess\Controller; +use Icinga\Module\Businessprocess\Web\Controller; class IndexController extends Controller { diff --git a/application/controllers/NodeController.php b/application/controllers/NodeController.php index c3d6a20..e84a2af 100644 --- a/application/controllers/NodeController.php +++ b/application/controllers/NodeController.php @@ -2,7 +2,7 @@ namespace Icinga\Module\Businessprocess\Controllers; -use Icinga\Module\Businessprocess\Controller; +use Icinga\Module\Businessprocess\Web\Controller; use Icinga\Module\Businessprocess\Simulation; use Icinga\Web\Url; diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index d7d84e7..a9620f2 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -3,7 +3,6 @@ namespace Icinga\Module\Businessprocess\Controllers; use Icinga\Module\Businessprocess\BusinessProcess; -use Icinga\Module\Businessprocess\Controller; use Icinga\Module\Businessprocess\ConfigDiff; use Icinga\Module\Businessprocess\Html\Element; use Icinga\Module\Businessprocess\Html\HtmlString; @@ -15,6 +14,7 @@ use Icinga\Module\Businessprocess\Renderer\TileRenderer; use Icinga\Module\Businessprocess\Renderer\TreeRenderer; use Icinga\Module\Businessprocess\Simulation; use Icinga\Module\Businessprocess\Html\Link; +use Icinga\Module\Businessprocess\Web\Controller; use Icinga\Module\Businessprocess\Web\Url; use Icinga\Web\Notification; use Icinga\Web\Widget\Tabextension\DashboardAction; @@ -168,6 +168,7 @@ class ProcessController extends Controller { $action = $this->params->get('action'); $form = null; + if ($action === 'add') { $form =$this->loadForm('AddNode') ->setProcess($bp) diff --git a/library/Businessprocess/Controller.php b/library/Businessprocess/Web/Controller.php similarity index 92% rename from library/Businessprocess/Controller.php rename to library/Businessprocess/Web/Controller.php index 0d9e119..20e91c0 100644 --- a/library/Businessprocess/Controller.php +++ b/library/Businessprocess/Web/Controller.php @@ -20,14 +20,17 @@ use Icinga\Web\Widget\Tabs; class Controller extends ModuleController { + /** @var View */ + public $view; + /** @deprecated, obsolete */ protected $backend; /** @var BusinessProcess */ protected $bp; - /** @var View */ - public $view; + /** @var Tabs */ + protected $tabs; /** @var Storage */ private $storage; @@ -122,16 +125,13 @@ class Controller extends ModuleController */ protected function singleTab($label) { - $tabs = Widget::create('tabs')->add( + return $this->tabs()->add( 'tab', array( 'label' => $label, 'url' => $this->getRequest()->getUrl() ) )->activate('tab'); - $this->controls()->add(HtmlString::create($tabs)); - - return $tabs; } /** @@ -142,11 +142,29 @@ class Controller extends ModuleController return $this->singleTab($this->translate('Business Process')); } + /** + * @return Tabs + */ + protected function overviewTab() + { + return $this->tabs()->add( + 'overview', + array( + 'label' => $this->translate('Business Process'), + 'url' => 'businessprocess' + ) + )->activate('overview'); + } + protected function tabs() { + // Todo: do not add to view once all of them render controls() if ($this->view->tabs === null) { - $this->view->tabs = Widget::create('tabs'); + $tabs = Widget::create('tabs'); + $this->controls()->add(HtmlString::create($tabs)); + $this->view->tabs = $tabs; } + return $this->view->tabs; } From 7ddc54f828c96c4318bddb8a169b40f8fc7caf4a Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 09:58:00 +0100 Subject: [PATCH 140/256] Metadata: move "header" logic to a dedicated class --- library/Businessprocess/BusinessProcess.php | 33 +- library/Businessprocess/Metadata.php | 195 +++++++++++ .../Businessprocess/Storage/LegacyStorage.php | 311 ++++++++++-------- 3 files changed, 392 insertions(+), 147 deletions(-) create mode 100644 library/Businessprocess/Metadata.php diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index 875954c..8d4aa1d 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -31,6 +31,9 @@ class BusinessProcess */ protected $backend; + /** @var Metadata */ + protected $metadata; + /** * Business process name * @@ -119,6 +122,21 @@ class BusinessProcess { } + public function getMetadata() + { + if ($this->metadata === null) { + $this->metadata = new Metadata(); + } + + return $this->metadata; + } + + public function setMetadata(Metadata $metadata) + { + $this->metadata = $metadata; + return $this; + } + public function applyChanges(ProcessChanges $changes) { $cnt = 0; @@ -185,28 +203,23 @@ class BusinessProcess public function getTitle() { - return $this->title ?: $this->getName(); + $meta = $this->getMetadata(); + return $meta->has('Title') ? $meta->get('Title') : $this->getName(); } public function hasTitle() { - return $this->title !== null; - } - - public function setBackendName($name) - { - $this->backendName = $name; - return $this; + return $this->getMetadata()->has('Title'); } public function getBackendName() { - return $this->backendName; + return $this->getMetadata()->get('Backend'); } public function hasBackendName() { - return $this->backendName !== null; + return $this->getMetadata()->has('Backend'); } public function setBackend(MonitoringBackend $backend) diff --git a/library/Businessprocess/Metadata.php b/library/Businessprocess/Metadata.php new file mode 100644 index 0000000..6efaefd --- /dev/null +++ b/library/Businessprocess/Metadata.php @@ -0,0 +1,195 @@ + null, + 'Description' => null, + 'Owner' => null, + 'AllowedUsers' => null, + 'AllowedGroups' => null, + 'AllowedRoles' => null, + 'Backend' => null, + 'Statetype' => null, + // 'SLAHosts' => null + ); + + public function getProperties() + { + return $this->properties; + } + + public function hasKey($key) + { + return array_key_exists($key, $this->properties); + } + + public function get($key, $default = null) + { + $this->assertKeyExists($key); + if ($this->properties[$key] === null) { + return $default; + } + + return $this->properties[$key]; + } + + public function set($key, $value) + { + $this->assertKeyExists($key); + $this->properties[$key] = $value; + + return $this; + } + + public function isNull($key) + { + return null === $this->get($key); + } + + public function has($key) + { + return null !== $this->get($key); + } + + protected function assertKeyExists($key) + { + if (! $this->hasKey($key)) { + throw new ProgrammingError('Trying to access invalid header key: %s', $key); + } + + return $this; + } + + public function hasRestrictions() + { + return ! ( + $this->isNull('AllowedUsers') + && $this->isNull('AllowedGroups') + && $this->isNull('AllowedRoles') + ); + } + + protected function getAuth() + { + return Auth::getInstance(); + } + + public function permissionsAreSatisfied(Auth $auth = null) + { + if ($auth === null) { + if (Icinga::app()->isCli()) { + return true; + } else { + $auth = $this->getAuth(); + } + } + + if (! $this->hasRestrictions()) { + return true; + } + + if (! $auth->isAuthenticated()) { + return false; + } + + return $this->userIsAllowed($auth->getUser()); + } + + public function userIsAllowed(User $user) + { + $username = $user->getUsername(); + + return $this->ownerIs($username) + || $this->isInAllowedUserList($username) + || $this->isMemberOfAllowedGroups($user) + || $this->hasOneOfTheAllowedRoles($user); + } + + public function ownerIs($username) + { + return $this->get('Owner') === $username; + } + + public function listAllowedUsers() + { + // TODO: $this->get('AllowedUsers', array()); + $list = $this->get('AllowedUsers'); + if ($list === null) { + return array(); + } else { + return $this->splitCommaSeparated($list); + } + } + + public function listAllowedGroups() + { + $list = $this->get('AllowedGroups'); + if ($list === null) { + return array(); + } else { + return $this->splitCommaSeparated($list); + } + } + + public function listAllowedRoles() + { + $list = $this->get('AllowedRoles'); + if ($list === null) { + return array(); + } else { + return $this->splitCommaSeparated($list); + } + } + + public function isInAllowedUserList($username) + { + foreach ($this->listAllowedUsers() as $allowedUser) { + if ($username === $allowedUser) { + return true; + } + } + + return false; + } + + public function isMemberOfAllowedGroups(User $user) + { + foreach ($this->listAllowedGroups() as $groups) { + foreach ($groups as $group) { + if ($user->isMemberOf($group)) { + return true; + } + } + } + + return false; + } + + public function hasOneOfTheAllowedRoles(User $user) + { + foreach ($this->listAllowedRoles() as $roles) { + foreach ($roles as $roleName) { + foreach ($user->getRoles() as $role) { + if ($role->getName() === $roleName) { + return true; + } + } + } + } + + return false; + } + + protected function splitCommaSeparated($string) + { + return preg_split('/\s*,\s*/', $string, -1, PREG_SPLIT_NO_EMPTY); + } +} diff --git a/library/Businessprocess/Storage/LegacyStorage.php b/library/Businessprocess/Storage/LegacyStorage.php index 3d50ce3..a40704e 100644 --- a/library/Businessprocess/Storage/LegacyStorage.php +++ b/library/Businessprocess/Storage/LegacyStorage.php @@ -5,11 +5,11 @@ namespace Icinga\Module\Businessprocess\Storage; use DirectoryIterator; use Icinga\Application\Benchmark; use Icinga\Application\Icinga; -use Icinga\Authentication\Auth; use Icinga\Exception\ConfigurationError; use Icinga\Module\Businessprocess\BpNode; use Icinga\Module\Businessprocess\BusinessProcess; use Icinga\Exception\SystemPermissionException; +use Icinga\Module\Businessprocess\Metadata; class LegacyStorage extends Storage { @@ -66,6 +66,23 @@ class LegacyStorage extends Storage { $files = array(); + foreach ($this->listAllProcessNames() as $name) { + $meta = $this->loadMetadata($name); + if (! $meta->permissionsAreSatisfied()) { + continue; + } + + $files[$name] = $name; + } + + natsort($files); + return $files; + } + + public function listAllProcessNames() + { + $files = array(); + foreach (new DirectoryIterator($this->getConfigDir()) as $file) { if ($file->isDot()) { continue; @@ -73,17 +90,7 @@ class LegacyStorage extends Storage $filename = $file->getFilename(); if (substr($filename, -5) === '.conf') { - $name = substr($filename, 0, -5); - $header = $this->readHeader($file->getPathname()); - if (! $this->headerPermissionsAreSatisfied($header)) { - continue; - } - - if ($header['Title'] === null) { - $files[$name] = $name; - } else { - $files[$name] = sprintf('%s (%s)', $header['Title'], $name); - } + $files[] = substr($filename, 0, -5); } } @@ -91,57 +98,6 @@ class LegacyStorage extends Storage return $files; } - protected function headerPermissionsAreSatisfied($header) - { - if (Icinga::app()->isCli()) { - return true; - } - - if ($header['Allowed users'] === null - && $header['Allowed groups'] === null - && $header['Allowed roles'] === null - ) { - return true; - } - - $auth = Auth::getInstance(); - if (! $auth->isAuthenticated()) { - return false; - } - - $user = $auth->getUser(); - $username = $user->getUsername(); - - if ($header['Owner'] === $username) { - return true; - } - - if ($header['Allowed users'] !== null) { - $users = $this->splitCommaSeparated($header['Allowed users']); - foreach ($users as $allowedUser) { - if ($username === $allowedUser) { - return true; - } - } - } - - if ($header['Allowed groups'] !== null) { - $groups = $this->splitCommaSeparated($header['Allowed groups']); - foreach ($groups as $group) { - if ($user->isMemberOf($group)) { - return true; - } - } - } - - if ($header['Allowed roles'] !== null) { - // TODO: not implemented yet - return false; - } - - return false; - } - protected function splitCommaSeparated($string) { return preg_split('/\s*,\s*/', $string, -1, PREG_SPLIT_NO_EMPTY); @@ -151,45 +107,46 @@ class LegacyStorage extends Storage { $fh = fopen($file, 'r'); $cnt = 0; - $header = $this->emptyHeader(); + $meta = new Metadata(); while ($cnt < 15 && false !== ($line = fgets($fh))) { $cnt++; - $this->parseHeaderLine($line, $header); + $this->parseHeaderLine($line, $meta); } fclose($fh); - return $header; + return $meta; } protected function readHeaderString($string) { - $header = $this->emptyHeader(); + $meta = new Metadata(); foreach (preg_split('/\n/', $string) as $line) { - $this->parseHeaderLine($line, $header); + $this->parseHeaderLine($line, $meta); } - return $header; + return $meta; } protected function emptyHeader() { return array( - 'Title' => null, - 'Owner' => null, - 'Allowed users' => null, - 'Allowed groups' => null, - 'Allowed roles' => null, - 'Backend' => null, - 'Statetype' => 'soft', - 'SLA Hosts' => null + 'Title' => null, + 'Description' => null, + 'Owner' => null, + 'AllowedUsers' => null, + 'AllowedGroups' => null, + 'AllowedRoles' => null, + 'Backend' => null, + 'Statetype' => 'soft', + 'SLAHosts' => null ); } - protected function parseHeaderLine($line, & $header) + protected function parseHeaderLine($line, Metadata $meta) { if (preg_match('/^\s*#\s+(.+?)\s*:\s*(.+)$/', $line, $m)) { - if (array_key_exists($m[1], $header)) { - $header[$m[1]] = $m[2]; + if ($meta->hasKey($m[1])) { + $meta->set($m[1], $m[2]); } } } @@ -201,14 +158,54 @@ class LegacyStorage extends Storage */ public function storeProcess(BusinessProcess $process) { - $filename = $this->getFilename($process->getName()); - $content = $process->toLegacyConfigString(); file_put_contents( - $filename, - $content + $this->getFilename($process->getName()), + $this->render($process) ); } + public function render(BusinessProcess $process) + { + return $this->renderHeader($process) + . $this->renderNodes($process); + } + + public function renderHeader(BusinessProcess $process) + { + $conf = "### Business Process Config File ###\n#\n"; + + $meta = $process->getMetadata(); + foreach ($meta->getProperties() as $key => $value) { + if ($value === null) { + continue; + } + + $conf .= sprintf("# %-11s : %s\n", $key, $value); + } + + $conf .= "#\n###################################\n\n"; + + return $conf; + } + + public function renderNodes(BusinessProcess $bp) + { + $rendered = array(); + $conf = ''; + + foreach ($bp->getChildren() as $child) { + $conf .= $child->toLegacyConfigString($rendered); + $rendered[$child->getName()] = true; + } + + foreach ($bp->getUnboundNodes() as $name => $node) { + $conf .= $node->toLegacyConfigString($rendered); + $rendered[$name] = true; + } + + return $conf . "\n"; + } + public function getSource($name) { return file_get_contents($this->getFilename($name)); @@ -224,7 +221,7 @@ class LegacyStorage extends Storage $bp = new BusinessProcess(); $bp->setName($name); $this->parseString($string, $bp); - $this->readHeaderString($string); + $bp->setMetadata($this->readHeaderString($string)); return $bp; } @@ -261,10 +258,22 @@ class LegacyStorage extends Storage return false; } - $header = $this->readHeader($file); - $bp = new BusinessProcess(); - $this->loadHeader($name, $bp); - return $this->headerPermissionsAreSatisfied($header); + return $this->loadMetaFromFile($file)->permissionsAreSatisfied(); + } + + public function loadMetadata($name) + { + return $this->loadMetaFromFile($this->getFilename($name)); + } + + public function loadMetadataFromString($string) + { + return $this->readHeaderString($string); + } + + public function loadMetaFromFile($filename) + { + return $this->readHeader($filename); } /** @@ -275,23 +284,7 @@ class LegacyStorage extends Storage { // TODO: do not open twice, this is quick and dirty based on existing code $file = $this->currentFilename = $this->getFilename($name); - $header = $this->readHeader($file); - $this->applyHeader($header, $bp); - } - - /** - * @param array $header - * @param BusinessProcess $bp - */ - protected function applyHeader($header, $bp) - { - $bp->setTitle($header['Title']); - if ($header['Backend']) { - $bp->setBackendName($header['Backend']); - } - if ($header['Statetype'] === 'soft') { - $bp->useSoftStates(); - } + $bp->setMetadata($this->readHeader($file)); } protected function parseFile($name, $bp) @@ -312,52 +305,96 @@ class LegacyStorage extends Storage unset($this->currentFilename); } - protected function parseString($string, $bp) + protected function parseString($string, BusinessProcess $bp) { foreach (preg_split('/\n/', $string) as $line) { $this->parseLine($line, $bp); } } - protected function parseLine(& $line, $bp) + /** + * @param $line + * @param BusinessProcess $bp + */ + protected function parseDisplay(& $line, BusinessProcess $bp) + { + list($display, $name, $desc) = preg_split('~\s*;\s*~', substr($line, 8), 3); + $bp->getNode($name)->setAlias($desc)->setDisplay($display); + if ($display > 0) { + $bp->addRootNode($name); + } + } + + protected function parseExternalInfo(& $line, BusinessProcess $bp) + { + list($name, $script) = preg_split('~\s*;\s*~', substr($line, 14), 2); + $bp->getNode($name)->setInfoCommand($script); + } + + protected function parseExtraInfo(& $line, BusinessProcess $bp) + { + // TODO: Not yet + // list($name, $script) = preg_split('~\s*;\s*~', substr($line, 14), 2); + // $this->getNode($name)->setExtraInfo($script); + } + + protected function parseInfoUrl(& $line, BusinessProcess $bp) + { + list($name, $url) = preg_split('~\s*;\s*~', substr($line, 9), 2); + $bp->getNode($name)->setInfoUrl($url); + } + + protected function parseExtraLine(& $line, $typeLength, BusinessProcess $bp) + { + $type = substr($line, 0, $typeLength); + if (substr($type, 0, 7) === 'display') { + $this->parseDisplay($line, $bp); + return true; + } + + switch ($type) { + case 'external_info': + $this->parseExternalInfo($line, $bp); + break; + case 'extra_info': + $this->parseExtraInfo($line, $bp); + break; + case 'info_url': + $this->parseInfoUrl($line, $bp); + break; + default: + return false; + } + + return true; + } + + /** + * Parses a single line + * + * Adds eventual new knowledge to the given Business Process config + * + * @param $line + * @param $bp + + */ + protected function parseLine(& $line, BusinessProcess $bp) { $line = trim($line); $this->parsing_line_number++; - if (empty($line)) { - return; - } - if ($line[0] === '#') { + // Skip empty or comment-only lines + if (empty($line) || $line[0] === '#') { return; } - // TODO: substr? - if (preg_match('~^display~', $line)) { - list($display, $name, $desc) = preg_split('~\s*;\s*~', substr($line, 8), 3); - $node = $bp->getNode($name)->setAlias($desc)->setDisplay($display); - if ($display > 0) { - $bp->addRootNode($name); + // Semicolon found in the first 14 cols? Might be a line with extra information + $pos = strpos($line, ';'); + if ($pos !== false && $pos < 14) { + if ($this->parseExtraLine($line, $pos, $bp)) { + return; } - return; - } - - if (preg_match('~^external_info~', $line)) { - list($name, $script) = preg_split('~\s*;\s*~', substr($line, 14), 2); - $node = $bp->getNode($name)->setInfoCommand($script); - return; - } - - // New feature: - // if (preg_match('~^extra_info~', $line)) { - // list($name, $script) = preg_split('~\s*;\s*~', substr($line, 14), 2); - // $node = $this->getNode($name)->setExtraInfo($script); - // } - - if (preg_match('~^info_url~', $line)) { - list($name, $url) = preg_split('~\s*;\s*~', substr($line, 9), 2); - $node = $bp->getNode($name)->setInfoUrl($url); - return; } list($name, $value) = preg_split('~\s*=\s*~', $line, 2); From f4bd0b05bd0f9887fd29d7d5f6fc24a919d8012e Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 13:46:58 +0100 Subject: [PATCH 141/256] TreeRenderer: do not ask process for lock --- library/Businessprocess/Renderer/TreeRenderer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Businessprocess/Renderer/TreeRenderer.php b/library/Businessprocess/Renderer/TreeRenderer.php index ee85465..b213e48 100644 --- a/library/Businessprocess/Renderer/TreeRenderer.php +++ b/library/Businessprocess/Renderer/TreeRenderer.php @@ -143,7 +143,7 @@ class TreeRenderer extends Renderer $td->add($this->createInfoAction($node)); } - if (! $this->bp->isLocked()) { + if (! $this->isLocked()) { $td->addContent($this->getActionIcons($bp, $node)); } From b11fbf52110ca06d42ab7ebba061588af76784d0 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 13:50:21 +0100 Subject: [PATCH 142/256] views/scripts: remove obsolete view scripts --- application/views/scripts/node/empty.phtml | 1 - .../views/scripts/process/history.phtml | 94 ------------------- .../views/scripts/process/toplevel.phtml | 47 ---------- application/views/scripts/warnings.phtml | 6 -- 4 files changed, 148 deletions(-) delete mode 100644 application/views/scripts/node/empty.phtml delete mode 100644 application/views/scripts/process/history.phtml delete mode 100644 application/views/scripts/process/toplevel.phtml delete mode 100644 application/views/scripts/warnings.phtml diff --git a/application/views/scripts/node/empty.phtml b/application/views/scripts/node/empty.phtml deleted file mode 100644 index 263fd9f..0000000 --- a/application/views/scripts/node/empty.phtml +++ /dev/null @@ -1 +0,0 @@ -__CLOSEME__ diff --git a/application/views/scripts/process/history.phtml b/application/views/scripts/process/history.phtml deleted file mode 100644 index 34a20b3..0000000 --- a/application/views/scripts/process/history.phtml +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - -
 
- -
- - - - - - -
 
-
- -
-history as $entry) { -if ($entry->hostname !== $last_host || $entry->service !== $last_service) { - -echo '' . "\n"; -} -$cnt++; -if ($cnt > 10000) break; - $duration = $entry->timestamp - $current_offset; - if ($next_color === null) { - $color = stateColor($entry->last_state); - } else { - $color = $next_color; - } - $next_color = stateColor($entry->state); - - if ($entry->state == 0) { - $offset = ceil($duration / 3600 / 6); - } else { - $offset = floor($duration / 3600 / 6); - } - echo '
 
'; - $current_offset += $duration; - - $last_host = $entry->hostname; -$last_service = $entry->service; - -} - - -?>
diff --git a/application/views/scripts/process/toplevel.phtml b/application/views/scripts/process/toplevel.phtml deleted file mode 100644 index b943dad..0000000 --- a/application/views/scripts/process/toplevel.phtml +++ /dev/null @@ -1,47 +0,0 @@ -bp->countChildren(); -$howMany = 'normal'; - -if ($count < 20) { - $howMany = 'few'; -} elseif ($count > 50) { - $howMany = 'many'; -} - -?> -compact): ?> -
-tabs ?> -

- formSelect('config', $this->configName, array('class' => 'autosubmit'), $this->processList) ?> - - icon('sitemap') ?> -

-
- - -
- - render('warnings.phtml') ?> -
diff --git a/application/views/scripts/warnings.phtml b/application/views/scripts/warnings.phtml deleted file mode 100644 index 9d1b16c..0000000 --- a/application/views/scripts/warnings.phtml +++ /dev/null @@ -1,6 +0,0 @@ -warnings): ?> -

translate('Warnings') ?>

-warnings as $warning): ?> -escape($warning) ?>
- - From 18dc398dca69850658293e595c06b10452f73b84 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 13:57:07 +0100 Subject: [PATCH 143/256] ActionBar: take over logic from controller --- application/controllers/ProcessController.php | 78 +------------ .../Web/Component/ActionBar.php | 105 +++++++++++++++++- library/Businessprocess/Web/Controller.php | 19 +--- 3 files changed, 109 insertions(+), 93 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index a9620f2..5ac20e8 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -14,6 +14,7 @@ use Icinga\Module\Businessprocess\Renderer\TileRenderer; use Icinga\Module\Businessprocess\Renderer\TreeRenderer; use Icinga\Module\Businessprocess\Simulation; use Icinga\Module\Businessprocess\Html\Link; +use Icinga\Module\Businessprocess\Web\Component\ActionBar; use Icinga\Module\Businessprocess\Web\Controller; use Icinga\Module\Businessprocess\Web\Url; use Icinga\Web\Notification; @@ -60,7 +61,6 @@ class ProcessController extends Controller { $bp = $this->prepareProcess(); $node = $this->getNode($bp); - $this->prepareActionBar(); $this->redirectOnConfigSwitch(); $bp->retrieveStatesFromBackend(); $this->handleSimulations($bp); @@ -275,83 +275,7 @@ class ProcessController extends Controller return $bp; } - protected function prepareActionBar() - { - $mode = $this->params->get('mode'); - $unlocked = (bool) $this->params->get('unlocked'); - if ($mode === 'tile') { - $this->actions()->add( - Link::create( - $this->translate('Tree'), - $this->url()->with('mode', 'tree'), - null, - array('class' => 'icon-sitemap') - ) - ); - } else { - $this->actions()->add( - Link::create( - $this->translate('Tiles'), - $this->url()->with('mode', 'tile'), - null, - array('class' => 'icon-dashboard') - ) - ); - } - - if ($unlocked) { - $this->actions()->add( - Link::create( - $this->translate('Lock'), - $this->url()->without('unlocked')->without('action'), - null, - array( - 'class' => 'icon-lock', - 'title' => $this->translate('Lock this process'), - ) - ) - ); - } else { - $this->actions()->add( - Link::create( - $this->translate('Unlock'), - $this->url()->with('unlocked', true), - null, - array( - 'class' => 'icon-lock-open', - 'title' => $this->translate('Unlock this process'), - ) - ) - ); - } - - $this->actions()->add( - Link::create( - $this->translate('Config'), - 'businessprocess/process/config', - $this->currentProcessParams(), - array( - 'class' => 'icon-wrench', - 'title' => $this->translate('Modify this process'), - 'data-base-target' => '_next', - ) - ) - ); - - $this->actions()->add( - Link::create( - $this->translate('Fullscreen'), - $this->url()->with('showFullscreen', true), - null, - array( - 'class' => 'icon-resize-full-alt', - 'title' => $this->translate('Switch to fullscreen mode'), - 'data-base-target' => '_main', - ) - ) - ); - } /** * Show the source code for a process diff --git a/library/Businessprocess/Web/Component/ActionBar.php b/library/Businessprocess/Web/Component/ActionBar.php index d44cccc..c30d554 100644 --- a/library/Businessprocess/Web/Component/ActionBar.php +++ b/library/Businessprocess/Web/Component/ActionBar.php @@ -2,11 +2,112 @@ namespace Icinga\Module\Businessprocess\Web\Component; -use Icinga\Module\Businessprocess\Html\Container; +use Icinga\Authentication\Auth; +use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\Html\BaseElement; +use Icinga\Module\Businessprocess\Html\Link; +use Icinga\Module\Businessprocess\Renderer\Renderer; +use Icinga\Module\Businessprocess\Renderer\TreeRenderer; -class ActionBar extends Container +class ActionBar extends BaseElement { protected $contentSeparator = ' '; + /** @var string */ + protected $tag = 'div'; + protected $defaultAttributes = array('class' => 'action-bar'); + + public function __construct(BusinessProcess $config, Renderer $renderer, Auth $auth, $url) + { + $meta = $config->getMetadata(); + + if ($renderer instanceof TreeRenderer) { + $this->add( + Link::create( + $this->translate('Tiles'), + $url->with('mode', 'tile'), + null, + array('class' => 'icon-dashboard') + ) + ); + } else { + $this->add( + Link::create( + $this->translate('Tree'), + $url->with('mode', 'tree'), + null, + array('class' => 'icon-sitemap') + ) + ); + } + $hasChanges = $config->hasSimulations() || $config->hasBeenChanged(); + + if ($renderer->isLocked()) { + $this->add( + Link::create( + $this->translate('Unlock'), + $url->with('unlocked', true), + null, + array( + 'class' => 'icon-lock-open', + 'title' => $this->translate('Unlock this process'), + ) + ) + ); + } elseif (! $hasChanges) { + $this->add( + Link::create( + $this->translate('Lock'), + $url->without('unlocked')->without('action'), + null, + array( + 'class' => 'icon-lock', + 'title' => $this->translate('Lock this process'), + ) + ) + ); + } + + if ($meta->canModify()) { + $this->add( + Link::create( + $this->translate('Config'), + 'businessprocess/process/config', + $this->currentProcessParams($url), + array( + 'class' => 'icon-wrench', + 'title' => $this->translate('Modify this process'), + 'data-base-target' => '_next', + ) + ) + ); + } + + $this->add( + Link::create( + $this->translate('Fullscreen'), + $url->with('showFullscreen', true), + null, + array( + 'class' => 'icon-resize-full-alt', + 'title' => $this->translate('Switch to fullscreen mode'), + 'data-base-target' => '_main', + ) + ) + ); + } + + protected function currentProcessParams($url) + { + $urlParams = $url->getParams(); + $params = array(); + foreach (array('config', 'node') as $name) { + if ($value = $urlParams->get($name)) { + $params[$name] = $value; + } + } + + return $params; + } } diff --git a/library/Businessprocess/Web/Controller.php b/library/Businessprocess/Web/Controller.php index 20e91c0..5ab5b0c 100644 --- a/library/Businessprocess/Web/Controller.php +++ b/library/Businessprocess/Web/Controller.php @@ -1,8 +1,9 @@ url; } - protected function currentProcessParams() - { - $params = array(); - foreach (array('config', 'node') as $name) { - if ($value = $this->params->get($name)) { - $params[$name] = $value; - } - } - - return $params; - } - /** * @return ActionBar */ @@ -156,6 +144,9 @@ class Controller extends ModuleController )->activate('overview'); } + /** + * @return Tabs + */ protected function tabs() { // Todo: do not add to view once all of them render controls() From cc2bb604c26d0d792a6abc3d711ed4840b8cd58e Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 13:58:33 +0100 Subject: [PATCH 144/256] AddNodeForm: useless use of use --- application/forms/AddNodeForm.php | 1 - 1 file changed, 1 deletion(-) diff --git a/application/forms/AddNodeForm.php b/application/forms/AddNodeForm.php index 55abfc2..2610b23 100644 --- a/application/forms/AddNodeForm.php +++ b/application/forms/AddNodeForm.php @@ -7,7 +7,6 @@ use Icinga\Module\Businessprocess\BusinessProcess; use Icinga\Module\Businessprocess\Modification\ProcessChanges; use Icinga\Module\Businessprocess\Web\Form\QuickForm; use Icinga\Module\Monitoring\Backend\MonitoringBackend; -use Icinga\Web\Notification; use Icinga\Web\Session\SessionNamespace; class AddNodeForm extends QuickForm From 36e624c4481b319375ec3110e96de1b74d8062ff Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 14:01:30 +0100 Subject: [PATCH 145/256] Dasboard: introduce a new config overview page --- application/controllers/IndexController.php | 22 +--- application/views/scripts/index/index.phtml | 7 +- .../Web/Component/Dashboard.php | 108 ++++++++++++++++++ .../Web/Component/DashboardAction.php | 29 +++++ public/css/module.less | 45 ++++++++ 5 files changed, 188 insertions(+), 23 deletions(-) create mode 100644 library/Businessprocess/Web/Component/Dashboard.php create mode 100644 library/Businessprocess/Web/Component/DashboardAction.php diff --git a/application/controllers/IndexController.php b/application/controllers/IndexController.php index 106e6f5..ecdd6e7 100644 --- a/application/controllers/IndexController.php +++ b/application/controllers/IndexController.php @@ -3,29 +3,17 @@ namespace Icinga\Module\Businessprocess\Controllers; use Icinga\Module\Businessprocess\Web\Controller; +use Icinga\Module\Businessprocess\Web\Component\Dashboard; class IndexController extends Controller { /** - * Show a welcome page if no process is available + * Show an overview page */ public function indexAction() { - $configs = $this->storage()->listProcesses(); - - if (! empty($configs)) { - // Redirect to show the first process if there is any - $this->redirectNow( - 'businessprocess/process/show?mode=tile', - array('config' => key($configs)) - ); - } - $this->tabs()->add('welcome', array( - 'label' => $this->translate('Business Processes'), - 'url' => $this->getRequest()->getUrl() - ))->activate('welcome'); - - // Check back from time to time, maybe someone created a process - $this->setAutorefreshInterval(30); + $this->view->dashboard = Dashboard::create($this->Auth(), $this->storage()); + $this->view->tabs = $this->overviewTab(); + $this->setAutorefreshInterval(15); } } diff --git a/application/views/scripts/index/index.phtml b/application/views/scripts/index/index.phtml index 4f090f3..82cb4f3 100644 --- a/application/views/scripts/index/index.phtml +++ b/application/views/scripts/index/index.phtml @@ -3,10 +3,5 @@
-

translate('Configure your first business process') ?>

-

translate('As no business process has been defined yet you might want to create a %s or upload an %s.'), - $this->qlink($this->translate('new business process'), 'businessprocess/process/create'), - $this->qlink($this->translate('existing one'), 'businessprocess/process/upload') -) ?>

+dashboard->render() ?>
diff --git a/library/Businessprocess/Web/Component/Dashboard.php b/library/Businessprocess/Web/Component/Dashboard.php new file mode 100644 index 0000000..8f8311a --- /dev/null +++ b/library/Businessprocess/Web/Component/Dashboard.php @@ -0,0 +1,108 @@ + 'overview-dashboard', + 'data-base-target' => '_next' + ); + + /** @var Auth */ + protected $auth; + + /** @var Storage */ + protected $storage; + + /** + * Dashboard constructor. + * @param Auth $auth + * @param Storage $storage + */ + protected function __construct(Auth $auth, Storage $storage) + { + $this->auth = $auth; + $this->storage = $storage; + // TODO: Auth? + $processes = $storage->listProcesses(); + $this->add( + HtmlTag::h1($this->translate('Welcome to your Business Process Overview')) + ); + $this->add( + HtmlTag::p( + $this->translate( + 'From here you can reach all your defined Business Process' + . ' configurations, create new or modify existing ones' + ) + ) + ); + if ($auth->hasPermission('businessprocess/create')) { + $this->add( + new DashboardAction( + $this->translate('Create'), + $this->translate('Create a new Business Process configuration'), + 'plus', + 'businessprocess/process/create', + null, + array('class' => 'addnew') + ) + )->add( + new DashboardAction( + $this->translate('Upload'), + $this->translate('Upload an existing Business Process configuration'), + 'upload', + 'businessprocess/process/upload', + null, + array('class' => 'addnew') + ) + ); + + } elseif (empty($processes)) { + $this->addContent( + Container::create() + ->add(HtmlTag::h1($this->translate('Not available'))) + ->add(HtmlTag::p($this->translate('No Business Process has been defined for you'))) + ); + } + + foreach ($processes as $name) { + $meta = $storage->loadMetadata($name); + $title = $meta->get('Title'); + if ($title) { + $title = sprintf('%s (%s)', $title, $name); + } else { + $title = $name; + } + $this->add(new DashboardAction( + $title, + $meta->get('Description'), + 'sitemap', + 'businessprocess/process/show', + array('config' => $name) + )); + } + } + + /** + * @param Auth $auth + * @param Storage $storage + * @return static + */ + public static function create(Auth $auth, Storage $storage) + { + return new static($auth, $storage); + } +} diff --git a/library/Businessprocess/Web/Component/DashboardAction.php b/library/Businessprocess/Web/Component/DashboardAction.php new file mode 100644 index 0000000..b413871 --- /dev/null +++ b/library/Businessprocess/Web/Component/DashboardAction.php @@ -0,0 +1,29 @@ + 'action'); + + public function __construct($title, $description, $icon, $url, $urlParams = null, $attributes = null) + { + $this->add( + Link::create( + Icon::create($icon), + $url, + $urlParams, + $attributes + )->add( + Element::create('span', array('class' => 'header'))->addContent($title) + )->addContent($description) + ); + } +} diff --git a/public/css/module.less b/public/css/module.less index df742d3..fb7ebde 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -323,6 +323,51 @@ div.knightrider table.bp { } } +/** BEGIN Dashboard **/ +.overview-dashboard { + > div.action { + width: 20em; + display: inline-block; + + > a { + text-decoration: none; + color: inherit; + vertical-align: middle; + display: block; + padding: 1em; + word-wrap: break-word; + width: 100%; + height: 12em; + overflow: hidden; + box-sizing: border-box; + + > span.header { + font-weight: bold; + display: block; + font-size: 1.25em; + } + + &:hover { + background-color: @text-color; + color: white; + } + + &.addnew:hover { + background-color: @icinga-blue; + } + + i { + float: left; + font-size: 2.5em; + margin-top: -0.1em; + margin-bottom: 2em; + color: inherit; + } + } + } +} +/** END Dashboard **/ + /** BEGIN Tiles **/ .tiles:after { content:''; From d24d0237f4824dad63a60a0a542d5b4315bc82b5 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 14:03:49 +0100 Subject: [PATCH 146/256] LegacyStorageTest: add a test targeting metadata --- .../Businessprocess/Storage/LegacyStorage.php | 41 +++++++------------ library/Businessprocess/Storage/Storage.php | 7 ++++ .../Storage/LegacyStorageTest.php | 16 ++++++-- 3 files changed, 34 insertions(+), 30 deletions(-) diff --git a/library/Businessprocess/Storage/LegacyStorage.php b/library/Businessprocess/Storage/LegacyStorage.php index a40704e..affa341 100644 --- a/library/Businessprocess/Storage/LegacyStorage.php +++ b/library/Businessprocess/Storage/LegacyStorage.php @@ -68,7 +68,7 @@ class LegacyStorage extends Storage foreach ($this->listAllProcessNames() as $name) { $meta = $this->loadMetadata($name); - if (! $meta->permissionsAreSatisfied()) { + if (! $meta->canRead()) { continue; } @@ -103,28 +103,26 @@ class LegacyStorage extends Storage return preg_split('/\s*,\s*/', $string, -1, PREG_SPLIT_NO_EMPTY); } - protected function readHeader($file) + protected function readHeader($file, Metadata $metadata) { $fh = fopen($file, 'r'); $cnt = 0; - $meta = new Metadata(); while ($cnt < 15 && false !== ($line = fgets($fh))) { $cnt++; - $this->parseHeaderLine($line, $meta); + $this->parseHeaderLine($line, $metadata); } fclose($fh); - return $meta; + return $metadata; } - protected function readHeaderString($string) + protected function readHeaderString($string, Metadata $metadata) { - $meta = new Metadata(); foreach (preg_split('/\n/', $string) as $line) { - $this->parseHeaderLine($line, $meta); + $this->parseHeaderLine($line, $metadata); } - return $meta; + return $metadata; } protected function emptyHeader() @@ -142,11 +140,11 @@ class LegacyStorage extends Storage ); } - protected function parseHeaderLine($line, Metadata $meta) + protected function parseHeaderLine($line, Metadata $metadata) { if (preg_match('/^\s*#\s+(.+?)\s*:\s*(.+)$/', $line, $m)) { - if ($meta->hasKey($m[1])) { - $meta->set($m[1], $m[2]); + if ($metadata->hasKey($m[1])) { + $metadata->set($m[1], $m[2]); } } } @@ -221,7 +219,7 @@ class LegacyStorage extends Storage $bp = new BusinessProcess(); $bp->setName($name); $this->parseString($string, $bp); - $bp->setMetadata($this->readHeaderString($string)); + $this->readHeaderString($string, $bp->getMetadata()); return $bp; } @@ -258,22 +256,13 @@ class LegacyStorage extends Storage return false; } - return $this->loadMetaFromFile($file)->permissionsAreSatisfied(); + return $this->loadMetadata($name)->canRead(); } public function loadMetadata($name) { - return $this->loadMetaFromFile($this->getFilename($name)); - } - - public function loadMetadataFromString($string) - { - return $this->readHeaderString($string); - } - - public function loadMetaFromFile($filename) - { - return $this->readHeader($filename); + $metadata = new Metadata($name); + return $this->readHeader($this->getFilename($name), $metadata); } /** @@ -284,7 +273,7 @@ class LegacyStorage extends Storage { // TODO: do not open twice, this is quick and dirty based on existing code $file = $this->currentFilename = $this->getFilename($name); - $bp->setMetadata($this->readHeader($file)); + $this->readHeader($file, $bp->getMetadata()); } protected function parseFile($name, $bp) diff --git a/library/Businessprocess/Storage/Storage.php b/library/Businessprocess/Storage/Storage.php index 89e1a7f..7d8ccb6 100644 --- a/library/Businessprocess/Storage/Storage.php +++ b/library/Businessprocess/Storage/Storage.php @@ -4,6 +4,7 @@ namespace Icinga\Module\Businessprocess\Storage; use Icinga\Data\ConfigObject; use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\Metadata; abstract class Storage { @@ -48,4 +49,10 @@ abstract class Storage * @return bool Whether the process has been deleted */ abstract public function deleteProcess($name); + + /** + * @param string $name + * @return Metadata + */ + abstract public function loadMetadata($name); } diff --git a/test/php/library/Businessprocess/Storage/LegacyStorageTest.php b/test/php/library/Businessprocess/Storage/LegacyStorageTest.php index 77adacf..b0254f4 100644 --- a/test/php/library/Businessprocess/Storage/LegacyStorageTest.php +++ b/test/php/library/Businessprocess/Storage/LegacyStorageTest.php @@ -31,12 +31,12 @@ class LegacyStorageTest extends BaseTestCase $keys = array_keys($this->makeInstance()->listProcesses()); sort($keys); $this->assertEquals( - $keys, array( 'broken_wrong-operator', 'simple_with-header', 'simple_without-header', - ) + ), + $keys ); } @@ -45,12 +45,12 @@ class LegacyStorageTest extends BaseTestCase $keys = array_values($this->makeInstance()->listProcesses()); sort($keys); $this->assertEquals( - $keys, array( 'Simple with header (simple_with-header)', 'broken_wrong-operator', 'simple_without-header', - ) + ), + $keys ); } @@ -104,6 +104,14 @@ class LegacyStorageTest extends BaseTestCase ); } + public function testTitleCanBeReadFromConfig() + { + $this->assertEquals( + 'Simple with header', + $this->makeInstance()->loadProcess('simple_with-header')->getMetadata()->get('Title') + ); + } + public function testAConfiguredLoopCanBeParsed() { $this->assertInstanceOf( From 791a0286b844281a146cd1945ca6d40c193c6758 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 14:05:58 +0100 Subject: [PATCH 147/256] BpConfigForm: deal with new Metadata object --- application/forms/BpConfigForm.php | 112 ++++++++++++++--------------- 1 file changed, 54 insertions(+), 58 deletions(-) diff --git a/application/forms/BpConfigForm.php b/application/forms/BpConfigForm.php index 5382673..8fed728 100644 --- a/application/forms/BpConfigForm.php +++ b/application/forms/BpConfigForm.php @@ -3,18 +3,19 @@ namespace Icinga\Module\Businessprocess\Forms; use Icinga\Application\Config; +use Icinga\Authentication\Auth; use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\Storage\Storage; use Icinga\Module\Businessprocess\Web\Form\QuickForm; -use Icinga\Web\Notification; -use Icinga\Web\Request; -use Icinga\Web\Url; class BpConfigForm extends QuickForm { + /** @var Storage */ protected $storage; protected $backend; + /** @var BusinessProcess */ protected $config; protected $node; @@ -27,16 +28,15 @@ class BpConfigForm extends QuickForm public function setup() { - $this->addElement('text', 'name', array( - 'label' => $this->translate('Name'), + 'label' => $this->translate('Name'), 'required' => true, 'description' => $this->translate( 'This is the unique identifier of this process' ), )); - $this->addElement('text', 'title', array( + $this->addElement('text', 'Title', array( 'label' => $this->translate('Title'), 'description' => $this->translate( 'Usually this title will be shown for this process. Equals name' @@ -44,7 +44,15 @@ class BpConfigForm extends QuickForm ), )); - $this->addElement('select', 'backend_name', array( + $this->addElement('textarea', 'Description', array( + 'label' => $this->translate('Description'), + 'description' => $this->translate( + 'A slightly more detailed description for this process, about 100-150 characters long ' + ), + 'rows' => 4, + )); + + $this->addElement('select', 'Backend', array( 'label' => $this->translate('Backend'), 'description' => $this->translate( 'Icinga Web Monitoring Backend where current object states for' @@ -55,15 +63,27 @@ class BpConfigForm extends QuickForm ) + $this->listAvailableBackends() )); - $this->addElement('select', 'state_type', array( + $this->addElement('select', 'Statetype', array( 'label' => $this->translate('State Type'), 'required' => true, 'description' => $this->translate( 'Whether this process should be based on Icinga hard or soft states' ), 'multiOptions' => array( - 'hard' => $this->translate('Use HARD states'), 'soft' => $this->translate('Use SOFT states'), + 'hard' => $this->translate('Use HARD states'), + ) + )); + + $this->addElement('select', 'AddToMenu', array( + 'label' => $this->translate('Add to menu'), + 'required' => true, + 'description' => $this->translate( + 'Whether this process should be linked in the main Icinga Web 2 menu' + ), + 'multiOptions' => array( + 'yes' => $this->translate('Yes'), + 'no' => $this->translate('No'), ) )); @@ -74,28 +94,20 @@ class BpConfigForm extends QuickForm } else { $config = $this->config; + $meta = $config->getMetadata(); + foreach ($meta->getProperties() as $k => $v) { + if ($el = $this->getElement($k)) { + $el->setValue($v); + } + } $this->getElement('name') ->setValue($config->getName()) ->setAttrib('readonly', true); - if ($config->hasTitle()) { - $this->getElement('title')->setValue($config->getTitle()); - } - - if ($config->hasBackend()) { - $this->getElement('backend_name')->setValue( - $config->getBackend()->getName() - ); - } - if ($config->usesSoftStates()) { - $this->getElement('state_type')->setValue('soft'); - } else { - $this->getElement('state_type')->setValue('hard'); - } - $this->setSubmitLabel( $this->translate('Store') ); + $label = $this->translate('Delete'); $el = $this->createElement('submit', $label) ->setLabel($label) @@ -121,7 +133,6 @@ class BpConfigForm extends QuickForm { $this->config = $config; return $this; - return $this; } protected function onRequest() @@ -138,53 +149,38 @@ class BpConfigForm extends QuickForm public function onSuccess() { - $name = $this->getValue('name'); - $title = $this->getValue('title'); - $backend = $this->getValue('backend'); + $name = $this->getValue('name'); if ($this->config === null) { // New config $config = new BusinessProcess(); $config->setName($name); - if ($title) { - $config->setTitle($title); - } - if ($backend) { - $config->setBackendName($backend); - } - if ($this->getValue('state_type') === 'soft') { - $config->useSoftStates(); - } else { - $config->useHardStates(); - } - $this->storage->storeProcess($config); - $config->clearAppliedChanges(); + $config->getMetadata()->set('Owner', Auth::getInstance()->getUser()->getUsername()); $this->setSuccessUrl( $this->getSuccessUrl()->setParams( array('config' => $name, 'unlocked' => true) ) ); + $this->setSuccessMessage(sprintf('Process %s has been created', $name)); - $this->redirectOnSuccess(sprintf('Process %s has been created', $name)); } else { $config = $this->config; - if ($title) { - $config->setTitle($title); - } - if ($backend) { - $config->setBackendName($backend); - } - if ($this->getValue('state_type') === 'soft') { - $config->useSoftStates(); - } else { - $config->useHardStates(); - } - - $this->storage->storeProcess($config); - $config->clearAppliedChanges(); - $this->getSuccessUrl()->setParam('config', $name); - Notification::success(sprintf('Process %s has been stored', $name)); + $this->setSuccessMessage(sprintf('Process %s has been stored', $name)); } + $meta = $config->getMetadata(); + foreach ($this->getValues() as $key => $value) { + if ($value === null || $value === '') { + continue; + } + if ($meta->hasKey($key)) { + $meta->set($key, $value); + } + } + + $this->storage->storeProcess($config); + $config->clearAppliedChanges(); + $this->setSuccessUrl('businessprocess/process/show', array('config' => $name)); + parent::onSuccess(); } public function hasDeleteButton() From 0aeb3183f44089ad852cb0373aec5d7306e949de Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 14:11:56 +0100 Subject: [PATCH 148/256] Node: remove toLegacyConfigString --- library/Businessprocess/Node.php | 7 ------- library/Businessprocess/Storage/LegacyStorage.php | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/library/Businessprocess/Node.php b/library/Businessprocess/Node.php index e8109ea..51f37cb 100644 --- a/library/Businessprocess/Node.php +++ b/library/Businessprocess/Node.php @@ -334,13 +334,6 @@ abstract class Node return ' '; } - // TODO: Why isn't this abstract? - // abstract public function toLegacyConfigString(); - public function toLegacyConfigString(& $rendered = array()) - { - return ''; - } - public function getName() { return $this->name; diff --git a/library/Businessprocess/Storage/LegacyStorage.php b/library/Businessprocess/Storage/LegacyStorage.php index affa341..e7c0a3a 100644 --- a/library/Businessprocess/Storage/LegacyStorage.php +++ b/library/Businessprocess/Storage/LegacyStorage.php @@ -191,7 +191,7 @@ class LegacyStorage extends Storage $rendered = array(); $conf = ''; - foreach ($bp->getChildren() as $child) { + foreach ($bp->getRootNodes() as $child) { $conf .= $child->toLegacyConfigString($rendered); $rendered[$child->getName()] = true; } From 588542e3045c8f9b5e887f84b2de184a4f20e586 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 14:17:25 +0100 Subject: [PATCH 149/256] Metadata: simpler auth handling --- library/Businessprocess/Metadata.php | 54 ++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/library/Businessprocess/Metadata.php b/library/Businessprocess/Metadata.php index 6efaefd..eba36e7 100644 --- a/library/Businessprocess/Metadata.php +++ b/library/Businessprocess/Metadata.php @@ -9,6 +9,8 @@ use Icinga\User; class Metadata { + protected $name; + protected $properties = array( 'Title' => null, 'Description' => null, @@ -16,11 +18,37 @@ class Metadata 'AllowedUsers' => null, 'AllowedGroups' => null, 'AllowedRoles' => null, + 'AddToMenu' => null, 'Backend' => null, 'Statetype' => null, // 'SLAHosts' => null ); + public function __construct($name) + { + $this->name = $name; + } + + public function getTitle() + { + if ($this->has('Title')) { + return $this->get('Title'); + } else { + return $this->name; + } + } + + public function getExtendedTitle() + { + $title = $this->getTitle(); + + if ($title === $this->name) { + return $title; + } else { + return sprint('%s (%s)', $title, $this->name); + } + } + public function getProperties() { return $this->properties; @@ -82,7 +110,7 @@ class Metadata return Auth::getInstance(); } - public function permissionsAreSatisfied(Auth $auth = null) + public function canModify(Auth $auth = null) { if ($auth === null) { if (Icinga::app()->isCli()) { @@ -92,6 +120,26 @@ class Metadata } } + return $this->canRead($auth) && ( + $auth->hasPermission('businessprocess/modify') + || $this->ownerIs($auth->getUser()->getUsername()) + ); + } + + public function canRead(Auth $auth = null) + { + if ($auth === null) { + if (Icinga::app()->isCli()) { + return true; + } else { + $auth = $this->getAuth(); + } + } + + if ($auth->hasPermission('businessprocess/showall')) { + return true; + } + if (! $this->hasRestrictions()) { return true; } @@ -100,10 +148,10 @@ class Metadata return false; } - return $this->userIsAllowed($auth->getUser()); + return $this->userCanRead($auth->getUser()); } - public function userIsAllowed(User $user) + protected function userCanRead(User $user) { $username = $user->getUsername(); From ce82446732dface081eef788bcf590826313e9d4 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 14:23:28 +0100 Subject: [PATCH 150/256] ProcessController: less work for BusinessProcess Delegate locking to the renderer, config to the storage --- application/controllers/ProcessController.php | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 5ac20e8..52ff199 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -68,6 +68,15 @@ class ProcessController extends Controller $this->setTitle('Business Process "%s"', $bp->getTitle()); $renderer = $this->prepareRenderer($bp, $node); + + if ($this->params->get('unlocked')) { + $renderer->unlock(); + } + + if ($bp->isEmpty() && $renderer->isLocked()) { + $this->redirectNow($this->url()->with('unlocked', true)); + } + $this->prepareControls($bp, $renderer); $this->content()->addContent($this->showHints($bp)); $this->content()->addContent($this->showWarnings($bp)); @@ -98,11 +107,11 @@ class ProcessController extends Controller } $controls->add(Breadcrumb::create($renderer)); if (! $this->showFullscreen && ! $this->view->compact) { - $controls->add($this->actions()); + $controls->add(new ActionBar($bp, $renderer, $this->Auth(), $this->url())); } } - protected function getNode($bp) + protected function getNode(BusinessProcess $bp) { if ($nodeName = $this->params->get('node')) { return $bp->getNode($nodeName); @@ -123,11 +132,6 @@ class ProcessController extends Controller $renderer->setUrl($this->url()) ->setPath($this->params->getValues('path')); - - if (!$bp->isLocked()) { - $renderer->unlock(); - } - $this->renderer = $renderer; } @@ -218,8 +222,7 @@ class ProcessController extends Controller } } - - public function showHints(BusinessProcess $bp) + protected function showHints(BusinessProcess $bp) { $ul = Element::create('ul', array('class' => 'error')); foreach ($bp->getErrors() as $error) { @@ -286,7 +289,7 @@ class ProcessController extends Controller $this->tabsForConfig()->activate('source'); $bp = $this->loadModifiedBpConfig(); - $this->view->source = $bp->toLegacyConfigString(); + $this->view->source = $this->storage()->render($bp); $this->view->showDiff = (bool) $this->params->get('showDiff', false); if ($this->view->showDiff) { @@ -322,7 +325,7 @@ class ProcessController extends Controller ); header('Content-Type: text/plain'); - echo $bp->toLegacyConfigString(); + echo $this->storage()->render($bp); // Didn't have time to lookup how to correctly disable our renderers // TODO: no exit :) $this->doNotRender(); From 2df97ef432805e38c8ab547805e58ffe6976ee62 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 14:32:47 +0100 Subject: [PATCH 151/256] configuration: add businessprocess to main menu --- configuration.php | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/configuration.php b/configuration.php index 6d4b89a..e96a339 100644 --- a/configuration.php +++ b/configuration.php @@ -1,9 +1,44 @@ menuSection(N_('Overview')) - ->add($this->translate('Business Processes')) - ->setPriority(45) - ->setUrl('businessprocess'); +use Icinga\Module\Businessprocess\Storage\LegacyStorage; $this->providePermission('businessprocess/create', 'Allow to create new configs'); $this->providePermission('businessprocess/modify', 'Allow to modify processes'); +$section = $this->menuSection(N_('Business Processes'), array( + 'url' => 'businessprocess', + 'icon' => 'sitemap', + 'priority' => 46 +)); + +try { + $storage = new LegacyStorage( + $this->getConfig()->getSection('global') + ); + + $prio = 0; + foreach ($storage->listProcesses() as $name) { + $prio++; + + $meta = $storage->loadMetadata($name); + if ($meta->get('AddToMenu') === 'no') { + continue; + } + + if ($prio > 5) { + $section->add(N_('Show all'), array( + 'url' => 'businessprocess', + 'priority' => $prio + )); + + break; + } + + $section->add($meta->getTitle(), array( + 'url' => 'businessprocess/process/show', + 'urlParameters' => array('config' => $name), + 'priority' => $prio + )); + } +} catch (Exception $e) { + // Well... there is not much we could do here +} From 4bda7e84934f809c97322f19f4960a9f739207b0 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 14:36:09 +0100 Subject: [PATCH 152/256] ProcessController: one less helper method --- application/controllers/ProcessController.php | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 52ff199..27fabd8 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -59,7 +59,7 @@ class ProcessController extends Controller */ public function showAction() { - $bp = $this->prepareProcess(); + $bp = $this->loadModifiedBpConfig(); $node = $this->getNode($bp); $this->redirectOnConfigSwitch(); $bp->retrieveStatesFromBackend(); @@ -264,20 +264,6 @@ class ProcessController extends Controller } } - protected function prepareProcess() - { - $bp = $this->loadModifiedBpConfig(); - if ($this->params->get('unlocked')) { - $bp->unlock(); - } - - if ($bp->isEmpty() && $bp->isLocked()) { - $this->redirectNow($this->url()->with('unlocked', true)); - } - - return $bp; - } - /** @@ -285,7 +271,6 @@ class ProcessController extends Controller */ public function sourceAction() { - $this->prepareProcess(); $this->tabsForConfig()->activate('source'); $bp = $this->loadModifiedBpConfig(); @@ -314,7 +299,6 @@ class ProcessController extends Controller */ public function downloadAction() { - $this->prepareProcess(); $bp = $this->loadModifiedBpConfig(); header( @@ -336,7 +320,6 @@ class ProcessController extends Controller */ public function configAction() { - $this->prepareProcess(); $this->tabsForConfig()->activate('config'); $bp = $this->loadModifiedBpConfig(); From d1d639eba8835d2689383256589543fbadb10a9d Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 14:36:45 +0100 Subject: [PATCH 153/256] configuration: change available permissions --- configuration.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/configuration.php b/configuration.php index e96a339..775146f 100644 --- a/configuration.php +++ b/configuration.php @@ -2,8 +2,6 @@ use Icinga\Module\Businessprocess\Storage\LegacyStorage; -$this->providePermission('businessprocess/create', 'Allow to create new configs'); -$this->providePermission('businessprocess/modify', 'Allow to modify processes'); $section = $this->menuSection(N_('Business Processes'), array( 'url' => 'businessprocess', 'icon' => 'sitemap', @@ -42,3 +40,16 @@ try { } catch (Exception $e) { // Well... there is not much we could do here } + +$this->providePermission( + 'businessprocess/showall', + $this->translate('Allow to see all available processes, regardless of configured restrictions') +); +$this->providePermission( + 'businessprocess/create', + $this->translate('Allow to create whole new process configuration (files)') +); +$this->providePermission( + 'businessprocess/modify', + $this->translate('Allow to modify process definitions, to add and remove nodes') +); From f7f230c7a18dccf4955d091f994d7f8e2bca658c Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 14:37:24 +0100 Subject: [PATCH 154/256] ProcessController: change default rendering mode --- application/controllers/ProcessController.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 27fabd8..6755d61 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -124,10 +124,10 @@ class ProcessController extends Controller { if ($this->renderer === null) { - if ($this->params->get('mode') === 'tile') { - $renderer = new TileRenderer($bp, $node); - } else { + if ($this->params->get('mode') === 'tree') { $renderer = new TreeRenderer($bp, $node); + } else { + $renderer = new TileRenderer($bp, $node); } $renderer->setUrl($this->url()) ->setPath($this->params->getValues('path')); From f3817b7d84f6612a11225a82d1314562f237d05d Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 14:39:55 +0100 Subject: [PATCH 155/256] HtmlTag: add new helper class --- library/Businessprocess/Html/HtmlTag.php | 28 ++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 library/Businessprocess/Html/HtmlTag.php diff --git a/library/Businessprocess/Html/HtmlTag.php b/library/Businessprocess/Html/HtmlTag.php new file mode 100644 index 0000000..da49cce --- /dev/null +++ b/library/Businessprocess/Html/HtmlTag.php @@ -0,0 +1,28 @@ +setContent($content); + } + + /** + * @param $content + * @param Attributes|array $attributes + * + * @return Element + */ + public static function p($content, $attributes = null) + { + return Element::create('p', $attributes)->setContent($content); + } +} From f382102cfcd3ae47266f456af43f159dc122b170 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 14:49:39 +0100 Subject: [PATCH 156/256] ProcessForm: introduce a new wizard --- application/forms/ProcessForm.php | 211 ++++++++++++++++++++++-------- 1 file changed, 153 insertions(+), 58 deletions(-) diff --git a/application/forms/ProcessForm.php b/application/forms/ProcessForm.php index ddad40b..b289ada 100644 --- a/application/forms/ProcessForm.php +++ b/application/forms/ProcessForm.php @@ -77,17 +77,6 @@ class ProcessForm extends QuickForm ) )); - $this->addElement('multiselect', 'children', array( - 'label' => $this->translate('Process components'), - 'required' => true, - 'size' => 14, - 'style' => 'width: 25em;', - 'description' => $this->translate( - 'Hosts, services or other processes that should be part of this' - . ' business process' - ) - )); - $this->addElement('text', 'url', array( 'label' => $this->translate('Info URL'), 'description' => $this->translate( @@ -95,62 +84,174 @@ class ProcessForm extends QuickForm ) )); - $this->addElement('submit', $this->translate('Store')); + $this->addElement('select', 'object_type', array( + 'label' => $this->translate('Add children'), + 'required' => $this->node === null || ! $this->node->hasChildren(), + 'ignore' => true, + 'class' => 'autosubmit', + 'multiOptions' => $this->optionalEnum( + array( + 'hosts' => $this->translate('Host'), + 'service' => $this->translate('Service'), + 'process' => $this->translate('Another process'), + 'include' => $this->translate('External process'), + ) + ) + )); + + switch ($this->getSentValue('object_type')) { + case 'hosts': + $this->addHostsElement(); + break; + case 'service': + $this->addHostElement(); + if ($host = $this->getSentValue('host')) { + $this->addServicesElement($host); + } + break; + case 'process': + $this->addProcessesElement(); + break; + } } - public function setBackend($backend) + protected function addHostsElement() + { + $this->addElement('multiselect', 'children', array( + 'label' => $this->translate('Hosts'), + 'required' => true, + 'size' => 14, + 'style' => 'width: 25em', + 'multiOptions' => $this->enumHostList(), + 'description' => $this->translate( + 'Hosts that should be part of this business process node' + ) + )); + } + + protected function addHostElement() + { + $this->addElement('select', 'host', array( + 'label' => $this->translate('Host'), + 'required' => true, + 'ignore' => true, + 'class' => 'autosubmit', + 'multiOptions' => $this->optionalEnum($this->enumHostList()), + )); + } + + protected function addServicesElement($host) + { + $this->addElement('multiselect', 'children', array( + 'label' => $this->translate('Services'), + 'required' => true, + 'size' => 14, + 'style' => 'width: 25em', + 'multiOptions' => $this->enumServiceList($host), + 'description' => $this->translate( + 'Services that should be part of this business process node' + ) + )); + } + + protected function addProcessesElement() + { + $this->addElement('multiselect', 'children', array( + 'label' => $this->translate('Process nodes'), + 'required' => true, + 'size' => 14, + '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; - $this->fetchObjectList(); - $this->fillAvailableChildren(); return $this; } - protected function fillAvailableChildren() + /** + * @param BusinessProcess $process + * @return $this + */ + public function setProcess(BusinessProcess $process) { - if (empty($this->processList)) { - $children = $this->objectList; - } else { - $children = array( - $this->translate('Other Business Processes') => $this->processList - ) + $this->objectList; - } - - $this->getElement('children')->setMultiOptions($children); - } - - public function setProcess($process) - { - $this->process = $process; + $this->bp = $process; $this->setBackend($process->getBackend()); - $this->processList = array(); - foreach ($process->getNodes() as $node) { - if ($node instanceof BpNode) { - // TODO: Blacklist parents - $this->processList[(string) $node] = (string) $node; // display name? - } - } - natsort($this->processList); - $this->fillAvailableChildren(); return $this; } - public function setNode(Node $node) + /** + * @param BpNode $node + * @return $this + */ + public function setNode(BpNode $node) { $this->node = $node; - - $this->setDefaults(array( - 'name' => (string) $node, - 'alias' => $node->hasAlias() ? $node->getAlias() : '', - 'display' => $node->getDisplay(), - 'operator' => $node->getOperator(), - 'url' => $node->getInfoUrl(), - 'children' => array_keys($node->getChildren()) - )); - $this->getElement('name')->setAttrib('readonly', true); return $this; } + /** + * @param SessionNamespace $session + * @return $this + */ + public function setSession(SessionNamespace $session) + { + $this->session = $session; + return $this; + } + + 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 + return array_combine((array) $names, (array) $names); + } + + 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 enumProcesses() + { + $list = array(); + + foreach ($this->bp->getNodes() as $node) { + if ($node instanceof BpNode) { + // TODO: Blacklist parents + $list[(string) $node] = (string) $node; // display name? + } + } + + natsort($list); + return $list; + } + protected function fetchObjectList() { $this->objectList = array(); @@ -184,15 +285,9 @@ class ProcessForm extends QuickForm return $this; } - public function setSession($session) - { - $this->session = $session; - return $this; - } - public function onSuccess() { - $changes = ProcessChanges::construct($this->process, $this->session); + $changes = ProcessChanges::construct($this->bp, $this->session); $modifications = array(); $children = $this->getValue('children'); @@ -246,7 +341,7 @@ class ProcessForm extends QuickForm Notification::success( sprintf( 'Process %s has been modified', - $this->process->getName() + $this->bp->getName() ) ); } From 8ef7bdbfb4fee17653ed23af303e48283b6a3167 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 14:50:19 +0100 Subject: [PATCH 157/256] Renderer: always preserve config property --- library/Businessprocess/Renderer/Renderer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Businessprocess/Renderer/Renderer.php b/library/Businessprocess/Renderer/Renderer.php index 6b87432..a364949 100644 --- a/library/Businessprocess/Renderer/Renderer.php +++ b/library/Businessprocess/Renderer/Renderer.php @@ -181,7 +181,7 @@ abstract class Renderer extends Html */ public function setBaseUrl(Url $url) { - $this->baseUrl = $url->without(array('config', 'node', 'path')); + $this->baseUrl = $url->without(array('node', 'path')); return $this; } From 945b14fd60bbc01fde7bf1de0e491934e1212e65 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 14:54:31 +0100 Subject: [PATCH 158/256] BusinessProcess: drop locking code --- library/Businessprocess/BusinessProcess.php | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index 8d4aa1d..cc11954 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -109,8 +109,6 @@ class BusinessProcess */ protected $simulation; - protected $locked = true; - protected $changeCount = 0; protected $simulationCount = 0; @@ -249,22 +247,6 @@ class BusinessProcess return false; } - public function isLocked() - { - return $this->locked; - } - - public function lock($lock = true) - { - $this->locked = (bool) $lock; - return $this; - } - - public function unlock() - { - return $this->lock(false); - } - public function hasSimulations() { return $this->countSimulations() > 0; @@ -441,6 +423,9 @@ class BusinessProcess return count($this->root_nodes); } + /** + * @return BpNode[] + */ public function getRootNodes() { ksort($this->root_nodes); From 8219de14910d65b152c6f5cde08fe14f4937ca20 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 14:56:18 +0100 Subject: [PATCH 159/256] BusinessProcess: more cleanup --- library/Businessprocess/BusinessProcess.php | 88 ++++++++++----------- 1 file changed, 42 insertions(+), 46 deletions(-) diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index cc11954..5d2c661 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -123,7 +123,7 @@ class BusinessProcess public function getMetadata() { if ($this->metadata === null) { - $this->metadata = new Metadata(); + $this->metadata = new Metadata($this->name); } return $this->metadata; @@ -373,7 +373,7 @@ class BusinessProcess foreach ($hostStatus as $row) { $this->handleDbRow($row); } - + // TODO: Union, single query? ksort($this->root_nodes); Benchmark::measure('Got states for business process ' . $this->getName()); @@ -573,16 +573,32 @@ class BusinessProcess throw new ProgrammingError('Not implemented yet'); } - public function listBpNodes() + /** + * @return BpNode[] + */ + public function getBpNodes() { $nodes = array(); foreach ($this->nodes as $node) { - if (! $node instanceof BpNode) { - continue; + if ($node instanceof BpNode) { + $nodes[$node->getName()] = $node; } + } - $name = (string) $node; + return $nodes; + } + + /** + * List all business process node names + * + * @return array + */ + public function listBpNodes() + { + $nodes = array(); + + foreach ($this->getBpNodes() as $name => $node) { $alias = $node->getAlias(); $nodes[$name] = $name === $alias ? $name : sprintf('%s (%s)', $alias, $node); } @@ -591,42 +607,57 @@ class BusinessProcess return $nodes; } + /** + * All business process nodes defined in this config but not + * assigned to any parent + * + * @return BpNode[] + */ public function getUnboundNodes() { $nodes = array(); - foreach ($this->nodes as $node) { - if (! $node instanceof BpNode) { - continue; - } + foreach ($this->getBpNodes() as $name => $node) { if ($node->hasParents()) { continue; } if ($node->getDisplay() === 0) { - $nodes[(string) $node] = $node; + $nodes[$name] = $node; } } return $nodes; } + /** + * @return bool + */ public function hasWarnings() { return ! empty($this->warnings); } + /** + * @return array + */ public function getWarnings() { return $this->warnings; } + /** + * @return bool + */ public function hasErrors() { return ! empty($this->errors) || $this->isEmpty(); } + /** + * @return array + */ public function getErrors() { $errors = $this->errors; @@ -679,41 +710,6 @@ class BusinessProcess return $this; } - public function toLegacyConfigString() - { - $settings = array(); - if ($this->hasTitle()) { - $settings['Title'] = $this->getTitle(); - } - // TODO: backendName? - if ($this->backend) { - $settings['Backend'] = $this->backend->getName(); - } - $settings['Statetype'] = $this->usesSoftStates() ? 'soft' : 'hard'; - - if (false) { - $settings['SLA Hosts'] = implode(', ', array()); - } - - $conf = "### Business Process Config File ###\n#\n"; - foreach ($settings as $key => $value) { - $conf .= sprintf("# %-9s : %s\n", $key, $value); - } - - $conf .= "#\n###################################\n\n"; - - $rendered = array(); - foreach ($this->getChildren() as $child) { - $conf .= $child->toLegacyConfigString($rendered); - $rendered[(string) $child] = true; - } - foreach ($this->getUnboundNodes() as $node) { - $conf .= $node->toLegacyConfigString($rendered); - $rendered[(string) $node] = true; - } - return $conf . "\n"; - } - public function beginLoopDetection($name) { // echo "Begin loop $name\n"; From 1817077086e14f4ae2adfa66e1a5e17fef189989 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 15:33:30 +0100 Subject: [PATCH 160/256] css: style warnings --- public/css/module.less | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/public/css/module.less b/public/css/module.less index fb7ebde..45f7c3e 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -641,7 +641,7 @@ div.bp .badges { /** END of breadcrumb **/ -ul.error { +ul.error, ul.warning { padding: 0; list-style-type: none; background-color: @colorCritical; @@ -654,9 +654,19 @@ ul.error { li a { color: inherit; + text-decoration: underline; + + &:hover { + text-decoration: none; + } } } + +ul.warning { + background-color: @colorWarning; +} + table.sourcecode { font-family: monospace; white-space: pre-wrap; From 74d614be377e6576959071d0cc078a4427d77cc0 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 16:08:25 +0100 Subject: [PATCH 161/256] Tabs: add renderable wrapper --- library/Businessprocess/Web/Component/Tabs.php | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 library/Businessprocess/Web/Component/Tabs.php diff --git a/library/Businessprocess/Web/Component/Tabs.php b/library/Businessprocess/Web/Component/Tabs.php new file mode 100644 index 0000000..a6e5e6a --- /dev/null +++ b/library/Businessprocess/Web/Component/Tabs.php @@ -0,0 +1,10 @@ + Date: Fri, 9 Dec 2016 16:10:32 +0100 Subject: [PATCH 162/256] ProcessController: change tab rendering and more --- application/controllers/ProcessController.php | 16 +++++++++----- library/Businessprocess/BusinessProcess.php | 3 +-- library/Businessprocess/Web/Controller.php | 22 +++++++++++-------- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 6755d61..a464ab4 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -19,6 +19,7 @@ use Icinga\Module\Businessprocess\Web\Controller; use Icinga\Module\Businessprocess\Web\Url; use Icinga\Web\Notification; use Icinga\Web\Widget\Tabextension\DashboardAction; +use Icinga\Web\Widget\Tabs; class ProcessController extends Controller { @@ -101,7 +102,7 @@ class ProcessController extends Controller ); } - $this->addProcessTabs($bp); + $controls->add($this->getProcessTabs($bp, $renderer)); if (! $this->view->compact) { $controls->add(Element::create('h1')->setContent($this->view->title)); } @@ -138,16 +139,18 @@ class ProcessController extends Controller return $this->renderer; } - protected function addProcessTabs($bp) + protected function getProcessTabs(BusinessProcess $bp, Renderer $renderer) { if ($this->showFullscreen || $this->view->compact) { return; } - $tabs = $this->defaultTab(); - if (! $bp->isLocked()) { + $tabs = $this->singleTab($bp->getTitle()); + if (! $renderer->isLocked()) { $tabs->extend(new DashboardAction()); } + + return $tabs; } protected function handleSimulations(BusinessProcess $bp) @@ -264,8 +267,6 @@ class ProcessController extends Controller } } - - /** * Show the source code for a process */ @@ -364,6 +365,9 @@ class ProcessController extends Controller )); } + /** + * @return Tabs + */ protected function tabsForCreate() { return $this->tabs()->add('create', array( diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index 5d2c661..99f7b5e 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -201,8 +201,7 @@ class BusinessProcess public function getTitle() { - $meta = $this->getMetadata(); - return $meta->has('Title') ? $meta->get('Title') : $this->getName(); + return $this->getMetadata()->getTitle(); } public function hasTitle() diff --git a/library/Businessprocess/Web/Controller.php b/library/Businessprocess/Web/Controller.php index 5ab5b0c..a2a4212 100644 --- a/library/Businessprocess/Web/Controller.php +++ b/library/Businessprocess/Web/Controller.php @@ -4,19 +4,17 @@ namespace Icinga\Module\Businessprocess\Web; use Icinga\Application\Icinga; use Icinga\Module\Businessprocess\BusinessProcess; -use Icinga\Module\Businessprocess\Html\HtmlString; use Icinga\Module\Businessprocess\Modification\ProcessChanges; use Icinga\Module\Businessprocess\Storage\LegacyStorage; use Icinga\Module\Businessprocess\Storage\Storage; use Icinga\Module\Businessprocess\Web\Component\ActionBar; use Icinga\Module\Businessprocess\Web\Component\Controls; use Icinga\Module\Businessprocess\Web\Component\Content; +use Icinga\Module\Businessprocess\Web\Component\Tabs; use Icinga\Module\Businessprocess\Web\Form\FormLoader; use Icinga\Web\Controller as ModuleController; use Icinga\Web\Notification; use Icinga\Web\View; -use Icinga\Web\Widget; -use Icinga\Web\Widget\Tabs; class Controller extends ModuleController { @@ -30,7 +28,7 @@ class Controller extends ModuleController protected $bp; /** @var Tabs */ - protected $tabs; + protected $mytabs; /** @var Storage */ private $storage; @@ -150,13 +148,13 @@ class Controller extends ModuleController protected function tabs() { // Todo: do not add to view once all of them render controls() - if ($this->view->tabs === null) { - $tabs = Widget::create('tabs'); - $this->controls()->add(HtmlString::create($tabs)); - $this->view->tabs = $tabs; + if ($this->mytabs === null) { + $tabs = new Tabs(); + //$this->controls()->add($tabs); + $this->mytabs = $tabs; } - return $this->view->tabs; + return $this->mytabs; } protected function session() @@ -258,6 +256,9 @@ class Controller extends ModuleController return FormLoader::load($name, $this->Module()); } + /** + * @return LegacyStorage|Storage + */ protected function storage() { if ($this->storage === null) { @@ -269,6 +270,9 @@ class Controller extends ModuleController return $this->storage; } + /** + * @deprecated + */ protected function loadSlas() { $bpconf = $this->bpconf; From 135197cc4575ea6ba7861aa8a10abf8f38be3046 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 16:11:03 +0100 Subject: [PATCH 163/256] css: no fixed height for container, link has one --- public/css/module.less | 1 - 1 file changed, 1 deletion(-) diff --git a/public/css/module.less b/public/css/module.less index 45f7c3e..e4160f9 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -377,7 +377,6 @@ div.knightrider table.bp { .tiles > div { width: 12em; - height: 6em; float: left; margin-right: 0.2em; margin-bottom: 0.2em; From 72e7aa798c6fc28747c372bf7773b9b5f8dd0e39 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 9 Dec 2016 16:59:25 +0100 Subject: [PATCH 164/256] Rendering: improve tile sizes, fullscreen mode --- application/controllers/ProcessController.php | 10 ++++---- library/Businessprocess/Renderer/Renderer.php | 24 +++++++++++++++++++ .../Businessprocess/Renderer/TileRenderer.php | 6 ++--- public/css/module.less | 6 ++--- 4 files changed, 36 insertions(+), 10 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index a464ab4..2670ef7 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -102,7 +102,9 @@ class ProcessController extends Controller ); } - $controls->add($this->getProcessTabs($bp, $renderer)); + if (! ($this->showFullscreen || $this->view->compact)) { + $controls->add($this->getProcessTabs($bp, $renderer)); + } if (! $this->view->compact) { $controls->add(Element::create('h1')->setContent($this->view->title)); } @@ -141,9 +143,6 @@ class ProcessController extends Controller protected function getProcessTabs(BusinessProcess $bp, Renderer $renderer) { - if ($this->showFullscreen || $this->view->compact) { - return; - } $tabs = $this->singleTab($bp->getTitle()); if (! $renderer->isLocked()) { @@ -175,6 +174,9 @@ class ProcessController extends Controller { $action = $this->params->get('action'); $form = null; + if ($this->showFullscreen) { + return; + } if ($action === 'add') { $form =$this->loadForm('AddNode') diff --git a/library/Businessprocess/Renderer/Renderer.php b/library/Businessprocess/Renderer/Renderer.php index a364949..cb5a32a 100644 --- a/library/Businessprocess/Renderer/Renderer.php +++ b/library/Businessprocess/Renderer/Renderer.php @@ -86,6 +86,30 @@ abstract class Renderer extends Html return $this->parent->getParents(); } + /** + * @return BpNode[] + */ + public function getChildNodes() + { + if ($this->wantsRootNodes()) { + return $this->bp->getRootNodes(); + } else { + return $this->parent->getChildren(); + } + } + + /** + * @return int + */ + public function countChildNodes() + { + if ($this->wantsRootNodes()) { + return $this->bp->countChildren(); + } else { + return $this->parent->countChildren(); + } + } + /** * @param $summary * @return Container diff --git a/library/Businessprocess/Renderer/TileRenderer.php b/library/Businessprocess/Renderer/TileRenderer.php index 62e8e6c..56029be 100644 --- a/library/Businessprocess/Renderer/TileRenderer.php +++ b/library/Businessprocess/Renderer/TileRenderer.php @@ -63,12 +63,12 @@ class TileRenderer extends Renderer */ protected function howMany() { - $count = $this->bp->countChildren(); + $count = $this->countChildNodes(); $howMany = 'normal'; - if ($count < 20) { + if ($count <= 6) { $howMany = 'few'; - } elseif ($count > 50) { + } elseif ($count > 12) { $howMany = 'many'; } diff --git a/public/css/module.less b/public/css/module.less index e4160f9..0ddc860 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -505,13 +505,13 @@ div.bp .badges { } .tiles.few { font-size: 3em; } -.tiles.normal { font-size: 2em; } -.tiles.many { font-size: 1.5em; } +.tiles.normal { font-size: 2.4em; } +.tiles.many { font-size: 1.8em; } #layout.twocols, #layout.layout-minimal, .compact { .tiles.few { font-size: 1.8em; } .tiles.normal { font-size: 1.4em; } - .tiles.many { font-size: 0.9em; } + .tiles.many { font-size: 1.2em; } } #layout.fullscreen-layout .controls { From 1f17ca8a245cfc786a0dba8cf7955a9a9c332fa7 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 12 Dec 2016 13:56:31 +0100 Subject: [PATCH 165/256] views/scripts: remove obsolete files --- application/views/scripts/node/add.phtml | 6 ------ application/views/scripts/node/simulate.phtml | 8 -------- 2 files changed, 14 deletions(-) delete mode 100644 application/views/scripts/node/add.phtml delete mode 100644 application/views/scripts/node/simulate.phtml diff --git a/application/views/scripts/node/add.phtml b/application/views/scripts/node/add.phtml deleted file mode 100644 index ddc111d..0000000 --- a/application/views/scripts/node/add.phtml +++ /dev/null @@ -1,6 +0,0 @@ -controls->render() ?> - -
-

translate('Add new process node') ?>

-form ?> -
diff --git a/application/views/scripts/node/simulate.phtml b/application/views/scripts/node/simulate.phtml deleted file mode 100644 index 3ee903b..0000000 --- a/application/views/scripts/node/simulate.phtml +++ /dev/null @@ -1,8 +0,0 @@ - - -
-

BI Simulation: escape(preg_replace('/;/', ' - ', $this->node)) ?>

-form ?> -
From 2aab94adf381bad76447732a8d657808cc40d25d Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 12 Dec 2016 13:56:52 +0100 Subject: [PATCH 166/256] application/locale: refresh German translation --- .../de_DE/LC_MESSAGES/businessprocess.mo | Bin 6507 -> 9966 bytes .../de_DE/LC_MESSAGES/businessprocess.po | 603 +++++++++++++----- 2 files changed, 437 insertions(+), 166 deletions(-) diff --git a/application/locale/de_DE/LC_MESSAGES/businessprocess.mo b/application/locale/de_DE/LC_MESSAGES/businessprocess.mo index f8b048442eb1500f7ac7e3620f412f2eddd04ded..dcdc468d903a025840eda9efb33ff72f9bcaaddd 100644 GIT binary patch literal 9966 zcmbW63v6W9S%6O-4b)Il(xyq8G(AacVkbL}?PQaUcQ=W>wqs}4_GWFb+dP`ynK?6a z<2(1x-h0QhyX4VEEfJ!q4<(|A0-}V61O>IKf)Fbtus{k$fJjhLivkrOgd#zL8l^(w z@%`tVdp)yu0-KS~eD`tgx##?^^Z(;luDJ9$#ph|-b+mn#D)lvZ=!f~@^V*P7H^bNA zPr&PbM5!N#N8sgf9bO9!{2=_azyB4u!TImNOW#rY#S8~1p zc|?5z-U%Orhu~M?QTX>z-h0b?lzJCD2>DafeEkqyf$xPIP{wP)Yv7ac3ixGs8T>8y zQTPpi{%v>*=dZvI!J!{>{YRk8_aKz#?twD?Jd}BEKzVNsihMTV_3%qj`hDH=k0GK} zFF{%N-@*v~03uptSd_@M1Et^7P}cVh6gm7XtieBl^4?Jf7ZvS?(*JH~^#bL&bj_^a>+cn6XvEQ3njM&LMl=DwQS^wiu^!_Xqx&8taef^3*|80N& z8xYl~--j~Ji%{(7A7KSv^nPa_x581*PeK{*Q=UHsWnQ0yBEMgR%Y^EcoGycQwI`feY|Ap!m^iP~>%pPBNeSq3A1zGw}292>fd( zdi?;x`1yc%6p!GGXF0^>GvFzaeoOCn$$O;*zF%d zvF~rekH8;5>31DQA@iApqW>fCCb$TD@D!AB{}qb;{Rb3#7(y7*|8jUIyb^Zc5)}FU zA(ZF-49dD+g7UuaLV50W&o@2)%kvU~k=%c`=jEPPdS2^!1H{zSO;E!_kRj9~Q0DnK zlsNG$l=prO%DCH5=KmrT{r&~i@O3D5eFZ0S{~9Rkx*pyOPeM8WIVke^ODJ-E6N>yU zy1_kn1(bDP1*f5g^8P9$w5rok=mG8*nveopwKlQ5$_;?KqYFlXnV5S6Jh zn%IHZ5~{X)?z`YLP3%E*E1#BwT7*@9ED)QN&jjr;d*u8?b1Zf*F;hO`TPrm2jR$CA zvtr**(T>pCwEJoD8F5g@JkNLv#0KTlbxgJUXM>NosW6>ro4q(Ln=X{MuuwP~%UmuJ;kzd$r;vK2r$bL?370EN34uJycW-&Q@K!Q zwC<6&(6~C%O;K~+=QC<{?mjhZB9j{rXLZ&#W_RpS&4yV!%rZ5bSV1wf2>a~?jE36k zc7*;%lU^Jp0lqij6@_ojoer~HT(Xet+-XcX4x*yJn#XR`e3G_t=?uNp#J3t*IadcuBB+V9)!_wDw3zx}?$e&1=o57_U!)Y71Tiyc{8_Sbhu7k}ez1k>Nc+GYQR za<&plLza}bohK}>tf}RIbuQ!nB1w~`sFq-h2A)i`2U&U;~vxk`up zOh=CLosg(jPNIqR zQmZbL;L@(A${b@y8E%KF7f;EaU0O(na$V}6R>ei#w_X!vt)dscyVZ6`r*737uufw3 zq19fPV-Q4C33V>oNHj8PjfL9dp3w0tSD>rE=Apa`Y zyLhimdU%f70?p{!>+dJiV3+UP?zMNiq}`Ob;iYYF+zmWc|-Qfnex;*9PjAu~cN zEYBuBeeUYB8yf{=nQEQwgRtZ<7?{>Ova=`$l(ozGd^?y^%5~M}6YE*m?wWR$xkyX*Led%b z2N7>y(kCJq({rcjpXrz=ro)EK7MkG}`_KP>Ag|@O8S7*yvkK3;sJUUHTEgp4k=;Od z$*Rn4HtdE(KbiWVg1Chw2J_dxJ*i4IT*uzE-|@&~IZbtzG>R=v{U1X_VZ6y0VQfX- z4mjxBJ||tS`f+UXc$&ygSy`-CPLeC*rBh{OFFb(lq>Cy>CdbCd#z%)vm`;-Bm8Gm1 z)+;mJW>#5CrqJcqy`kNq$C9);bbMv4VskA^O&e|qA!soN%R87Eh# zW95X|3gyjv_PhOn>vt^3^2%Bo#2Lj*l1}NhR?@~b?=w+DAIj~?y-iNWVw2xJbd0p4 z8#LKl>*mqfxuK=SrMaCIPK=EY5wLS2Kt+Pil+M}MZtX+}>Yyzhh){>u*XAn+cKXS? zDQGHlG2s?Boze%YVLo&`B58{%l8R9f#T|Q^9iBW$T_bl6kHq?h!+K)$U^%JEnhDxd zdgx=7`%LV1qA7hmG4+u8W$eJ%onsS2wuB>f>XB%(C2Jpi5z`1&lkRoXx*6esB`_&+ zz2UmvLh>d~+EgM&Z-0gd({9`x>`&_5OVS2TBK6zRT@~E}T@zvAL>ReWp6PX@m1a~z zhFc?>hO|*4*jcj9%D^a^Id4DTigGFK5Reo>a!$!vQFHQ`$$KcyRdtl8DFYilLoVG) zO;}~dx*nR0-Pn&W#fv;s)^-n?u-0O{2qmj^syZIdx1Z0$COKvppY1lb&mjo1l`g#s z7p?f=bgRD$>2M>7ns{UXCPI)wwhZy2PqFt3y^fg5>og zsgw#(rn0xdM#MkDQRSY`_0Gz7sy`k++jV(&covzNcpveg7aDz(B8j_p_QlHCrGq*M(Us+gDTjX|}w{e0XEn^;qR$J(qbs;NNc&VnxT@>bxKFkS-QJ;EYzY3J$0N_x#aHrtJ~+Q5~gacyu4EkVAOye z${99`ot5jWNV3so!;t4u5g7(bBH|5a2*@d{Jg|zhdMho+&#VxN&!hJ?veE$5^kyzT z)$hW#syw#+0w$06{DHIj*eg_Qx^xIdZ0?~jIxlU%z~s?`$kh5t@dSHix*4%F9Tg+* zZEvWdE8ZLh$VRzRQ!EMvmg;l$X5R&CqiPn@mPp4hN~w!Mh*A6p&*jA!RfZwt>ZD|Q zl3HyBaqQ-5t!cJj`rD2f_KN3QAvnF)JHayaK(RSjpLhz{SHv_wt2{v7YTON-I*7u2RCFGta^j{6MzWyzts| zrVymJLF*Vh7wuAZGJPKyF;p-9^CrO$ZK9H0w_U*6eZ83bhNzMqVL)7JdGW}CFIaH~ zT!X-k^Rip8DYLtz7>pQO?hDs}aN8FmCFn6L_Y!u95D&<_DaQ#mEL&UY@r4N#+Twm` z!sT-@nWuk{ea!zRRzB(Dvz3;ub$13h%B$E8<2>u6MnVC`?4>XeW7tv~M07)>vOlQJ zvRZ}}6R{)OWPl$o_&t|wx^H~DZVDo#ClPNa=Bt(2xxO-ZT718JHyic^JB6S2U@D-> ztIibOx(>eZ7fwq=4dSyV6m{<8Ddi=SgrlwLzhJle(kmPHGn>Vn zi?K5aRS$M4WROv&{HT(j#5QPD19AbWLhMAiIKu^-=fpNfh3$6V2(I?F`*(j~>=an0 za(&r{yTdPNVjOk1PFF5>`5D1(XQE%C0r8hSP)%2DSCSuo-lKdj>`+P0QiMk;X12i| zHkYyg@xx}?zg{mH8CT-H-M_`ZLZ;9q$-*@;JzqO}VR}s?p;?hlxsg!w=Tbi7r-q*z z>?GNg%kVrR{{lGg5z(oA$d=MJ^{^TsFCq6lisQ?qsisX-R@3D&t@x#ymUPLq(!nsI zZT?UMDj$jF!~tD2Ef0$>eqFvyR>#;fU1CrgmG3c_N@sOR>2!;@@Q$95`r2kI()Uvf04p zcG#fW;P-@0TOnZ}sowrdkxnvYo2Z|!SEDv4;o12MN$i@7->ub3byt^e!I#U4Y1aW+`FF9h9Aj$o-h2M zX6~KdM`n{MrNz<@A{0cZh+m{4QWPq(_+e2oC{px&iKjx5B%i40QlL0Y@QS)l2YZ_%hrLzX$nKmpBc=U%&_875EW& z^L0vn0^R{{fum5ynS!5$k3*5G1LxrLunI53>*3*nZ2WO3{idPF^`v9f(RU0TQ^#i< zpL2ZC@vHC-#`zkQ^}OnM30}kfoA4;S3^lwSVNkK!?|1;px(~vS!{boaRfc!L&%<4? z3vYujK+)GXAxl#~fHLooq1fS9P~`bL6utci$~v#XD2Sj2pxoaE#eR3g``|uUfU{8S z{0!U+zYV4T8&KxC3`M@b!eMv?iaqW@NSSXK4#Fc)#(&bW4Mn~$LDBy=;QjDbcozN^ zPQyEH%+_0lGSAac^s@>@AJ4;a_$|oP>Q7Mo>}~kkRp^#U4{-k@gcbSz24%k6I5CBK z5Xv~CQ2ccsieAc4^#2r;`7XdNd=82n??Q%BSD}0^)ov*L9)SB`0T$rr-18Tq=Kdvk zJA4g_y0|KZ+Wfg;yGq3G*fDEisWAfng1Af`~G zP{y6$gy`xCDC-X(OI8=)o$xD;-+|&kKXv>il>Toz{sD?V{TYhi{0kn0x1wC(8Mq&= zLDA25q1fyDP{#QQ6#KjmMecW?%y$z?co^OeS&Dkh@hK>FN}$O3GJFXB5H7>FU<*z$ znAqzg6g~VL%6z|uqQBq4Q}EwV^l*~Fm|B$~?y44Ov$Q=lIb^KO$2>PDHb3C{n7c;= zb(r=U+BurclOGamqGvfIp3l-w(qzq#(d0NnE7L?yIYcfw4$)?4;^%TalELahE?J6= zLAjPS%ON(9Lv$qj$mZiTHPZq;8sv2I@%+`B7;n(XX zGC|eEs#uy(#p-epc3Y;_VAf)-rUTQ}ohametOFULh{&s|D0jM&#(p4g@*Yhuc7mHa zZ)$qgBziSWb=z+=6W#TKL?@xHM#f8w_Vi$^!$_x{R_LLOfcqge?p2prs~XP-pU z_5hJjElicCP8DZVzG@@0MY`@sak5Q3dZjIv6ec<{N#q;UoJ$@jNHm53>Xa4E0U1RD zTNkP(Ov|u>r0Mq-uO|Gs?Z>g22)lt@P(F6G&m?D2Ma!xqGi9vKhH(<_*RhFKd=xDz z36rLYa^ZNCG`&Q}O)Gx|sl6!CVLh9Fn{ZQA9L3Y;O-0X!fuDqtIF8e+?lQ8`)ilEF z3{v@msWP-IKh|}o-n^zwQ|210>3S5l)fCRjIupNbxUPqKX<k8D~S5;xm2J&iuXPAx~)s z&y?=j`tBc$@829^8=lICHKX18EsmUYF^!s=R&xt#u44$VFj8~YN?RQjznq^eO-z-} zsCh5Ks`$ONKqwAACLxKN&9~BqALuYmI%%Suk8g?I{Ao38cftU3#cIKH!YIL>YOxu1 zb)3a^RU&h-mqb{e6F)NTaD`Cb>0N9RmE{JtDCW=3y1d$vOEjw%(@O5cVL;TE#oc9W z2_c)H{C1~h+DMkgde(zZC`PA&|8$CZYP|OAK6=PK)(x8V&S#Kucgk}^{oM$HuI=b@ zCKFrdB_q<)Pg+KmLxfr}E$#HDmP*s5x$`B3kLph7BM$LE9%^rKAMI7JN9MuSzm^E( ztuDP}vz5v~e%Km*ItzDl5TY!_MxQM%Ot=8E_lt9<%bV}>P^_7{m$p!>OMvoi{?0DZ zel42K6AEkJ{!!|+TC3R_5>bcl`mGk$l34Br8zC+W9MY3d(;tHpH`=dT!#4aC6EN5R zI*>Eq`;2wYm%;qCEJn{47fKQw=e;Pv5V1OMGZOX?3H$R0uWXFHM^dVIF*+cwT*_;D z5w{UxQO|RlG8g>V8k`7HJ0^47M3P`(s#Z8>qL^KHOy@zJdnc(UMVEUH4h;_t7b+(4 zM)nS5!C07$8-A@Yo;Kn_IUK_zD<^%Mf``JWF)%+@F4#SVaSIc6mpM2*GFljZxG-{9 z4<8;IIksqioM zbF#tBP+*cr2WF@jQm?`0Rx?jcO!n>zW#hHS^h~jIW~q2)Qa>^?bF$@&$d3-pPR&kk z=wf7Ocz_KyA&v`@9maHGE+hv!Ezb{*OF=@COHM46PZy4E^b`3gEeewX$q6%$>7x}t z86fRNUaN4Lj7=U4I`%d`aqzg#E>G+U^nEAv$ll`vpDmm*L6&L9^n7S)exgT)Mu!d! z*wTqAs7sw7+wewq+hc;gx|>Edvxf@=MuZ@d{wplZ1Yw(GW%T-&`7la@Mt}bm6LrI= zj`vHFp6=VW<;mlzX|-&w?Yq-U=q8G*O-eaJo-%6O#L4EuW_Nq<2Y}gcX=PTfuYf@hk<$gd0rG%2T?bq$~i`6DOcA{&k zM(lQ)w0u72Yo-~DHS#0~%MD>3Z9H!>Ny{T8Z!fg+*?NUm{=ledsk0VOomII`ki{UC zoy|_QU1;fxUg#A_B9?U&WS>dNHi()aV%tv;l4xT;oOA*^g>6$oZ$WBi{UW+%VBf?W zV=F?pYpn6|*>YiR+R-GLRP1C|uX-N92)w~Nnx=wPTrIZPtHnfno_LGNS3yQKEfJt> z{8lDKyv<}38?l%&4#fy+g7|#@vTTJcmW|t%F?FV@g49;7xIt1}C)%=ZGW%MHKk+f1 z(VLL^OzO~HeO8eAY>~o6?AEWi4_2d%8!xID9|LE;ZH>6`(9My(Q|F~4Gxx0q$ER4E zj5D7OHvHdg(`Ft&cKxqP{mcqFf7?e>k05Q-^po0cVscaUR?Mur)zeIW z^;VE=icFFHJn66A3dJ;Tcon{I{@v$so1VJz4M*6h-+IY}k=KkbDPbdzWjfhNVB6j_ z#P@4dc6OMoc1vSh!liMt+7Vk5Wn@29;s+6xTprEV7c(WJg`k@&{CjiBxka&csdJPSo{g<*B$Z_+Os1}_qp8!9FxV*9FE#_aL3Dj8B&&)@Tb1_Z6t_*ue;BMasOdyc zkInVQ0J*7Nxb<& zw@u}erJAd3>yp-8qJB_*>R^~`1C@%h5NE%VsXAr<%W|!9o1m>S* E3#ba+EdT%j diff --git a/application/locale/de_DE/LC_MESSAGES/businessprocess.po b/application/locale/de_DE/LC_MESSAGES/businessprocess.po index 2f2c880..53528d3 100644 --- a/application/locale/de_DE/LC_MESSAGES/businessprocess.po +++ b/application/locale/de_DE/LC_MESSAGES/businessprocess.po @@ -1,135 +1,258 @@ # Icinga Web 2 - Head for multiple monitoring backends. -# Copyright (C) 2015 Icinga Development Team +# Copyright (C) 2016 Icinga Development Team # This file is distributed under the same license as Businessprocess Module. # FIRST AUTHOR , YEAR. # msgid "" msgstr "" -"Project-Id-Version: Businessprocess Module (2.0.0-beta1)\n" +"Project-Id-Version: Businessprocess Module (2.0.0)\n" "Report-Msgid-Bugs-To: dev@icinga.org\n" -"POT-Creation-Date: 2015-03-16 06:19+0100\n" -"PO-Revision-Date: 2015-03-16 09:04+0100\n" +"POT-Creation-Date: 2016-12-12 13:32+0100\n" +"PO-Revision-Date: 2016-12-12 13:48+0100\n" "Last-Translator: Thomas Gelf \n" "Language: de_DE\n" -"Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -"X-Generator: Poedit 1.5.4\n" +"Language-Team: \n" +"X-Generator: Poedit 1.8.7.1\n" -#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:83 +#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:174 #, php-format msgid "%d applied simulation(s) have been dropped" msgstr "%d angewendete Simulation(en) wurde(n) entfernt" -#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Controller.php:73 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Controller.php:187 #, php-format msgid "%d pending change(s) have been dropped" msgstr "%d vorgenommene Änderung(en) wurde(n) verworfen" -#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:165 +#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:349 #, php-format msgid "%s: Configuration" msgstr "%s: Konfiguration" -#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:129 +#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:313 #, php-format msgid "%s: Source Code" msgstr "%s: Quellcode" -#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:124 +#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:308 #, php-format msgid "%s: Source Code Differences" msgstr "%s: Quellcode Unterschiede" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:48 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/ProvidedHook/Director/DataType/BusinessProcess.php:17 +msgid "(no process config chosen)" +msgstr "(keine Prozess-Konfiguration gewählt)" + +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Form/QuickBaseForm.php:113 +msgid "- please choose -" +msgstr "- bitte wählen -" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:50 +msgid "" +"A slightly more detailed description for this process, about 100-150 " +"characters long " +msgstr "" +"Eine etwas detailliertere Beschreibung dieses Prozesses, etwa 100-150 " +"Zeichen lang" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:53 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:76 msgid "AND" msgstr "UND" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:42 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:62 msgid "Acknowledged" msgstr "Bestätigt" -#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/node/add.phtml:8 -msgid "Add new process node" -msgstr "Neuen Knoten hinzufügen" +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:92 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpUploadForm.php:86 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Renderer/TileRenderer.php:91 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Renderer/TreeRenderer.php:241 +msgid "Add" +msgstr "Hinzufügen" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:49 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Renderer/TileRenderer.php:89 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Renderer/TreeRenderer.php:246 +msgid "Add a new business process node" +msgstr "Neuen Business-Prozess hinzufügen" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:88 +msgid "Add children" +msgstr "Nachfolger hinzufügen" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:43 +#, php-format +msgid "Add simulation for %s" +msgstr "Simulation für %s hinzufügen" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:79 +msgid "Add to menu" +msgstr "Zum Menü hinzufügen" + +#: /usr/local/icingaweb-modules/businessprocess/configuration.php:50 +msgid "Allow to create whole new process configuration (files)" +msgstr "Erlaube das erstellen einer neuen Prozess-Konfiguration (Datei)" + +#: /usr/local/icingaweb-modules/businessprocess/configuration.php:54 +msgid "Allow to modify process definitions, to add and remove nodes" +msgstr "" +"Erlaubt es Prozessdefinitionen zu modifizieren, sowie Knoten hinzuzufügen " +"und zu entfernen" + +#: /usr/local/icingaweb-modules/businessprocess/configuration.php:46 +msgid "" +"Allow to see all available processes, regardless of configured restrictions" +msgstr "" +"Erlaubt es, alle verfügbaren Prozesse unabhängig von konfigurierten " +"Restriktionen zu sehen" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:96 +msgid "Another process" +msgstr "Ein anderer Prozess" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:72 msgid "Apply" msgstr "Anwenden" -#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/index/index.phtml:8 -#, php-format -msgid "" -"As no business process has been defined yet you might want to create a %s or " -"upload an %s." -msgstr "" -"Nachdem noch kein Business-Prozess definiert wurde, möchtest du vermutlich " -"einen %s erstellen oder einen %s hochladen." +#: /usr/local/icingaweb-modules/businessprocess/application/forms/DeleteNodeForm.php:32 +msgid "Are you sure?" +msgstr "Bist du sicher?" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:47 -#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:27 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:56 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpUploadForm.php:48 msgid "Backend" msgstr "Backend" -#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:206 +#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:384 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Controller.php:128 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Controller.php:139 msgid "Business Process" msgstr "Business-Prozess" -#: /usr/local/icingaweb-modules/businessprocess/configuration.php:4 -#: /usr/local/icingaweb-modules/businessprocess/application/controllers/IndexController.php:13 +#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:81 +#, php-format +msgid "Business Process \"%s\"" +msgstr "Businessprozesse \"%s\"" + +#: /usr/local/icingaweb-modules/businessprocess/application/controllers/IndexController.php:15 +msgid "Business Process Overview" +msgstr "Businessprozessübersicht" + +#: /usr/local/icingaweb-modules/businessprocess/configuration.php:5 msgid "Business Processes" msgstr "Business-Prozesse" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:31 -msgid "CRITICAL" -msgstr "KRITISCH" +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Component/ActionBar.php:75 +msgid "Config" +msgstr "Konfiguration" -#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/index/index.phtml:6 -msgid "Configure your first business process" -msgstr "Konfiguriere deinen ersten Business-Prozess" - -#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/BusinessProcess.php:297 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/BusinessProcess.php:316 #, php-format msgid "Could not retrieve process state: %s" msgstr "Konnte den Prozess-Status nicht ermitteln: %s" -#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:214 +#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:395 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Component/Dashboard.php:55 msgid "Create" msgstr "Erstelle" -#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:21 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Component/Dashboard.php:56 +msgid "Create a new Business Process configuration" +msgstr "Erstelle eine neue Businessprozesskonfiguration" + +#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:37 msgid "Create a new business process" msgstr "Neuen Business-Prozess erstellen" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/DeleteConfigForm.php:18 -msgid "Delete this process" -msgstr "Lösche diesen Prozess" +#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:30 +msgid "Current simulation" +msgstr "Aktuelle Simulation" -#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/show.phtml:40 -#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/show.phtml:51 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:79 +msgid "DEG" +msgstr "DEG" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:111 +msgid "Delete" +msgstr "Löschen" + +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Renderer/TileRenderer/NodeTile.php:159 +msgid "Delete this node" +msgstr "Lösche diesen Knoten" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:48 +msgid "Description" +msgstr "Beschreibung" + +#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:267 +#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:279 msgid "Dismiss" msgstr "Verwerfen" +#: /usr/local/icingaweb-modules/businessprocess/application/forms/DeleteNodeForm.php:35 +msgid "Do you really want to delete this node" +msgstr "Möchtest du diesen Knoten wirklich löschen" + #: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/source.phtml:6 msgid "Download process configuration" msgstr "Prozess-Konfiguration herunterladen" +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:125 +msgid "Existing Process" +msgstr "Existierender Prozess" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:97 +msgid "External process" +msgstr "Externer Prozess" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpUploadForm.php:75 +msgid "File" +msgstr "Datei" + +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Form/QuickForm.php:390 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Form/QuickForm.php:415 +msgid "Form has successfully been sent" +msgstr "Das Formular wurde erfolgreich versandt" + +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Component/Dashboard.php:47 +msgid "" +"From here you can reach all your defined Business Process configurations, " +"create new or modify existing ones" +msgstr "" +"Von hier kannst du alle definierten Businessprozesskonfigurationen " +"erreichen, neue erstellen oder bestehende bearbeiten" + +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Component/ActionBar.php:89 +msgid "Fullscreen" +msgstr "Vollbild" + #: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/source.phtml:10 msgid "Highlight changes" msgstr "Änderungen hervorheben" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:80 -msgid "" -"Hosts, services or other processes that should be part of this business " -"process" -msgstr "" -"Hosts, Services oder andere Prozesse welche Teil dieses Business-Prozesses " -"sein sollen" +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:94 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:135 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:120 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:171 +msgid "Host" +msgstr "Host" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:49 -#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:29 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:121 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:147 +msgid "Hosts" +msgstr "Hosts" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:127 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:153 +msgid "Hosts that should be part of this business process node" +msgstr "Hosts welche Teil dieses Business-Prozesses sein sollen" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:58 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpUploadForm.php:50 msgid "" "Icinga Web Monitoring Backend where current object states for this process " "should be retrieved from" @@ -137,51 +260,65 @@ msgstr "" "Das Icinga Web Monitoring Backend von welchem die Status-Informationen für " "diesen Prozess bezogen werden sollen" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:46 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:67 msgid "In downtime" msgstr "In Downtime" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:86 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:81 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:106 msgid "Info URL" msgstr "Info-URL" -#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/show.phtml:13 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Component/ActionBar.php:61 +msgid "Lock" +msgstr "Sperren" + +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Component/ActionBar.php:66 msgid "Lock this process" msgstr "Sperre diesen Prozess" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:50 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:56 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:80 msgid "MIN 1" msgstr "MIN 1" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:51 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:57 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:81 msgid "MIN 2" msgstr "MIN 2" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:52 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:58 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:82 msgid "MIN 3" msgstr "MIN 3" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:53 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:59 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:83 msgid "MIN 4" msgstr "MIN 4" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:54 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:60 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:84 msgid "MIN 5" msgstr "MIN 5" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:55 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:61 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:85 msgid "MIN 6" msgstr "MIN 6" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:56 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:62 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:86 msgid "MIN 7" msgstr "MIN 7" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:57 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:63 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:87 msgid "MIN 8" msgstr "MIN 8" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:58 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:64 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:88 msgid "MIN 9" msgstr "MIN 9" @@ -190,225 +327,359 @@ msgstr "MIN 9" msgid "Modify process node: %s" msgstr "Bearbeite Knoten: %s" -#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/BpNode.php:268 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:41 +#, php-format +msgid "Modify simulation for %s" +msgstr "Bearbeite die Simulation für %s" + +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Renderer/TileRenderer/NodeTile.php:136 +msgid "Modify this business process node" +msgstr "Bearbeite diesen Prozessknoten" + +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Renderer/TreeRenderer.php:199 msgid "Modify this node" msgstr "Bearbeite diesen Knoten" -#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Node.php:350 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Component/ActionBar.php:80 +msgid "Modify this process" +msgstr "Bearbeite diesen Prozess" + +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Renderer/TreeRenderer.php:222 msgid "More information" msgstr "Weitere Informationen" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:31 -#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:16 -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:29 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:55 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:78 +msgid "NOT" +msgstr "NICHT" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:32 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:34 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:57 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpUploadForm.php:32 msgid "Name" msgstr "Name" -#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/BusinessProcess.php:519 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/BpNode.php:276 +#, php-format +msgid "Nesting error detected: %s" +msgstr "Verschachtelungsfehler erkannt: %s" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:128 +msgid "New Process Node" +msgstr "Neuen Prozess-Knoten hinzufügen" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:47 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:164 +msgid "Next" +msgstr "Weiter" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:86 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/DeleteNodeForm.php:39 +msgid "No" +msgstr "Nein" + +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Component/Dashboard.php:77 +msgid "No Business Process has been defined for you" +msgstr "Es wurde noch kein Business-Prozess für dich definiert" + +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/BusinessProcess.php:693 #, php-format msgid "No business process nodes for \"%s\" have been defined yet" msgstr "Es wurden noch keine Business-Prozess Knoten für \"%s\" definiert" -#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/BusinessProcess.php:467 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Controller.php:205 +#, php-format +msgid "No such process config: \"%s\"" +msgstr "Keine entsprechende Prozesskonfiguration gefunden: \"%s\"" + +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/BusinessProcess.php:575 #, php-format msgid "Node \"%s\" has been defined twice" msgstr "Der Knoden \"%s\" wurde doppelt definiert" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:29 -msgid "OK" -msgstr "OK" +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:131 +msgid "Node type" +msgstr "Knotentyp" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:49 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Component/Dashboard.php:76 +msgid "Not available" +msgstr "NIcht verfügbar" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:54 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:77 msgid "OR" msgstr "ODER" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:45 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:50 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:73 msgid "Operator" msgstr "Operator" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:109 -msgid "Other Business Processes" -msgstr "Andere Business-Prozesse" +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:166 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:202 +msgid "Other processes that should be part of this business process node" +msgstr "Andere Prozesse welche Teil dieses Business-Prozesses sein sollen" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:33 -msgid "PENDING" -msgstr "PENDING" - -#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/BusinessProcess.php:538 -#, php-format -msgid "Parser waring on %s:%s: %s" -msgstr "Parse-Warnung an %s:%s: %s" - -#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:38 -msgid "Plugin output" -msgstr "Plugin-Ausgabe" - -#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:225 +#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:406 msgid "Process Configuration" msgstr "Prozess-Konfiguration" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:75 -msgid "Process components" -msgstr "Prozess-Komponenten" +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:160 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:196 +msgid "Process nodes" +msgstr "Prozess-Knoten" -#: /usr/local/icingaweb-modules/businessprocess/configuration.php:3 -msgid "Reporting" -msgstr "Reporting" +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:95 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:121 +msgid "Service" +msgstr "Service" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:146 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:182 +msgid "Services" +msgstr "Services" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:152 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:188 +msgid "Services that should be part of this business process node" +msgstr "Services welche Teil dieses Business-Prozesses sein sollen" + +#: /usr/local/icingaweb-modules/businessprocess/configuration.php:26 +msgid "Show all" +msgstr "Alle anzeigen" #: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/source.phtml:8 msgid "Show source code" msgstr "Quellcode anzeigen" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:86 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Renderer/TileRenderer/NodeTile.php:151 +msgid "Show the business impact of this node by simulating a specific state" +msgstr "" +"Zeige den Business-Impact dieses Knoten durch Simulation eines gewünschten " +"Zustandes" + +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Renderer/TileRenderer/NodeTile.php:142 +msgid "Show this subtree as a tree" +msgstr "Zeige diesen Teilbaum als eigenen Baum" + +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Renderer/TreeRenderer.php:212 +msgid "Simulate a specific state" +msgstr "Einen bestimmten Zustand simulieren" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:111 msgid "Simulation has been removed" msgstr "Simulation wurde entfernt" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:89 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:103 msgid "Simulation has been set" msgstr "Simulation wurde gesended" -#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:228 +#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:409 msgid "Source" msgstr "Quelle" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:26 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:52 msgid "State" msgstr "Status" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:58 -#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:36 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:67 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpUploadForm.php:59 msgid "State Type" msgstr "Statustyp" -#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/show.phtml:36 -#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:68 -#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:44 -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:92 +#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:261 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:108 msgid "Store" msgstr "Speichern" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:69 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Form/QuickForm.php:178 +msgid "Submit" +msgstr "Absenden" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:76 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:101 msgid "Subprocess only" msgstr "Nur Sub-Prozess" -#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/upload.phtml:8 -msgid "This has not been implemented yet" -msgstr "Das wurde noch nicht implementiert" +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Component/ActionBar.php:94 +msgid "Switch to fullscreen mode" +msgstr "Zum Vollbildmodus wechseln" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:34 -#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:18 -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:32 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:134 +msgid "The node type you want to add" +msgstr "Der gewünschte Knotentyp" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:35 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:37 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:60 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpUploadForm.php:35 msgid "This is the unique identifier of this process" msgstr "Das ist der eindeutige Identifier dieses Prozesses" -#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/show.phtml:33 +#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:257 #, php-format msgid "This process has %d pending change(s)." msgstr "Dieser Prozess hat %d ungespeicherte Änderung(en)" -#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/show.phtml:48 +#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:276 #, php-format msgid "This process shows %d simulated state(s)." msgstr "Dieser Prozess zeigt %d simulierte Zustände" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:39 -#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:22 -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:37 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Component/ActionBar.php:28 +msgid "Tiles" +msgstr "Kacheln" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:40 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:42 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:65 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpUploadForm.php:40 msgid "Title" msgstr "Titel" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:70 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:75 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:100 msgid "Toplevel Process" msgstr "Toplevel-Prozess" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:32 -msgid "UNKNOWN" -msgstr "UNBEKANNT" +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Component/ActionBar.php:37 +msgid "Tree" +msgstr "Baum" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:88 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:83 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:108 msgid "URL pointing to more information about this node" msgstr "URL zu mehr Informationen über diesen Knoten" -#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/process/show.phtml:10 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Component/ActionBar.php:49 +msgid "Unlock" +msgstr "Entsperren" + +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Component/ActionBar.php:54 msgid "Unlock this process" msgstr "Entsperre diesen Prozess" -#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:217 +#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:398 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpUploadForm.php:111 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Component/Dashboard.php:64 msgid "Upload" msgstr "Upload" -#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:35 +#: /usr/local/icingaweb-modules/businessprocess/application/controllers/ProcessController.php:56 msgid "Upload a business process config file" msgstr "Lade eine Business-Prozess Konfigurationsdatei hoch" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:64 -#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:40 +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Component/Dashboard.php:65 +msgid "Upload an existing Business Process configuration" +msgstr "Lade eine Business-Prozess Konfigurationsdatei hoch" + +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:74 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpUploadForm.php:65 msgid "Use HARD states" msgstr "HARD-States benutzen" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:65 -#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:41 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:73 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpUploadForm.php:66 msgid "Use SOFT states" msgstr "SOFT-States benutzen" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:31 -msgid "Use current default backend" -msgstr "Aktuelles Standard-Backend benutzen" +#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:124 +#, php-format +msgid "Use current state (%s)" +msgstr "Aktuellen Status benutzen (%s)" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:28 -msgid "Use current state" -msgstr "Aktuellen Status benutzen" - -#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:53 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:62 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpUploadForm.php:54 msgid "Use the configured default backend" msgstr "Benutze das konfigurierte Standard-Backend" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:39 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:44 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:67 msgid "" "Usually this title will be shown for this node. Equals name if not given" msgstr "" "Für gewöhnlich wird dieser Titel für diesen Knoten angezeigt. Entspricht dem " "Namen, wenn nicht angegeben" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:41 -#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:23 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:42 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpUploadForm.php:42 msgid "" "Usually this title will be shown for this process. Equals name if not given" msgstr "" "Für gewöhnlich wird dieser Titel für diesen Prozess angezeigt. Entspricht " "dem Namen, wenn nicht angegeben" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:63 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:69 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:93 msgid "Visualization" msgstr "Darstellung" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/SimulationForm.php:30 -msgid "WARNING" -msgstr "WARNUNG" +#: /usr/local/icingaweb-modules/businessprocess/library/Businessprocess/Web/Component/Dashboard.php:42 +msgid "Welcome to your Business Process Overview" +msgstr "Willkommen zur Übersicht deiner Business-Prozesse" -#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/warnings.phtml:2 -msgid "Warnings" -msgstr "Warnungen" - -#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:66 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/ProcessForm.php:72 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/AddNodeForm.php:96 msgid "Where to show this process" msgstr "Wo soll dieser Prozess angezeigt werden" -#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:61 -#: /usr/local/icingaweb-modules/businessprocess/application/forms/_CreateConfigForm.php:38 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:70 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpUploadForm.php:62 msgid "Whether this process should be based on Icinga hard or soft states" -msgstr "Ob dieser Prozess auf Icinga's hard- oder soft-states basieren soll" +msgstr "Ob dieser Prozess auf Icinga's Hard- oder Softstates basieren soll" -#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/index/index.phtml:10 -msgid "existing one" -msgstr "einen existierenden" +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:82 +msgid "Whether this process should be linked in the main Icinga Web 2 menu" +msgstr "Ob dieser Prozess ins Icinga Web 2 Hauptmenü verlinkt werden soll" -#: /usr/local/icingaweb-modules/businessprocess/application/views/scripts/index/index.phtml:9 -msgid "new business process" -msgstr "neuen Business-Prozess" +#: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:85 +#: /usr/local/icingaweb-modules/businessprocess/application/forms/DeleteNodeForm.php:40 +msgid "Yes" +msgstr "Ja" -#~ msgid "Show" -#~ msgstr "Zeige" +#~ msgid "" +#~ "As no business process has been defined yet you might want to create a %s " +#~ "or upload an %s." +#~ msgstr "" +#~ "Nachdem noch kein Business-Prozess definiert wurde, möchtest du " +#~ "vermutlich einen %s erstellen oder einen %s hochladen." + +#~ msgid "CRITICAL" +#~ msgstr "KRITISCH" + +#~ msgid "Configure your first business process" +#~ msgstr "Konfiguriere deinen ersten Business-Prozess" + +#~ msgid "OK" +#~ msgstr "OK" + +#~ msgid "PENDING" +#~ msgstr "PENDING" + +#~ msgid "Parser waring on %s:%s: %s" +#~ msgstr "Parse-Warnung an %s:%s: %s" + +#~ msgid "Plugin output" +#~ msgstr "Plugin-Ausgabe" + +#~ msgid "Reporting" +#~ msgstr "Reporting" + +#~ msgid "This has not been implemented yet" +#~ msgstr "Das wurde noch nicht implementiert" + +#~ msgid "UNKNOWN" +#~ msgstr "UNBEKANNT" + +#~ msgid "Use current default backend" +#~ msgstr "Aktuelles Standard-Backend benutzen" + +#~ msgid "WARNING" +#~ msgstr "WARNUNG" + +#~ msgid "Warnings" +#~ msgstr "Warnungen" #~ msgid "min" #~ msgstr "min" From dccf411a7042715c2a75d3c9c4940a016bed8761 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 16 Dec 2016 19:21:29 +0100 Subject: [PATCH 167/256] BreadCrumb: do not preserve delete action --- library/Businessprocess/Renderer/Breadcrumb.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/Businessprocess/Renderer/Breadcrumb.php b/library/Businessprocess/Renderer/Breadcrumb.php index ab4d36d..e196c83 100644 --- a/library/Businessprocess/Renderer/Breadcrumb.php +++ b/library/Businessprocess/Renderer/Breadcrumb.php @@ -25,8 +25,12 @@ class Breadcrumb extends BaseElement { $bp = $renderer->getBusinessProcess(); $breadcrumb = new static; + $bpUrl = $renderer->getBaseUrl(); + if ($bpUrl->getParam('action') === 'delete') { + $bpUrl->remove('action'); + } $breadcrumb->add(Element::create('li')->add( - Link::create($bp->getTitle(), $renderer->getBaseUrl()) + Link::create($bp->getTitle(), $bpUrl) )); $path = $renderer->getCurrentPath(); From d876f160224d26027243ed11267e2417e39afdb2 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 16 Dec 2016 19:22:39 +0100 Subject: [PATCH 168/256] view/scripts: work with Renderables only --- application/views/scripts/index/index.phtml | 9 ++------- application/views/scripts/process/create.phtml | 11 ++--------- application/views/scripts/process/upload.phtml | 10 ++-------- 3 files changed, 6 insertions(+), 24 deletions(-) diff --git a/application/views/scripts/index/index.phtml b/application/views/scripts/index/index.phtml index 82cb4f3..3e2cc59 100644 --- a/application/views/scripts/index/index.phtml +++ b/application/views/scripts/index/index.phtml @@ -1,7 +1,2 @@ -
-tabs ?> -
- -
-dashboard->render() ?> -
+controls->render() ?> +content->render() ?> diff --git a/application/views/scripts/process/create.phtml b/application/views/scripts/process/create.phtml index 6cd98f7..3e2cc59 100644 --- a/application/views/scripts/process/create.phtml +++ b/application/views/scripts/process/create.phtml @@ -1,9 +1,2 @@ -
-tabs ?> -

escape($this->title) ?>

-
- -
-form ?> -
- +controls->render() ?> +content->render() ?> diff --git a/application/views/scripts/process/upload.phtml b/application/views/scripts/process/upload.phtml index 7adf827..3e2cc59 100644 --- a/application/views/scripts/process/upload.phtml +++ b/application/views/scripts/process/upload.phtml @@ -1,8 +1,2 @@ -
-tabs ?> -

escape($this->title) ?>

-
- -
-form ?> -
+controls->render() ?> +content->render() ?> From 983bd27bf8051c00356dd611918d25f3d171c46b Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 16 Dec 2016 19:24:27 +0100 Subject: [PATCH 169/256] BpConfigForm: remove useless spaces, also from... ...translation --- application/forms/BpConfigForm.php | 2 +- application/locale/de_DE/LC_MESSAGES/businessprocess.po | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application/forms/BpConfigForm.php b/application/forms/BpConfigForm.php index 8fed728..bac2c19 100644 --- a/application/forms/BpConfigForm.php +++ b/application/forms/BpConfigForm.php @@ -47,7 +47,7 @@ class BpConfigForm extends QuickForm $this->addElement('textarea', 'Description', array( 'label' => $this->translate('Description'), 'description' => $this->translate( - 'A slightly more detailed description for this process, about 100-150 characters long ' + 'A slightly more detailed description for this process, about 100-150 characters long' ), 'rows' => 4, )); diff --git a/application/locale/de_DE/LC_MESSAGES/businessprocess.po b/application/locale/de_DE/LC_MESSAGES/businessprocess.po index 53528d3..3e6bddc 100644 --- a/application/locale/de_DE/LC_MESSAGES/businessprocess.po +++ b/application/locale/de_DE/LC_MESSAGES/businessprocess.po @@ -54,7 +54,7 @@ msgstr "- bitte wählen -" #: /usr/local/icingaweb-modules/businessprocess/application/forms/BpConfigForm.php:50 msgid "" "A slightly more detailed description for this process, about 100-150 " -"characters long " +"characters long" msgstr "" "Eine etwas detailliertere Beschreibung dieses Prozesses, etwa 100-150 " "Zeichen lang" From 2effa02d7fc85dc7274d7f5bd9410aef725d4d2f Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 16 Dec 2016 19:26:46 +0100 Subject: [PATCH 170/256] QuickBaseForm: we are a Renderable object --- library/Businessprocess/Web/Form/QuickBaseForm.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/Businessprocess/Web/Form/QuickBaseForm.php b/library/Businessprocess/Web/Form/QuickBaseForm.php index d8d80a0..a0246ee 100644 --- a/library/Businessprocess/Web/Form/QuickBaseForm.php +++ b/library/Businessprocess/Web/Form/QuickBaseForm.php @@ -4,9 +4,10 @@ namespace Icinga\Module\Businessprocess\Web\Form; use Icinga\Application\Icinga; use Icinga\Application\Modules\Module; +use Icinga\Module\Businessprocess\Html\Renderable; use Zend_Form; -abstract class QuickBaseForm extends Zend_Form +abstract class QuickBaseForm extends Zend_Form implements Renderable { /** * The Icinga module this form belongs to. Usually only set if the From d90998cc7289d7b1877943c2614c1075e7823311 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 16 Dec 2016 19:28:33 +0100 Subject: [PATCH 171/256] IndexController: use controls() and content() --- application/controllers/IndexController.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/application/controllers/IndexController.php b/application/controllers/IndexController.php index ecdd6e7..60ddc70 100644 --- a/application/controllers/IndexController.php +++ b/application/controllers/IndexController.php @@ -12,8 +12,9 @@ class IndexController extends Controller */ public function indexAction() { - $this->view->dashboard = Dashboard::create($this->Auth(), $this->storage()); - $this->view->tabs = $this->overviewTab(); + $this->setTitle($this->translate('Business Process Overview')); + $this->controls()->add($this->overviewTab()); + $this->content()->add(Dashboard::create($this->Auth(), $this->storage())); $this->setAutorefreshInterval(15); } } From 0bcdb7f502f36812d2d6a494cddbf9b25c21504b Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 16 Dec 2016 19:29:53 +0100 Subject: [PATCH 172/256] NodeController: remove obsolete actions... ...they are handled by various inline forms in the ProcessController --- application/controllers/NodeController.php | 40 ---------------------- 1 file changed, 40 deletions(-) diff --git a/application/controllers/NodeController.php b/application/controllers/NodeController.php index e84a2af..e44f20a 100644 --- a/application/controllers/NodeController.php +++ b/application/controllers/NodeController.php @@ -32,44 +32,4 @@ class NodeController extends Controller $this->view->node = $node; } - - public function simulateAction() - { - $bp = $this->loadBpConfig(); - $nodename = $this->getParam('node'); - $node = $bp->getNode($nodename); - $url = Url::fromPath( - 'businessprocess/process/show?unlocked', - array('config' => $bp->getName()) - ); - - $this->view->form = $this->loadForm('simulation') - ->setSimulation(new Simulation($bp, $this->session())) - ->setNode($node) - ->setSuccessUrl($url) - ->handleRequest(); - - $this->view->node = $node; - } - - public function addAction() - { - $this->defaultTab(); - $bp = $this->loadBpConfig(); - - $url = Url::fromPath( - 'businessprocess/process/show', - array('config' => $bp->getName()) - ); - - $this->view->form = $this->loadForm('process') - ->setProcess($bp) - ->setSession($this->session()) - ->setSuccessUrl($url) - ->handleRequest(); - } - - public function deleteAction() - { - } } From 0d696216e294c3b0d748e4f683779fcc1e1b8635 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 16 Dec 2016 19:31:22 +0100 Subject: [PATCH 173/256] ActionBar: do not forget permissions --- library/Businessprocess/Web/Component/ActionBar.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Businessprocess/Web/Component/ActionBar.php b/library/Businessprocess/Web/Component/ActionBar.php index c30d554..a31f257 100644 --- a/library/Businessprocess/Web/Component/ActionBar.php +++ b/library/Businessprocess/Web/Component/ActionBar.php @@ -69,7 +69,7 @@ class ActionBar extends BaseElement ); } - if ($meta->canModify()) { + if ($hasChanges || (! $renderer->isLocked()) && $meta->canModify()) { $this->add( Link::create( $this->translate('Config'), From 9e30be79c655f0739e48cf4f887c9a22ebfd738b Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 16 Dec 2016 19:32:36 +0100 Subject: [PATCH 174/256] NodeRemoveAction: rework to fit current reality --- .../Modification/NodeRemoveAction.php | 36 +++++++++++++++++-- .../Modification/ProcessChanges.php | 6 ++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/library/Businessprocess/Modification/NodeRemoveAction.php b/library/Businessprocess/Modification/NodeRemoveAction.php index 03bf984..62a48bf 100644 --- a/library/Businessprocess/Modification/NodeRemoveAction.php +++ b/library/Businessprocess/Modification/NodeRemoveAction.php @@ -13,12 +13,39 @@ use Icinga\Module\Businessprocess\BusinessProcess; */ class NodeRemoveAction extends NodeAction { + protected $preserveProperties = array('path'); + + protected $path; + + /** + * @param array $path + * @return $this + */ + public function setPath(array $path) + { + $this->path = $path; + return $this; + } + + /** + * @return mixed + */ + public function getPath() + { + return $this->path; + } + /** * @inheritdoc */ public function appliesTo(BusinessProcess $bp) { - return $bp->hasNode($this->getNodeName()); + $path = $this->getPath(); + if ($path === null) { + return $bp->hasNodeByPath($this->getNodeName(), $this->getPath()); + } else { + return $bp->hasNode($this->getNodeName()); + } } /** @@ -26,6 +53,11 @@ class NodeRemoveAction extends NodeAction */ public function applyTo(BusinessProcess $bp) { - $bp->removeNode($this->getNodeName()); + $path = $this->getPath(); + if ($path === null) { + $bp->removeNode($this->getNodeName()); + } else { + $bp->removeNodeByPath($this->getNodeName(), $this->getPath()); + } } } diff --git a/library/Businessprocess/Modification/ProcessChanges.php b/library/Businessprocess/Modification/ProcessChanges.php index 0fb52ae..a86eecc 100644 --- a/library/Businessprocess/Modification/ProcessChanges.php +++ b/library/Businessprocess/Modification/ProcessChanges.php @@ -99,9 +99,11 @@ class ProcessChanges * * @return $this */ - public function deleteNode(Node $node) + public function deleteNode(Node $node, array $path) { - return $this->push(new NodeRemoveAction($node)); + $action = new NodeRemoveAction($node); + $action->setProperties('path', $path); + return $this->push($action); } /** From 20f59021601616c7a12cd65a3065553003b8e4f3 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 16 Dec 2016 19:33:37 +0100 Subject: [PATCH 175/256] DeleteNodeForm: provide a new delete action Form --- application/forms/DeleteNodeForm.php | 106 +++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 application/forms/DeleteNodeForm.php diff --git a/application/forms/DeleteNodeForm.php b/application/forms/DeleteNodeForm.php new file mode 100644 index 0000000..335ac4a --- /dev/null +++ b/application/forms/DeleteNodeForm.php @@ -0,0 +1,106 @@ +addElement('select', 'confirm', array( + 'label' => $this->translate('Are you sure?'), + 'required' => true, + 'description' => $this->translate( + 'Do you really want to delete this node' + ), + 'multiOptions' => $this->optionalEnum( + array( + 'no' => $this->translate('No'), + 'yes' => $this->translate('Yes'), + )) + )); + } + + /** + * @param MonitoringBackend $backend + * @return $this + */ + public function setBackend(MonitoringBackend $backend) + { + $this->backend = $backend; + return $this; + } + + /** + * @param BusinessProcess $process + * @return $this + */ + public function setProcess(BusinessProcess $process) + { + $this->bp = $process; + $this->setBackend($process->getBackend()); + return $this; + } + + /** + * @param BpNode $node + * @return $this + */ + public function setNode(BpNode $node) + { + $this->node = $node; + return $this; + } + + /** + * @param array $path + * @return $this + */ + public function setPath(array $path) + { + $this->path = $path; + return $this; + } + + /** + * @param SessionNamespace $session + * @return $this + */ + public function setSession(SessionNamespace $session) + { + $this->session = $session; + return $this; + } + + public function onSuccess() + { + $changes = ProcessChanges::construct($this->bp, $this->session); + $changes->deleteNode($this->node, $this->path); + // Trigger session desctruction 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(); + } +} From d8a8e488a7a52bc5c0f7eb299d21574329f071c2 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 16 Dec 2016 19:34:45 +0100 Subject: [PATCH 176/256] ActionBar: move fullscreen action more to the right --- .../Web/Component/ActionBar.php | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/library/Businessprocess/Web/Component/ActionBar.php b/library/Businessprocess/Web/Component/ActionBar.php index a31f257..7536b9f 100644 --- a/library/Businessprocess/Web/Component/ActionBar.php +++ b/library/Businessprocess/Web/Component/ActionBar.php @@ -41,6 +41,20 @@ class ActionBar extends BaseElement ) ); } + + $this->add( + Link::create( + $this->translate('Fullscreen'), + $url->with('showFullscreen', true), + null, + array( + 'class' => 'icon-resize-full-alt', + 'title' => $this->translate('Switch to fullscreen mode'), + 'data-base-target' => '_main', + ) + ) + ); + $hasChanges = $config->hasSimulations() || $config->hasBeenChanged(); if ($renderer->isLocked()) { @@ -83,19 +97,6 @@ class ActionBar extends BaseElement ) ); } - - $this->add( - Link::create( - $this->translate('Fullscreen'), - $url->with('showFullscreen', true), - null, - array( - 'class' => 'icon-resize-full-alt', - 'title' => $this->translate('Switch to fullscreen mode'), - 'data-base-target' => '_main', - ) - ) - ); } protected function currentProcessParams($url) From 191edb794cf57fd7ee62004c1ec79cdde253c365 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 16 Dec 2016 19:36:24 +0100 Subject: [PATCH 177/256] Renderer, Breadcrumb: let them know from each... ...other. Not nice, but helps for now --- .../Businessprocess/Renderer/Breadcrumb.php | 2 ++ library/Businessprocess/Renderer/Renderer.php | 20 ++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/library/Businessprocess/Renderer/Breadcrumb.php b/library/Businessprocess/Renderer/Breadcrumb.php index e196c83..d146819 100644 --- a/library/Businessprocess/Renderer/Breadcrumb.php +++ b/library/Businessprocess/Renderer/Breadcrumb.php @@ -53,6 +53,8 @@ class Breadcrumb extends BaseElement protected static function renderNode(BpNode $node, $path, Renderer $renderer) { // TODO: something more generic than NodeTile? + $renderer = clone($renderer); + $renderer->lock()->setIsBreadcrumb(); $p = new NodeTile($renderer, (string) $node, $node, $path); $p->attributes()->add('class', $renderer->getNodeClasses($node)); $p->setTag('li'); diff --git a/library/Businessprocess/Renderer/Renderer.php b/library/Businessprocess/Renderer/Renderer.php index cb5a32a..9b69434 100644 --- a/library/Businessprocess/Renderer/Renderer.php +++ b/library/Businessprocess/Renderer/Renderer.php @@ -12,7 +12,6 @@ use Icinga\Module\Businessprocess\Html\Html; use Icinga\Module\Businessprocess\Html\HtmlString; use Icinga\Module\Businessprocess\Node; use Icinga\Module\Businessprocess\Web\Url; -use Icinga\Web\Request; abstract class Renderer extends Html { @@ -34,6 +33,9 @@ abstract class Renderer extends Html /** @var array */ protected $path = array(); + /** @var bool */ + protected $isBreadcrumb = false; + /** * Renderer constructor. * @@ -253,6 +255,22 @@ abstract class Renderer extends Html return $this; } + /** + * TODO: Get rid of this + * + * @return $this + */ + public function setIsBreadcrumb() + { + $this->isBreadcrumb = true; + return $this; + } + + public function isBreadcrumb() + { + return $this->isBreadcrumb; + } + public function timeSince($time, $timeOnly = false) { if (! $time) { From 549c07b457ed13c0a78f56eaec2c5a15dcfd9310 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 16 Dec 2016 19:37:39 +0100 Subject: [PATCH 178/256] Controller: remove outdated permission code --- library/Businessprocess/Web/Controller.php | 23 +--------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/library/Businessprocess/Web/Controller.php b/library/Businessprocess/Web/Controller.php index a2a4212..b7ba8b3 100644 --- a/library/Businessprocess/Web/Controller.php +++ b/library/Businessprocess/Web/Controller.php @@ -197,29 +197,8 @@ class Controller extends ModuleController protected function loadBpConfig() { + $name = $this->params->get('config'); $storage = $this->storage(); - if ($this->hasPermission('businessprocess/create')) { - $this->view->processList = array_merge( - $storage->listProcesses(), - array('.new' => $this->translate('Create new configuration')) - ); - } else { - $this->view->processList = $storage->listProcesses(); - } - - // No process found? Go to welcome page - if (empty($this->view->processList)) { - $this->redirectNow('businessprocess'); - } - - $name = $this->params->get( - 'config', - key($this->view->processList) - ); - - if ($name === '.new') { - $this->redirectNow('businessprocess/process/create'); - } if (! $storage->hasProcess($name)) { $this->httpNotFound( From 6f33705173ec3664aeb1cc69d5f645306ad3e3e6 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 16 Dec 2016 19:39:48 +0100 Subject: [PATCH 179/256] TileRenderer, Node: change rendering again... ...use a container div and provide multiple links now, this gives more flexibility --- .../Businessprocess/Renderer/TileRenderer.php | 34 ++- .../Renderer/TileRenderer/NodeTile.php | 236 +++++++++++++++--- 2 files changed, 232 insertions(+), 38 deletions(-) diff --git a/library/Businessprocess/Renderer/TileRenderer.php b/library/Businessprocess/Renderer/TileRenderer.php index 56029be..5d362f0 100644 --- a/library/Businessprocess/Renderer/TileRenderer.php +++ b/library/Businessprocess/Renderer/TileRenderer.php @@ -26,13 +26,9 @@ class TileRenderer extends Renderer ) ); - if ($this->wantsRootNodes()) { - $nodes = $bp->getChildren(); - } else { - $nodes = $this->parent->getChildren(); - } + $nodes = $this->getChildNodes(); - if (! $this->isLocked()) { + if (! $this->isLocked() && count($nodes) > 8) { $this->add($this->addNewNode()); } @@ -77,10 +73,26 @@ class TileRenderer extends Renderer protected function addNewNode() { - return Element::create( - 'div', + $div = Container::create( array('class' => 'addnew') - )->add( + ); + + $actions = Container::create( + array( + 'class' => 'actions', + 'data-base-target' => '_self' + ) + ); + + $link = Link::create( + $this->translate('Add'), + $this->getUrl()->with('action', 'add'), + null, + array( + 'title' => $this->translate('Add a new business process node') + ) + ); + $actions->add( Link::create( Icon::create('plus'), $this->getUrl()->with('action', 'add'), @@ -88,7 +100,9 @@ class TileRenderer extends Renderer array( 'title' => $this->translate('Add a new business process node') ) - )->addContent($this->translate('Add')) + ) ); + + return $div->add($actions)->add($link); } } diff --git a/library/Businessprocess/Renderer/TileRenderer/NodeTile.php b/library/Businessprocess/Renderer/TileRenderer/NodeTile.php index d323bf4..57582a9 100644 --- a/library/Businessprocess/Renderer/TileRenderer/NodeTile.php +++ b/library/Businessprocess/Renderer/TileRenderer/NodeTile.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Businessprocess\Renderer\TileRenderer; use Icinga\Module\Businessprocess\BpNode; use Icinga\Module\Businessprocess\HostNode; use Icinga\Module\Businessprocess\Html\BaseElement; +use Icinga\Module\Businessprocess\Html\Container; use Icinga\Module\Businessprocess\Html\HtmlString; use Icinga\Module\Businessprocess\Html\Icon; use Icinga\Module\Businessprocess\Html\Link; @@ -18,6 +19,19 @@ class NodeTile extends BaseElement { protected $tag = 'div'; + protected $renderer; + + protected $name; + + protected $node; + + protected $path; + + /** + * @var Container + */ + private $actions; + /** * NodeTile constructor. * @param Renderer $renderer @@ -27,52 +41,218 @@ class NodeTile extends BaseElement */ public function __construct(Renderer $renderer, $name, Node $node, $path = null) { + $this->renderer = $renderer; + $this->name = $name; + $this->node = $node; + $this->path = $path; + } + + protected function actions() + { + if ($this->actions === null) { + $this->addActions(); + } + return $this->actions; + } + + protected function addActions() + { + $this->actions = Container::create( + array( + 'class' => 'actions', + 'data-base-target' => '_self' + ) + ); + + return $this->add($this->actions); + } + + public function render() + { + $renderer = $this->renderer; + $node = $this->node; + $attributes = $this->attributes(); $attributes->add('class', $renderer->getNodeClasses($node)); $attributes->add('id', 'bp-' . (string) $node); - if ($node instanceof MonitoredNode) { - $attributes->add('data-base-target', '_next'); - $url = $node->getUrl(); - } else { - $bp = $renderer->getBusinessProcess(); - $params = array( - 'config' => $node instanceof ImportedNode ? - $node->getConfigName() : - $bp->getName() - ); + $this->addActions(); - if ($name !== null) { - $params['node'] = $name; - } + $link = $this->getMainNodeLink(); + $this->add($link); - $url = $renderer->getBaseUrl(); - $p = $url->getParams(); - $p->mergeValues($params); - if (! empty($path)) { - $p->addValues('path', $path); + if ($node instanceof BpNode) { + if ($renderer->isBreadcrumb()) { + $link->addContent($renderer->renderStateBadges($node->getStateSummary())); + } else { + $this->addContent($renderer->renderStateBadges($node->getStateSummary())); } } + if (! $renderer->isBreadcrumb()) { + $this->addDetailsActions(); + } + + if (! $renderer->isLocked()) { + $this->addActionLinks(); + } + + return parent::render(); + } + + protected function getMainNodeUrl(Node $node) + { + if ($node instanceof BpNode) { + return $this->makeBpUrl($node); + } else { + /** @var MonitoredNode $node */ + return $node->getUrl(); + } + } + + protected function makeBpUrl(BpNode $node) + { + $path = $this->path; + $name = $this->name; // TODO: ?? + $renderer = $this->renderer; + + $bp = $renderer->getBusinessProcess(); + $params = array( + 'config' => $node instanceof ImportedNode ? + $node->getConfigName() : + $bp->getName() + ); + + if ($name !== null) { + $params['node'] = $name; + } + + $url = $renderer->getBaseUrl(); + $p = $url->getParams(); + $p->mergeValues($params); + if (! empty($path)) { + $p->addValues('path', $path); + } + + return $url; + } + + 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 Link + */ + protected function getMainNodeLink() + { + $node = $this->node; + $url = $this->getMainNodeUrl($node); if ($node instanceof ServiceNode) { $link = Link::create( - Icon::create('service'), + $node->getAlias(), $url - )->addContent($node->getHostname()) - ->addContent(HtmlString::create('
')) - ->addContent($node->getServiceDescription()); + ); } elseif ($node instanceof HostNode) { - $link = Link::create( - Icon::create('host'), - $url - )->addContent($node->getHostname()); + $link = Link::create( + $node->getHostname(), + $url + ); } else { $link = Link::create($node->getAlias(), $url); } - $this->add($link); + return $link; + } + + protected function addDetailsActions() + { + $node = $this->node; + $url = $this->getMainNodeUrl($node); + if ($node instanceof BpNode) { - $link->addContent($renderer->renderStateBadges($node->getStateSummary())); + $this->actions()->add(Link::create( + Icon::create('dashboard'), + $url->with('mode', 'tile'), + null, + array('title' => $this->translate('Show tiles for this subtree')) + ))->add(Link::create( + Icon::create('sitemap'), + $url->with('mode', 'tree'), + null, + array( + 'title' => $this->translate('Show this subtree as a tree'), + ) + )); + } else { + $url = $this->makeMonitoredNodeUrl($node); + if ($node instanceof ServiceNode) { + $this->actions()->add(Link::create( + Icon::create('service'), + $url, + null, + array('data-base-target' => '_next') + )); + + } elseif ($node instanceof HostNode) { + $this->actions()->add(Link::create( + Icon::create('host'), + $url, + null, + array('data-base-target' => '_next') + )); + } } + + } + + protected function addActionLinks() + { + $node = $this->node; + $renderer = $this->renderer; + + if ($node instanceof BpNode) { + $this->actions()->add(Link::create( + Icon::create('edit'), + $renderer->getUrl()->with('action', 'edit'), + null, + array('title' => $this->translate('Modify this business process node')) + )); + } else { + $this->actions()->add(Link::create( + Icon::create('magic'), + $renderer->getUrl()->with('action', 'simulate')->with('simulationnode', $this->name), + null, + array('title' => $this->translate('Show the business impact of this node by simulating a specific state')) + )); + } + + $this->actions()->add(Link::create( + Icon::create('cancel'), + $renderer->getUrl()->with('action', 'delete'), + null, + array('title' => $this->translate('Delete this node')) + )); } } From 76a526d8dda435ae53cd4881d11b350faba3ff0a Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 16 Dec 2016 19:41:22 +0100 Subject: [PATCH 180/256] BpNode: let empty processes be unknown --- library/Businessprocess/BpNode.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Businessprocess/BpNode.php b/library/Businessprocess/BpNode.php index 31adbf0..dcd60f8 100644 --- a/library/Businessprocess/BpNode.php +++ b/library/Businessprocess/BpNode.php @@ -306,7 +306,7 @@ class BpNode extends Node if (!$this->hasChildren()) { // TODO: delegate this to operators, should mostly fail - $this->state = 0; + $this->state = 3; $this->setMissing(); return $this; } From f9f92333058c8e071234461a6434721a2ee18a56 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 16 Dec 2016 19:42:22 +0100 Subject: [PATCH 181/256] css: restructured to fit new tile renderer --- public/css/module.less | 221 ++++++++++++++++++++++------------------- 1 file changed, 118 insertions(+), 103 deletions(-) diff --git a/public/css/module.less b/public/css/module.less index 0ddc860..79b1ee7 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -368,63 +368,7 @@ div.knightrider table.bp { } /** END Dashboard **/ -/** BEGIN Tiles **/ -.tiles:after { - content:''; - display:block; - clear: both; -} - -.tiles > div { - width: 12em; - float: left; - margin-right: 0.2em; - margin-bottom: 0.2em; - color: @text-color; - border: 1px solid @text-color; - border-left: 0.5em solid @text-color; - a:hover { - background-color: @text-color; - color: white; - } -} - -.tiles > div.parent::before { - content: '&'; - position: absolute; - font-size: 1.2em; -} - -.tiles > div.parent { - width: 100%; - height: 2em; -} - -.tiles > div > a { - text-decoration: none; - font-size: 0.5em; - color: inherit; - vertical-align: middle; - text-align: center; - display: block; - padding: 1em; - font-weight: bold; - word-wrap: break-word; - width: 100%; - height: 12em; - box-sizing: border-box; - - i { - float: left; - font-size: 2.5em; - margin-top: -0.1em; - } -} - -.tiles > div > a:hover { - color: white; -} - +/** BEGIN Badges **/ .badges { display: block; padding: 0.5em; @@ -446,72 +390,143 @@ div.bp .badges { .badge-warning { background: @colorWarning; } .badge-pending { background: @colorPending; } .badge-missing { background: #ccc; } +/** END Badges **/ -.tiles { - > .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; } - > .warning { border-color: @colorWarning; } - > .warning.handled { border-color: @colorWarningHandled; } - > .ok { border-color: @colorOk; } - > .pending { border-color: @colorPending; } - > .missing { border-color: #ccc; } +/** BEGIN Tiles **/ +.tiles:after { + content:''; + display:block; + clear: both; } -.tiles { - > .critical a:hover { background: @colorCritical; } - > .critical.handled a:hover { background: @colorCriticalHandled; } - > .down a:hover { background: @colorCritical; } - > .down.handled a:hover { background: @colorCriticalHandled; } - > .unknown a:hover { background: @colorUnknown; } - > .unknown.handled a:hover { background: @colorUnknownHandled; } - > .unreachable a:hover { background: @colorUnknown; } - > .unreachable.handled a:hover { background: @colorUnknownHandled; } - > .warning a:hover { background: @colorWarning; } - > .warning.handled a:hover { background: @colorWarningHandled; } - > .ok a:hover { background: @colorOk; } - > .pending a:hover { background: @colorPending; } - > .missing a:hover { background: #ccc; } -} +.tiles > div { + color: white; + width: 12em; + display: inline-block; + float: left; + margin-right: 0.2em; + margin-bottom: 0.2em; + height: 6em; + cursor: pointer; -.tiles > .addnew { - border-color: @icinga-blue; - a { - color: @icinga-blue; - &:hover { - background: @icinga-blue; - } + .badges { + text-align: center; + font-size: 0.5em; + display: block; } - &.with-form { - width: auto; - height: auto; - min-width: 30em; - min-height: 10em; + > a { + display: block; + } + + &:hover { + box-shadow: 0px 0px 5px #666; + } + + .actions { + font-size: 0.75em; + margin-left: 0.5em; + padding-top: 0.2em; + height: 1.8em; + + i { + float: none; + display: block; + width: 100%; + font-size: 1em; + line-height: normal; + margin: 0; + padding: 0 0 0 0.25em; + } a { - display: none; + margin: 0; + padding: 0; + display: inline-block; + width: 1.5em; + height: 1.5em; + border-radius: 0.3em; + } + + a:hover { + background-color: white; + color: @text-color; } } } + +.tiles > div.parent::before { + content: '&'; + position: absolute; + font-size: 1.2em; +} + +.tiles > div.parent { + width: 100%; + height: 2em; +} + +.tiles > div > a { + text-decoration: none; + font-size: 0.5em; + color: inherit; + vertical-align: middle; + text-align: center; + padding: 1em; + font-weight: bold; + word-wrap: break-word; + width: 100%; + box-sizing: border-box; +} + +.tiles { + > .critical { background-color: @colorCritical; background-color: @colorCritical; color: white; } + > .critical.handled { background-color: @colorCriticalHandled; } + > .down { background-color: @colorCritical; } + > .down.handled { background-color: @colorCriticalHandled; } + > .unknown { background-color: @colorUnknown; } + > .unknown.handled { background-color: @colorUnknownHandled; } + > .unreachable { background-color: @colorUnknown; } + > .unreachable.handled { background-color: @colorUnknownHandled; } + > .warning { background-color: @colorWarning; } + > .warning.handled { background-color: @colorWarningHandled; } + > .ok { background-color: @colorOk; } + > .up { background-color: @colorOk; } + > .pending { background-color: @colorPending; } + > .missing { background-color: #ccc; } + > .addnew { background-color: @icinga-blue; } +} + +/* +.tiles > div:hover { + > .critical { background: white; color: @text-color; } + > .critical.handled { background: @colorCriticalHandled; } + > .down { background: @colorCritical; } + > .down.handled { background: @colorCriticalHandled; } + > .unknown { background: @colorUnknown; } + > .unknown.handled { background: @colorUnknownHandled; } + > .unreachable { background: @colorUnknown; } + > .unreachable.handled { background: @colorUnknownHandled; } + > .warning { background: @colorWarning; } + > .warning.handled { background: @colorWarningHandled; } + > .ok { background: @colorOk; } + > .pending { background: @colorPending; } + > .missing { background: #ccc; } +} +*/ .tiles .missing a { pointer-events: none; cursor: default; } -.tiles.few { font-size: 3em; } -.tiles.normal { font-size: 2.4em; } +.tiles.few { font-size: 2.5em; } +.tiles.normal { font-size: 2.1em; } .tiles.many { font-size: 1.8em; } #layout.twocols, #layout.layout-minimal, .compact { .tiles.few { font-size: 1.8em; } - .tiles.normal { font-size: 1.4em; } - .tiles.many { font-size: 1.2em; } + .tiles.normal { font-size: 1.8em; } + .tiles.many { font-size: 1.8em; } } #layout.fullscreen-layout .controls { From 0214c596e61543233900c6c14cfbc5fb3772ada4 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 16 Dec 2016 19:43:00 +0100 Subject: [PATCH 182/256] TileRenderer: forgot to move addNode --- library/Businessprocess/Renderer/TileRenderer.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/library/Businessprocess/Renderer/TileRenderer.php b/library/Businessprocess/Renderer/TileRenderer.php index 5d362f0..3a22b8e 100644 --- a/library/Businessprocess/Renderer/TileRenderer.php +++ b/library/Businessprocess/Renderer/TileRenderer.php @@ -43,6 +43,10 @@ class TileRenderer extends Renderer $this->add($this->add(new NodeTile($this, $name, $unbound))); } + if (! $this->isLocked()) { + $this->add($this->addNewNode()); + } + $nodesDiv->addContent($this->getContent()); $this->setContent($nodesDiv); From 93f9a1ac2616074ada9a9eb031502dfa013b25c4 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 16 Dec 2016 19:44:48 +0100 Subject: [PATCH 183/256] ProcessController: restructure create & upload --- application/controllers/ProcessController.php | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 2670ef7..bf6335b 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -6,6 +6,7 @@ use Icinga\Module\Businessprocess\BusinessProcess; use Icinga\Module\Businessprocess\ConfigDiff; use Icinga\Module\Businessprocess\Html\Element; use Icinga\Module\Businessprocess\Html\HtmlString; +use Icinga\Module\Businessprocess\Html\HtmlTag; use Icinga\Module\Businessprocess\Html\Icon; use Icinga\Module\Businessprocess\Node; use Icinga\Module\Businessprocess\Renderer\Breadcrumb; @@ -15,11 +16,11 @@ use Icinga\Module\Businessprocess\Renderer\TreeRenderer; use Icinga\Module\Businessprocess\Simulation; use Icinga\Module\Businessprocess\Html\Link; use Icinga\Module\Businessprocess\Web\Component\ActionBar; +use Icinga\Module\Businessprocess\Web\Component\Tabs; use Icinga\Module\Businessprocess\Web\Controller; use Icinga\Module\Businessprocess\Web\Url; use Icinga\Web\Notification; use Icinga\Web\Widget\Tabextension\DashboardAction; -use Icinga\Web\Widget\Tabs; class ProcessController extends Controller { @@ -33,13 +34,18 @@ class ProcessController extends Controller { $this->assertPermission('businessprocess/create'); - $this->setTitle($this->translate('Create a new business process')); - $this->tabsForCreate()->activate('create'); + $title = $this->translate('Create a new business process'); + $this->setTitle($title); + $this->controls() + ->add($this->tabsForCreate()->activate('create')) + ->add(HtmlTag::h1($title)); - $this->view->form = $this->loadForm('bpConfig') + $this->content()->add( + $this->loadForm('bpConfig') ->setStorage($this->storage()) ->setSuccessUrl('businessprocess/process/show') - ->handleRequest(); + ->handleRequest() + ); } /** @@ -47,12 +53,18 @@ class ProcessController extends Controller */ public function uploadAction() { - $this->setTitle($this->translate('Upload a business process config file')); - $this->tabsForCreate()->activate('upload'); - $this->view->form = $this->loadForm('BpUpload') - ->setStorage($this->storage()) - ->setSuccessUrl('businessprocess/process/show') - ->handleRequest(); + $title = $this->translate('Upload a business process config file'); + $this->setTitle($title); + $this->controls() + ->add($this->tabsForCreate()->activate('upload')) + ->add(HtmlTag::h1($title)); + + $this->content()->add( + $this->loadForm('BpUpload') + ->setStorage($this->storage()) + ->setSuccessUrl('businessprocess/process/show') + ->handleRequest() + ); } /** From c856a6fc26ad4b4972a1c2895d8404b433b87144 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 16 Dec 2016 19:47:36 +0100 Subject: [PATCH 184/256] BpNode: add comment to better reflect what's... ...going on. We are unknown when state calculation fails, not for empty processes - related git commit message was misleading --- library/Businessprocess/BpNode.php | 1 + 1 file changed, 1 insertion(+) diff --git a/library/Businessprocess/BpNode.php b/library/Businessprocess/BpNode.php index dcd60f8..7c8b9c7 100644 --- a/library/Businessprocess/BpNode.php +++ b/library/Businessprocess/BpNode.php @@ -277,6 +277,7 @@ class BpNode extends Node $e->getMessage() ); + // Failing nodes are unknown $this->state = 3; } } From c1802a8f599e1fd5ea13c97208e37d1119886c46 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Sat, 17 Dec 2016 19:15:50 +0100 Subject: [PATCH 185/256] ProcessController: title, cosmetics --- application/controllers/ProcessController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index bf6335b..2bdb4e0 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -78,7 +78,7 @@ class ProcessController extends Controller $bp->retrieveStatesFromBackend(); $this->handleSimulations($bp); - $this->setTitle('Business Process "%s"', $bp->getTitle()); + $this->setTitle($this->translate('Business Process "%s"'), $bp->getTitle()); $renderer = $this->prepareRenderer($bp, $node); @@ -191,7 +191,7 @@ class ProcessController extends Controller } if ($action === 'add') { - $form =$this->loadForm('AddNode') + $form = $this->loadForm('AddNode') ->setProcess($bp) ->setParentNode($node) ->setSession($this->session()) From ac7fa5654f66288b813898c61827e91b0e20905b Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Sat, 17 Dec 2016 19:18:15 +0100 Subject: [PATCH 186/256] TileRenderer: improve behavior, link targets --- .../Businessprocess/Renderer/TileRenderer.php | 3 +-- .../Renderer/TileRenderer/NodeTile.php | 22 +++++++++++++------ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/library/Businessprocess/Renderer/TileRenderer.php b/library/Businessprocess/Renderer/TileRenderer.php index 3a22b8e..c1c3725 100644 --- a/library/Businessprocess/Renderer/TileRenderer.php +++ b/library/Businessprocess/Renderer/TileRenderer.php @@ -3,7 +3,6 @@ namespace Icinga\Module\Businessprocess\Renderer; use Icinga\Module\Businessprocess\Html\Container; -use Icinga\Module\Businessprocess\Html\Element; use Icinga\Module\Businessprocess\Html\Icon; use Icinga\Module\Businessprocess\Html\Link; use Icinga\Module\Businessprocess\Renderer\TileRenderer\NodeTile; @@ -22,7 +21,7 @@ class TileRenderer extends Renderer 'tiles', $this->howMany() ), - 'data-base-target' => '_main', + 'data-base-target' => '_self', ) ); diff --git a/library/Businessprocess/Renderer/TileRenderer/NodeTile.php b/library/Businessprocess/Renderer/TileRenderer/NodeTile.php index 57582a9..ba466ae 100644 --- a/library/Businessprocess/Renderer/TileRenderer/NodeTile.php +++ b/library/Businessprocess/Renderer/TileRenderer/NodeTile.php @@ -172,12 +172,16 @@ class NodeTile extends BaseElement if ($node instanceof ServiceNode) { $link = Link::create( $node->getAlias(), - $url + $url, + null, + array('data-base-target' => '_next') ); } elseif ($node instanceof HostNode) { $link = Link::create( $node->getHostname(), - $url + $url, + null, + array('data-base-target' => '_next') ); } else { $link = Link::create($node->getAlias(), $url); @@ -196,21 +200,25 @@ class NodeTile extends BaseElement Icon::create('dashboard'), $url->with('mode', 'tile'), null, - array('title' => $this->translate('Show tiles for this subtree')) + array( + 'title' => $this->translate('Show tiles for this subtree'), + 'data-base-target' => '_next' + ) ))->add(Link::create( Icon::create('sitemap'), $url->with('mode', 'tree'), null, array( 'title' => $this->translate('Show this subtree as a tree'), + 'data-base-target' => '_next' ) )); } else { - $url = $this->makeMonitoredNodeUrl($node); + // $url = $this->makeMonitoredNodeUrl($node); if ($node instanceof ServiceNode) { $this->actions()->add(Link::create( Icon::create('service'), - $url, + $node->getUrl(), null, array('data-base-target' => '_next') )); @@ -218,7 +226,7 @@ class NodeTile extends BaseElement } elseif ($node instanceof HostNode) { $this->actions()->add(Link::create( Icon::create('host'), - $url, + $node->getUrl(), null, array('data-base-target' => '_next') )); @@ -242,7 +250,7 @@ class NodeTile extends BaseElement } else { $this->actions()->add(Link::create( Icon::create('magic'), - $renderer->getUrl()->with('action', 'simulate')->with('simulationnode', $this->name), + $renderer->getUrl()->with('action', 'simulation')->with('simulationnode', $this->name), null, array('title' => $this->translate('Show the business impact of this node by simulating a specific state')) )); From 46b452c014a690b4be44a7abc50483d5f32cd078 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Sat, 17 Dec 2016 19:18:58 +0100 Subject: [PATCH 187/256] ProcessController: fix lock negation --- application/controllers/ProcessController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 2bdb4e0..3a13e96 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -157,7 +157,7 @@ class ProcessController extends Controller { $tabs = $this->singleTab($bp->getTitle()); - if (! $renderer->isLocked()) { + if ($renderer->isLocked()) { $tabs->extend(new DashboardAction()); } From cb1d43dcb278e7db2d02239676b4556f37e1ed35 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Sat, 17 Dec 2016 19:20:54 +0100 Subject: [PATCH 188/256] AddNodeForm: main title, skip a step when new --- application/forms/AddNodeForm.php | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/application/forms/AddNodeForm.php b/application/forms/AddNodeForm.php index 2610b23..726a38a 100644 --- a/application/forms/AddNodeForm.php +++ b/application/forms/AddNodeForm.php @@ -29,6 +29,19 @@ class AddNodeForm extends QuickForm public function setup() { + $view = $this->getView(); + if ($this->hasParentNode()) { + $this->addHtml( + '

' . $view->escape( + sprintf($this->translate('Add a node to %s'), $this->parent->getAlias()) + ) . '

' + ); + } else { + $this->addHtml( + '

' . $this->translate('Add a new root node') . '

' + ); + } + $type = $this->selectNodeType(); switch ($type) { case 'host': @@ -51,8 +64,6 @@ class AddNodeForm extends QuickForm protected function addNewProcess() { - $this->addHtml('

Add a new node

'); - $this->addElement('text', 'name', array( 'label' => $this->translate('Name'), 'required' => true, @@ -119,6 +130,14 @@ class AddNodeForm extends QuickForm 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()) { From 8516e8749e266fe1f655d233b2aed4804eb0ff95 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Sat, 17 Dec 2016 19:22:04 +0100 Subject: [PATCH 189/256] DeleteNodeForm: add to process, add title --- application/controllers/ProcessController.php | 7 +++++++ application/forms/DeleteNodeForm.php | 7 ++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 3a13e96..7f26a6e 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -196,6 +196,13 @@ class ProcessController extends Controller ->setParentNode($node) ->setSession($this->session()) ->handleRequest(); + } elseif ($action === 'delete') { + $form =$this->loadForm('DeleteNode') + ->setProcess($bp) + ->setNode($node) + ->setPath($this->params->getValues('path')) + ->setSession($this->session()) + ->handleRequest(); } elseif ($action === 'simulation') { $form = $this->loadForm('simulation') ->setNode($bp->getNode($this->params->get('simulationnode'))) diff --git a/application/forms/DeleteNodeForm.php b/application/forms/DeleteNodeForm.php index 335ac4a..2cb9c08 100644 --- a/application/forms/DeleteNodeForm.php +++ b/application/forms/DeleteNodeForm.php @@ -28,11 +28,16 @@ class DeleteNodeForm extends QuickForm public function setup() { + $this->addHtml( + '

' . $this->getView()->escape( + sprintf($this->translate('Delete %s'), $this->node->getAlias()) + ) . '

' + ); $this->addElement('select', 'confirm', array( 'label' => $this->translate('Are you sure?'), 'required' => true, 'description' => $this->translate( - 'Do you really want to delete this node' + 'Do you really want to delete this node?' ), 'multiOptions' => $this->optionalEnum( array( From af8fe7c03df7b9ec67d79ff0ca5349db407eb7ef Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 22 Dec 2016 14:18:13 +0100 Subject: [PATCH 190/256] ImportedNode: provide Url --- library/Businessprocess/ImportedNode.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/library/Businessprocess/ImportedNode.php b/library/Businessprocess/ImportedNode.php index c099d43..318a83a 100644 --- a/library/Businessprocess/ImportedNode.php +++ b/library/Businessprocess/ImportedNode.php @@ -59,6 +59,16 @@ class ImportedNode extends Node return $this->importedNode()->getAlias(); } + public function getUrl() + { + $params = array( + 'config' => $this->getConfigName(), + 'node' => $this->importedNode->getName() + ); + + return Url::fromPath('businessprocess/process/show', $params); + } + /** * @inheritdoc */ From d5721b2a99cfe52582dc17398cfa9aee7a9ede97 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Thu, 22 Dec 2016 14:19:00 +0100 Subject: [PATCH 191/256] ImportedNode: use method, not property --- library/Businessprocess/ImportedNode.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Businessprocess/ImportedNode.php b/library/Businessprocess/ImportedNode.php index 318a83a..6134394 100644 --- a/library/Businessprocess/ImportedNode.php +++ b/library/Businessprocess/ImportedNode.php @@ -63,7 +63,7 @@ class ImportedNode extends Node { $params = array( 'config' => $this->getConfigName(), - 'node' => $this->importedNode->getName() + 'node' => $this->importedNode()->getName() ); return Url::fromPath('businessprocess/process/show', $params); From 1fc6634b75ec73dbfbdfb1ef812d39de13936c7b Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 23 Dec 2016 11:14:00 +0100 Subject: [PATCH 192/256] Renderer: do not preserve action-related params --- library/Businessprocess/Renderer/Renderer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Businessprocess/Renderer/Renderer.php b/library/Businessprocess/Renderer/Renderer.php index 9b69434..894b2af 100644 --- a/library/Businessprocess/Renderer/Renderer.php +++ b/library/Businessprocess/Renderer/Renderer.php @@ -196,7 +196,7 @@ abstract class Renderer extends Html */ public function setUrl(Url $url) { - $this->url = clone($url); + $this->url = $url->without(array('simulationnode', 'deletenode')); $this->setBaseUrl($url); return $this; } From 40165353d83cddc7732f5bf6e8e5640bf97fd869 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 23 Dec 2016 11:16:37 +0100 Subject: [PATCH 193/256] ProcessChanges: fit changed NodeRemove api --- library/Businessprocess/Modification/ProcessChanges.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/library/Businessprocess/Modification/ProcessChanges.php b/library/Businessprocess/Modification/ProcessChanges.php index a86eecc..78061ae 100644 --- a/library/Businessprocess/Modification/ProcessChanges.php +++ b/library/Businessprocess/Modification/ProcessChanges.php @@ -79,7 +79,7 @@ class ProcessChanges /** * @param Node|string $nodeName - * @param object $properties + * @param array $properties * @param Node $parent * * @return $this @@ -102,7 +102,7 @@ class ProcessChanges public function deleteNode(Node $node, array $path) { $action = new NodeRemoveAction($node); - $action->setProperties('path', $path); + $action->setPath($path); return $this->push($action); } @@ -120,7 +120,6 @@ class ProcessChanges return $this; } - /** * Get all stacked actions * From ad5cf478628dd64e8939ff76f1bd3c150170b9b7 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Fri, 23 Dec 2016 12:46:56 +0100 Subject: [PATCH 194/256] tests: adjust namespaces --- test/php/library/Businessprocess/BpNodeTest.php | 2 +- test/php/library/Businessprocess/HostNodeTest.php | 2 +- test/php/library/Businessprocess/ServiceNodeTest.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/php/library/Businessprocess/BpNodeTest.php b/test/php/library/Businessprocess/BpNodeTest.php index b9ef61c..c1f224b 100644 --- a/test/php/library/Businessprocess/BpNodeTest.php +++ b/test/php/library/Businessprocess/BpNodeTest.php @@ -1,6 +1,6 @@ Date: Fri, 23 Dec 2016 12:47:43 +0100 Subject: [PATCH 195/256] MetaData: fix typo --- library/Businessprocess/Metadata.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Businessprocess/Metadata.php b/library/Businessprocess/Metadata.php index eba36e7..cfa9525 100644 --- a/library/Businessprocess/Metadata.php +++ b/library/Businessprocess/Metadata.php @@ -45,7 +45,7 @@ class Metadata if ($title === $this->name) { return $title; } else { - return sprint('%s (%s)', $title, $this->name); + return sprintf('%s (%s)', $title, $this->name); } } From 5443bd5b4eb882de5d736c10309cbfa5e59d0eb5 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 26 Dec 2016 11:43:32 +0100 Subject: [PATCH 196/256] LegacyStorage: new list helper method --- .../Businessprocess/Storage/LegacyStorage.php | 41 +++++++++++++++++-- .../Web/Component/Dashboard.php | 2 +- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/library/Businessprocess/Storage/LegacyStorage.php b/library/Businessprocess/Storage/LegacyStorage.php index e7c0a3a..8d009cc 100644 --- a/library/Businessprocess/Storage/LegacyStorage.php +++ b/library/Businessprocess/Storage/LegacyStorage.php @@ -60,6 +60,11 @@ class LegacyStorage extends Storage } /** + * All processes readable by the current user + * + * The returned array has the form => , sorted + * by title + * * @return array */ public function listProcesses() @@ -72,13 +77,43 @@ class LegacyStorage extends Storage continue; } - $files[$name] = $name; + $files[$name] = $meta->getExtendedTitle(); } - natsort($files); + natcasesort($files); return $files; } + /** + * All process names readable by the current user + * + * The returned array has the form => and is + * sorted + * + * @return array + */ + public function listProcessNames() + { + $files = array(); + + foreach ($this->listAllProcessNames() as $name) { + $meta = $this->loadMetadata($name); + if (! $meta->canRead()) { + continue; + } + + $files[$name] = $name; + } + + natcasesort($files); + return $files; + } + + /** + * All available process names, regardless of eventual restrictions + * + * @return array + */ public function listAllProcessNames() { $files = array(); @@ -94,7 +129,7 @@ class LegacyStorage extends Storage } } - natsort($files); + natcasesort($files); return $files; } diff --git a/library/Businessprocess/Web/Component/Dashboard.php b/library/Businessprocess/Web/Component/Dashboard.php index 8f8311a..698daec 100644 --- a/library/Businessprocess/Web/Component/Dashboard.php +++ b/library/Businessprocess/Web/Component/Dashboard.php @@ -37,7 +37,7 @@ class Dashboard extends BaseElement $this->auth = $auth; $this->storage = $storage; // TODO: Auth? - $processes = $storage->listProcesses(); + $processes = $storage->listProcessNames(); $this->add( HtmlTag::h1($this->translate('Welcome to your Business Process Overview')) ); From e385a8cab23b2e7b35fd8bec237ee62f063ffb5a Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:03:30 +0100 Subject: [PATCH 197/256] php-diff: replace vendor lib --- application/controllers/ProcessController.php | 2 +- library/Businessprocess/ConfigDiff.php | 40 - .../Businessprocess/Storage/ConfigDiff.php | 91 +++ library/vendor/PHP-FineDiff/finediff.php | 688 ---------------- library/vendor/php-diff/README.md | 65 ++ library/vendor/php-diff/SOURCE | 9 + library/vendor/php-diff/lib/Diff.php | 179 +++++ .../php-diff/lib/Diff/Renderer/Abstract.php | 82 ++ .../php-diff/lib/Diff/Renderer/Html/Array.php | 231 ++++++ .../lib/Diff/Renderer/Html/Inline.php | 143 ++++ .../lib/Diff/Renderer/Html/SideBySide.php | 163 ++++ .../lib/Diff/Renderer/Text/Context.php | 128 +++ .../lib/Diff/Renderer/Text/Unified.php | 87 ++ .../php-diff/lib/Diff/SequenceMatcher.php | 742 ++++++++++++++++++ public/css/module.less | 102 ++- 15 files changed, 2022 insertions(+), 730 deletions(-) delete mode 100644 library/Businessprocess/ConfigDiff.php create mode 100644 library/Businessprocess/Storage/ConfigDiff.php delete mode 100644 library/vendor/PHP-FineDiff/finediff.php create mode 100644 library/vendor/php-diff/README.md create mode 100644 library/vendor/php-diff/SOURCE create mode 100644 library/vendor/php-diff/lib/Diff.php create mode 100644 library/vendor/php-diff/lib/Diff/Renderer/Abstract.php create mode 100644 library/vendor/php-diff/lib/Diff/Renderer/Html/Array.php create mode 100644 library/vendor/php-diff/lib/Diff/Renderer/Html/Inline.php create mode 100644 library/vendor/php-diff/lib/Diff/Renderer/Html/SideBySide.php create mode 100644 library/vendor/php-diff/lib/Diff/Renderer/Text/Context.php create mode 100644 library/vendor/php-diff/lib/Diff/Renderer/Text/Unified.php create mode 100644 library/vendor/php-diff/lib/Diff/SequenceMatcher.php diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 7f26a6e..00f3c80 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -3,7 +3,7 @@ namespace Icinga\Module\Businessprocess\Controllers; use Icinga\Module\Businessprocess\BusinessProcess; -use Icinga\Module\Businessprocess\ConfigDiff; +use Icinga\Module\Businessprocess\Storage\ConfigDiff; use Icinga\Module\Businessprocess\Html\Element; use Icinga\Module\Businessprocess\Html\HtmlString; use Icinga\Module\Businessprocess\Html\HtmlTag; diff --git a/library/Businessprocess/ConfigDiff.php b/library/Businessprocess/ConfigDiff.php deleted file mode 100644 index 355e9b1..0000000 --- a/library/Businessprocess/ConfigDiff.php +++ /dev/null @@ -1,40 +0,0 @@ -a = $a; - $this->b = $b; - require_once dirname(__DIR__) . '/vendor/PHP-FineDiff/finediff.php'; - $granularity = FineDiff::$paragraphGranularity; // character, word, sentence, paragraph - $this->diff = new FineDiff($a, $b, $granularity); - } - - public function renderHtml() - { - return $this->diff->renderDiffToHTML(); - } - - public function __toString() - { - return $this->renderHtml(); - } - - public static function create($a, $b) - { - $diff = new static($a, $b); - return $diff; - } -} diff --git a/library/Businessprocess/Storage/ConfigDiff.php b/library/Businessprocess/Storage/ConfigDiff.php new file mode 100644 index 0000000..35b8280 --- /dev/null +++ b/library/Businessprocess/Storage/ConfigDiff.php @@ -0,0 +1,91 @@ +requireVendorLib('Diff.php'); + + if (empty($a)) { + $this->a = array(); + } else { + $this->a = explode("\n", (string) $a); + } + + if (empty($b)) { + $this->b = array(); + } else { + $this->b = explode("\n", (string) $b); + } + + $options = array( + 'context' => 5, + // 'ignoreWhitespace' => true, + // 'ignoreCase' => true, + ); + $this->diff = new Diff($this->a, $this->b, $options); + } + + /** + * @return string + */ + public function render() + { + return $this->renderHtmlSideBySide(); + } + + public function renderHtmlSideBySide() + { + $this->requireVendorLib('Diff/Renderer/Html/SideBySide.php'); + $renderer = new Diff_Renderer_Html_SideBySide; + return $this->diff->render($renderer); + } + + public function renderHtmlInline() + { + $this->requireVendorLib('Diff/Renderer/Html/Inline.php'); + $renderer = new Diff_Renderer_Html_Inline; + return $this->diff->render($renderer); + } + + public function renderTextContext() + { + $this->requireVendorLib('Diff/Renderer/Text/Context.php'); + $renderer = new Diff_Renderer_Text_Context; + return $this->diff->render($renderer); + } + + public function renderTextUnified() + { + $this->requireVendorLib('Diff/Renderer/Text/Unified.php'); + $renderer = new Diff_Renderer_Text_Unified; + return $this->diff->render($renderer); + } + + protected function requireVendorLib($file) + { + require_once dirname(dirname(__DIR__)) . '/vendor/php-diff/lib/' . $file; + } + + public static function create($a, $b) + { + $diff = new static($a, $b); + return $diff; + } +} \ No newline at end of file diff --git a/library/vendor/PHP-FineDiff/finediff.php b/library/vendor/PHP-FineDiff/finediff.php deleted file mode 100644 index 920365a..0000000 --- a/library/vendor/PHP-FineDiff/finediff.php +++ /dev/null @@ -1,688 +0,0 @@ -copy->insert -* command (swap) for when the inserted segment is exactly the same -* as the deleted one, and with only a copy operation in between. -* TODO: How often this case occurs? Is it worth it? Can only -* be done as a postprocessing method (->optimize()?) -*/ -abstract class FineDiffOp { - abstract public function getFromLen(); - abstract public function getToLen(); - abstract public function getOpcode(); - } - -class FineDiffDeleteOp extends FineDiffOp { - public function __construct($len) { - $this->fromLen = $len; - } - public function getFromLen() { - return $this->fromLen; - } - public function getToLen() { - return 0; - } - public function getOpcode() { - if ( $this->fromLen === 1 ) { - return 'd'; - } - return "d{$this->fromLen}"; - } - } - -class FineDiffInsertOp extends FineDiffOp { - public function __construct($text) { - $this->text = $text; - } - public function getFromLen() { - return 0; - } - public function getToLen() { - return strlen($this->text); - } - public function getText() { - return $this->text; - } - public function getOpcode() { - $to_len = strlen($this->text); - if ( $to_len === 1 ) { - return "i:{$this->text}"; - } - return "i{$to_len}:{$this->text}"; - } - } - -class FineDiffReplaceOp extends FineDiffOp { - public function __construct($fromLen, $text) { - $this->fromLen = $fromLen; - $this->text = $text; - } - public function getFromLen() { - return $this->fromLen; - } - public function getToLen() { - return strlen($this->text); - } - public function getText() { - return $this->text; - } - public function getOpcode() { - if ( $this->fromLen === 1 ) { - $del_opcode = 'd'; - } - else { - $del_opcode = "d{$this->fromLen}"; - } - $to_len = strlen($this->text); - if ( $to_len === 1 ) { - return "{$del_opcode}i:{$this->text}"; - } - return "{$del_opcode}i{$to_len}:{$this->text}"; - } - } - -class FineDiffCopyOp extends FineDiffOp { - public function __construct($len) { - $this->len = $len; - } - public function getFromLen() { - return $this->len; - } - public function getToLen() { - return $this->len; - } - public function getOpcode() { - if ( $this->len === 1 ) { - return 'c'; - } - return "c{$this->len}"; - } - public function increase($size) { - return $this->len += $size; - } - } - -/** -* FineDiff ops -* -* Collection of ops -*/ -class FineDiffOps { - public function appendOpcode($opcode, $from, $from_offset, $from_len) { - if ( $opcode === 'c' ) { - $edits[] = new FineDiffCopyOp($from_len); - } - else if ( $opcode === 'd' ) { - $edits[] = new FineDiffDeleteOp($from_len); - } - else /* if ( $opcode === 'i' ) */ { - $edits[] = new FineDiffInsertOp(substr($from, $from_offset, $from_len)); - } - } - public $edits = array(); - } - -/** -* FineDiff class -* -* TODO: Document -* -*/ -class FineDiff { - - /**------------------------------------------------------------------------ - * - * Public section - * - */ - - /** - * Constructor - * ... - * The $granularityStack allows FineDiff to be configurable so that - * a particular stack tailored to the specific content of a document can - * be passed. - */ - public function __construct($from_text = '', $to_text = '', $granularityStack = null) { - // setup stack for generic text documents by default - $this->granularityStack = $granularityStack ? $granularityStack : FineDiff::$characterGranularity; - $this->edits = array(); - $this->from_text = $from_text; - $this->doDiff($from_text, $to_text); - } - - public function getOps() { - return $this->edits; - } - - public function getOpcodes() { - $opcodes = array(); - foreach ( $this->edits as $edit ) { - $opcodes[] = $edit->getOpcode(); - } - return implode('', $opcodes); - } - - public function renderDiffToHTML() { - $in_offset = 0; - ob_start(); - foreach ( $this->edits as $edit ) { - $n = $edit->getFromLen(); - if ( $edit instanceof FineDiffCopyOp ) { - FineDiff::renderDiffToHTMLFromOpcode('c', $this->from_text, $in_offset, $n); - } - else if ( $edit instanceof FineDiffDeleteOp ) { - FineDiff::renderDiffToHTMLFromOpcode('d', $this->from_text, $in_offset, $n); - } - else if ( $edit instanceof FineDiffInsertOp ) { - FineDiff::renderDiffToHTMLFromOpcode('i', $edit->getText(), 0, $edit->getToLen()); - } - else /* if ( $edit instanceof FineDiffReplaceOp ) */ { - FineDiff::renderDiffToHTMLFromOpcode('d', $this->from_text, $in_offset, $n); - FineDiff::renderDiffToHTMLFromOpcode('i', $edit->getText(), 0, $edit->getToLen()); - } - $in_offset += $n; - } - return ob_get_clean(); - } - - /**------------------------------------------------------------------------ - * Return an opcodes string describing the diff between a "From" and a - * "To" string - */ - public static function getDiffOpcodes($from, $to, $granularities = null) { - $diff = new FineDiff($from, $to, $granularities); - return $diff->getOpcodes(); - } - - /**------------------------------------------------------------------------ - * Return an iterable collection of diff ops from an opcodes string - */ - public static function getDiffOpsFromOpcodes($opcodes) { - $diffops = new FineDiffOps(); - FineDiff::renderFromOpcodes(null, $opcodes, array($diffops,'appendOpcode')); - return $diffops->edits; - } - - /**------------------------------------------------------------------------ - * Re-create the "To" string from the "From" string and an "Opcodes" string - */ - public static function renderToTextFromOpcodes($from, $opcodes) { - ob_start(); - FineDiff::renderFromOpcodes($from, $opcodes, array('FineDiff','renderToTextFromOpcode')); - return ob_get_clean(); - } - - /**------------------------------------------------------------------------ - * Render the diff to an HTML string - */ - public static function renderDiffToHTMLFromOpcodes($from, $opcodes) { - ob_start(); - FineDiff::renderFromOpcodes($from, $opcodes, array('FineDiff','renderDiffToHTMLFromOpcode')); - return ob_get_clean(); - } - - /**------------------------------------------------------------------------ - * Generic opcodes parser, user must supply callback for handling - * single opcode - */ - public static function renderFromOpcodes($from, $opcodes, $callback) { - if ( !is_callable($callback) ) { - return; - } - $opcodes_len = strlen($opcodes); - $from_offset = $opcodes_offset = 0; - while ( $opcodes_offset < $opcodes_len ) { - $opcode = substr($opcodes, $opcodes_offset, 1); - $opcodes_offset++; - $n = intval(substr($opcodes, $opcodes_offset)); - if ( $n ) { - $opcodes_offset += strlen(strval($n)); - } - else { - $n = 1; - } - if ( $opcode === 'c' ) { // copy n characters from source - call_user_func($callback, 'c', $from, $from_offset, $n, ''); - $from_offset += $n; - } - else if ( $opcode === 'd' ) { // delete n characters from source - call_user_func($callback, 'd', $from, $from_offset, $n, ''); - $from_offset += $n; - } - else /* if ( $opcode === 'i' ) */ { // insert n characters from opcodes - call_user_func($callback, 'i', $opcodes, $opcodes_offset + 1, $n); - $opcodes_offset += 1 + $n; - } - } - } - - /** - * Stock granularity stacks and delimiters - */ - - const paragraphDelimiters = "\n\r"; - public static $paragraphGranularity = array( - FineDiff::paragraphDelimiters - ); - const sentenceDelimiters = ".\n\r"; - public static $sentenceGranularity = array( - FineDiff::paragraphDelimiters, - FineDiff::sentenceDelimiters - ); - const wordDelimiters = " \t.\n\r"; - public static $wordGranularity = array( - FineDiff::paragraphDelimiters, - FineDiff::sentenceDelimiters, - FineDiff::wordDelimiters - ); - const characterDelimiters = ""; - public static $characterGranularity = array( - FineDiff::paragraphDelimiters, - FineDiff::sentenceDelimiters, - FineDiff::wordDelimiters, - FineDiff::characterDelimiters - ); - - public static $textStack = array( - ".", - " \t.\n\r", - "" - ); - - /**------------------------------------------------------------------------ - * - * Private section - * - */ - - /** - * Entry point to compute the diff. - */ - private function doDiff($from_text, $to_text) { - $this->last_edit = false; - $this->stackpointer = 0; - $this->from_text = $from_text; - $this->from_offset = 0; - // can't diff without at least one granularity specifier - if ( empty($this->granularityStack) ) { - return; - } - $this->_processGranularity($from_text, $to_text); - } - - /** - * This is the recursive function which is responsible for - * handling/increasing granularity. - * - * Incrementally increasing the granularity is key to compute the - * overall diff in a very efficient way. - */ - private function _processGranularity($from_segment, $to_segment) { - $delimiters = $this->granularityStack[$this->stackpointer++]; - $has_next_stage = $this->stackpointer < count($this->granularityStack); - foreach ( FineDiff::doFragmentDiff($from_segment, $to_segment, $delimiters) as $fragment_edit ) { - // increase granularity - if ( $fragment_edit instanceof FineDiffReplaceOp && $has_next_stage ) { - $this->_processGranularity( - substr($this->from_text, $this->from_offset, $fragment_edit->getFromLen()), - $fragment_edit->getText() - ); - } - // fuse copy ops whenever possible - else if ( $fragment_edit instanceof FineDiffCopyOp && $this->last_edit instanceof FineDiffCopyOp ) { - $this->edits[count($this->edits)-1]->increase($fragment_edit->getFromLen()); - $this->from_offset += $fragment_edit->getFromLen(); - } - else { - /* $fragment_edit instanceof FineDiffCopyOp */ - /* $fragment_edit instanceof FineDiffDeleteOp */ - /* $fragment_edit instanceof FineDiffInsertOp */ - $this->edits[] = $this->last_edit = $fragment_edit; - $this->from_offset += $fragment_edit->getFromLen(); - } - } - $this->stackpointer--; - } - - /** - * This is the core algorithm which actually perform the diff itself, - * fragmenting the strings as per specified delimiters. - * - * This function is naturally recursive, however for performance purpose - * a local job queue is used instead of outright recursivity. - */ - private static function doFragmentDiff($from_text, $to_text, $delimiters) { - // Empty delimiter means character-level diffing. - // In such case, use code path optimized for character-level - // diffing. - if ( empty($delimiters) ) { - return FineDiff::doCharDiff($from_text, $to_text); - } - - $result = array(); - - // fragment-level diffing - $from_text_len = strlen($from_text); - $to_text_len = strlen($to_text); - $from_fragments = FineDiff::extractFragments($from_text, $delimiters); - $to_fragments = FineDiff::extractFragments($to_text, $delimiters); - - $jobs = array(array(0, $from_text_len, 0, $to_text_len)); - - $cached_array_keys = array(); - - while ( $job = array_pop($jobs) ) { - - // get the segments which must be diff'ed - list($from_segment_start, $from_segment_end, $to_segment_start, $to_segment_end) = $job; - - // catch easy cases first - $from_segment_length = $from_segment_end - $from_segment_start; - $to_segment_length = $to_segment_end - $to_segment_start; - if ( !$from_segment_length || !$to_segment_length ) { - if ( $from_segment_length ) { - $result[$from_segment_start * 4] = new FineDiffDeleteOp($from_segment_length); - } - else if ( $to_segment_length ) { - $result[$from_segment_start * 4 + 1] = new FineDiffInsertOp(substr($to_text, $to_segment_start, $to_segment_length)); - } - continue; - } - - // find longest copy operation for the current segments - $best_copy_length = 0; - - $from_base_fragment_index = $from_segment_start; - - $cached_array_keys_for_current_segment = array(); - - while ( $from_base_fragment_index < $from_segment_end ) { - $from_base_fragment = $from_fragments[$from_base_fragment_index]; - $from_base_fragment_length = strlen($from_base_fragment); - // performance boost: cache array keys - if ( !isset($cached_array_keys_for_current_segment[$from_base_fragment]) ) { - if ( !isset($cached_array_keys[$from_base_fragment]) ) { - $to_all_fragment_indices = $cached_array_keys[$from_base_fragment] = array_keys($to_fragments, $from_base_fragment, true); - } - else { - $to_all_fragment_indices = $cached_array_keys[$from_base_fragment]; - } - // get only indices which falls within current segment - if ( $to_segment_start > 0 || $to_segment_end < $to_text_len ) { - $to_fragment_indices = array(); - foreach ( $to_all_fragment_indices as $to_fragment_index ) { - if ( $to_fragment_index < $to_segment_start ) { continue; } - if ( $to_fragment_index >= $to_segment_end ) { break; } - $to_fragment_indices[] = $to_fragment_index; - } - $cached_array_keys_for_current_segment[$from_base_fragment] = $to_fragment_indices; - } - else { - $to_fragment_indices = $to_all_fragment_indices; - } - } - else { - $to_fragment_indices = $cached_array_keys_for_current_segment[$from_base_fragment]; - } - // iterate through collected indices - foreach ( $to_fragment_indices as $to_base_fragment_index ) { - $fragment_index_offset = $from_base_fragment_length; - // iterate until no more match - for (;;) { - $fragment_from_index = $from_base_fragment_index + $fragment_index_offset; - if ( $fragment_from_index >= $from_segment_end ) { - break; - } - $fragment_to_index = $to_base_fragment_index + $fragment_index_offset; - if ( $fragment_to_index >= $to_segment_end ) { - break; - } - if ( $from_fragments[$fragment_from_index] !== $to_fragments[$fragment_to_index] ) { - break; - } - $fragment_length = strlen($from_fragments[$fragment_from_index]); - $fragment_index_offset += $fragment_length; - } - if ( $fragment_index_offset > $best_copy_length ) { - $best_copy_length = $fragment_index_offset; - $best_from_start = $from_base_fragment_index; - $best_to_start = $to_base_fragment_index; - } - } - $from_base_fragment_index += strlen($from_base_fragment); - // If match is larger than half segment size, no point trying to find better - // TODO: Really? - if ( $best_copy_length >= $from_segment_length / 2) { - break; - } - // no point to keep looking if what is left is less than - // current best match - if ( $from_base_fragment_index + $best_copy_length >= $from_segment_end ) { - break; - } - } - - if ( $best_copy_length ) { - $jobs[] = array($from_segment_start, $best_from_start, $to_segment_start, $best_to_start); - $result[$best_from_start * 4 + 2] = new FineDiffCopyOp($best_copy_length); - $jobs[] = array($best_from_start + $best_copy_length, $from_segment_end, $best_to_start + $best_copy_length, $to_segment_end); - } - else { - $result[$from_segment_start * 4 ] = new FineDiffReplaceOp($from_segment_length, substr($to_text, $to_segment_start, $to_segment_length)); - } - } - - ksort($result, SORT_NUMERIC); - return array_values($result); - } - - /** - * Perform a character-level diff. - * - * The algorithm is quite similar to doFragmentDiff(), except that - * the code path is optimized for character-level diff -- strpos() is - * used to find out the longest common subequence of characters. - * - * We try to find a match using the longest possible subsequence, which - * is at most the length of the shortest of the two strings, then incrementally - * reduce the size until a match is found. - * - * I still need to study more the performance of this function. It - * appears that for long strings, the generic doFragmentDiff() is more - * performant. For word-sized strings, doCharDiff() is somewhat more - * performant. - */ - private static function doCharDiff($from_text, $to_text) { - $result = array(); - $jobs = array(array(0, strlen($from_text), 0, strlen($to_text))); - while ( $job = array_pop($jobs) ) { - // get the segments which must be diff'ed - list($from_segment_start, $from_segment_end, $to_segment_start, $to_segment_end) = $job; - $from_segment_len = $from_segment_end - $from_segment_start; - $to_segment_len = $to_segment_end - $to_segment_start; - - // catch easy cases first - if ( !$from_segment_len || !$to_segment_len ) { - if ( $from_segment_len ) { - $result[$from_segment_start * 4 + 0] = new FineDiffDeleteOp($from_segment_len); - } - else if ( $to_segment_len ) { - $result[$from_segment_start * 4 + 1] = new FineDiffInsertOp(substr($to_text, $to_segment_start, $to_segment_len)); - } - continue; - } - if ( $from_segment_len >= $to_segment_len ) { - $copy_len = $to_segment_len; - while ( $copy_len ) { - $to_copy_start = $to_segment_start; - $to_copy_start_max = $to_segment_end - $copy_len; - while ( $to_copy_start <= $to_copy_start_max ) { - $from_copy_start = strpos(substr($from_text, $from_segment_start, $from_segment_len), substr($to_text, $to_copy_start, $copy_len)); - if ( $from_copy_start !== false ) { - $from_copy_start += $from_segment_start; - break 2; - } - $to_copy_start++; - } - $copy_len--; - } - } - else { - $copy_len = $from_segment_len; - while ( $copy_len ) { - $from_copy_start = $from_segment_start; - $from_copy_start_max = $from_segment_end - $copy_len; - while ( $from_copy_start <= $from_copy_start_max ) { - $to_copy_start = strpos(substr($to_text, $to_segment_start, $to_segment_len), substr($from_text, $from_copy_start, $copy_len)); - if ( $to_copy_start !== false ) { - $to_copy_start += $to_segment_start; - break 2; - } - $from_copy_start++; - } - $copy_len--; - } - } - // match found - if ( $copy_len ) { - $jobs[] = array($from_segment_start, $from_copy_start, $to_segment_start, $to_copy_start); - $result[$from_copy_start * 4 + 2] = new FineDiffCopyOp($copy_len); - $jobs[] = array($from_copy_start + $copy_len, $from_segment_end, $to_copy_start + $copy_len, $to_segment_end); - } - // no match, so delete all, insert all - else { - $result[$from_segment_start * 4] = new FineDiffReplaceOp($from_segment_len, substr($to_text, $to_segment_start, $to_segment_len)); - } - } - ksort($result, SORT_NUMERIC); - return array_values($result); - } - - /** - * Efficiently fragment the text into an array according to - * specified delimiters. - * No delimiters means fragment into single character. - * The array indices are the offset of the fragments into - * the input string. - * A sentinel empty fragment is always added at the end. - * Careful: No check is performed as to the validity of the - * delimiters. - */ - private static function extractFragments($text, $delimiters) { - // special case: split into characters - if ( empty($delimiters) ) { - $chars = str_split($text, 1); - $chars[strlen($text)] = ''; - return $chars; - } - $fragments = array(); - $start = $end = 0; - for (;;) { - $end += strcspn($text, $delimiters, $end); - $end += strspn($text, $delimiters, $end); - if ( $end === $start ) { - break; - } - $fragments[$start] = substr($text, $start, $end - $start); - $start = $end; - } - $fragments[$start] = ''; - return $fragments; - } - - /** - * Stock opcode renderers - */ - private static function renderToTextFromOpcode($opcode, $from, $from_offset, $from_len) { - if ( $opcode === 'c' || $opcode === 'i' ) { - echo substr($from, $from_offset, $from_len); - } - } - - private static function renderDiffToHTMLFromOpcode($opcode, $from, $from_offset, $from_len) { - if ( $opcode === 'c' ) { - echo htmlentities(substr($from, $from_offset, $from_len)); - } - else if ( $opcode === 'd' ) { - $deletion = substr($from, $from_offset, $from_len); - if ( strcspn($deletion, " \n\r") === 0 ) { - $deletion = str_replace(array("\n","\r"), array('\n','\r'), $deletion); - } - echo '', htmlentities($deletion), ''; - } - else /* if ( $opcode === 'i' ) */ { - echo '', htmlentities(substr($from, $from_offset, $from_len)), ''; - } - } - } - diff --git a/library/vendor/php-diff/README.md b/library/vendor/php-diff/README.md new file mode 100644 index 0000000..0110b5c --- /dev/null +++ b/library/vendor/php-diff/README.md @@ -0,0 +1,65 @@ +PHP Diff Class +-------------- + +Introduction +------------ +A comprehensive library for generating differences between +two hashable objects (strings or arrays). Generated differences can be +rendered in all of the standard formats including: + * Unified + * Context + * Inline HTML + * Side by Side HTML + +The logic behind the core of the diff engine (ie, the sequence matcher) +is primarily based on the Python difflib package. The reason for doing +so is primarily because of its high degree of accuracy. + +Example Use +----------- +A quick usage example can be found in the example/ directory and under +example.php. + +More complete documentation will be available shortly. + +Merge files using jQuery +------------------------ +Xiphe has build a jQuery plugin with that you can merge the compared +files. Have a look at [jQuery-Merge-for-php-diff](https://github.com/Xiphe/jQuery-Merge-for-php-diff). + +Todo +---- + * Ability to ignore blank line changes + * 3 way diff support + * Performance optimizations + +License (BSD License) +--------------------- +Copyright (c) 2009 Chris Boulton +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + - Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + - Neither the name of the Chris Boulton nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +``` +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +``` diff --git a/library/vendor/php-diff/SOURCE b/library/vendor/php-diff/SOURCE new file mode 100644 index 0000000..c083b23 --- /dev/null +++ b/library/vendor/php-diff/SOURCE @@ -0,0 +1,9 @@ +git clone https://github.com/chrisboulton/php-diff.git +# Last used commit: +cd php-diff +git checkout f4db229d7ae8dffa0a4f90e1adbec9bf22c93d99 +rm -rf .git +rm -rf .gitignore +rm -rf composer.json +rm -rf example +cd .. diff --git a/library/vendor/php-diff/lib/Diff.php b/library/vendor/php-diff/lib/Diff.php new file mode 100644 index 0000000..d1eb9da --- /dev/null +++ b/library/vendor/php-diff/lib/Diff.php @@ -0,0 +1,179 @@ + + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the Chris Boulton nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package Diff + * @author Chris Boulton + * @copyright (c) 2009 Chris Boulton + * @license New BSD License http://www.opensource.org/licenses/bsd-license.php + * @version 1.1 + * @link http://github.com/chrisboulton/php-diff + */ + +class Diff +{ + /** + * @var array The "old" sequence to use as the basis for the comparison. + */ + private $a = null; + + /** + * @var array The "new" sequence to generate the changes for. + */ + private $b = null; + + /** + * @var array Array containing the generated opcodes for the differences between the two items. + */ + private $groupedCodes = null; + + /** + * @var array Associative array of the default options available for the diff class and their default value. + */ + private $defaultOptions = array( + 'context' => 3, + 'ignoreNewLines' => false, + 'ignoreWhitespace' => false, + 'ignoreCase' => false + ); + + /** + * @var array Array of the options that have been applied for generating the diff. + */ + private $options = array(); + + /** + * The constructor. + * + * @param array $a Array containing the lines of the first string to compare. + * @param array $b Array containing the lines for the second string to compare. + */ + public function __construct($a, $b, $options=array()) + { + $this->a = $a; + $this->b = $b; + + if (is_array($options)) + $this->options = array_merge($this->defaultOptions, $options); + else + $this->options = $this->defaultOptions; + } + + /** + * Render a diff using the supplied rendering class and return it. + * + * @param object $renderer An instance of the rendering object to use for generating the diff. + * @return mixed The generated diff. Exact return value depends on the rendered. + */ + public function render(Diff_Renderer_Abstract $renderer) + { + $renderer->diff = $this; + return $renderer->render(); + } + + /** + * Get a range of lines from $start to $end from the first comparison string + * and return them as an array. If no values are supplied, the entire string + * is returned. It's also possible to specify just one line to return only + * that line. + * + * @param int $start The starting number. + * @param int $end The ending number. If not supplied, only the item in $start will be returned. + * @return array Array of all of the lines between the specified range. + */ + public function getA($start=0, $end=null) + { + if($start == 0 && $end === null) { + return $this->a; + } + + if($end === null) { + $length = 1; + } + else { + $length = $end - $start; + } + + return array_slice($this->a, $start, $length); + + } + + /** + * Get a range of lines from $start to $end from the second comparison string + * and return them as an array. If no values are supplied, the entire string + * is returned. It's also possible to specify just one line to return only + * that line. + * + * @param int $start The starting number. + * @param int $end The ending number. If not supplied, only the item in $start will be returned. + * @return array Array of all of the lines between the specified range. + */ + public function getB($start=0, $end=null) + { + if($start == 0 && $end === null) { + return $this->b; + } + + if($end === null) { + $length = 1; + } + else { + $length = $end - $start; + } + + return array_slice($this->b, $start, $length); + } + + /** + * Generate a list of the compiled and grouped opcodes for the differences between the + * two strings. Generally called by the renderer, this class instantiates the sequence + * matcher and performs the actual diff generation and return an array of the opcodes + * for it. Once generated, the results are cached in the diff class instance. + * + * @return array Array of the grouped opcodes for the generated diff. + */ + public function getGroupedOpcodes() + { + if(!is_null($this->groupedCodes)) { + return $this->groupedCodes; + } + + require_once dirname(__FILE__).'/Diff/SequenceMatcher.php'; + $sequenceMatcher = new Diff_SequenceMatcher($this->a, $this->b, null, $this->options); + $this->groupedCodes = $sequenceMatcher->getGroupedOpcodes($this->options['context']); + return $this->groupedCodes; + } +} \ No newline at end of file diff --git a/library/vendor/php-diff/lib/Diff/Renderer/Abstract.php b/library/vendor/php-diff/lib/Diff/Renderer/Abstract.php new file mode 100644 index 0000000..f63c3e7 --- /dev/null +++ b/library/vendor/php-diff/lib/Diff/Renderer/Abstract.php @@ -0,0 +1,82 @@ + + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the Chris Boulton nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package DiffLib + * @author Chris Boulton + * @copyright (c) 2009 Chris Boulton + * @license New BSD License http://www.opensource.org/licenses/bsd-license.php + * @version 1.1 + * @link http://github.com/chrisboulton/php-diff + */ + +abstract class Diff_Renderer_Abstract +{ + /** + * @var object Instance of the diff class that this renderer is generating the rendered diff for. + */ + public $diff; + + /** + * @var array Array of the default options that apply to this renderer. + */ + protected $defaultOptions = array(); + + /** + * @var array Array containing the user applied and merged default options for the renderer. + */ + protected $options = array(); + + /** + * The constructor. Instantiates the rendering engine and if options are passed, + * sets the options for the renderer. + * + * @param array $options Optionally, an array of the options for the renderer. + */ + public function __construct(array $options = array()) + { + $this->setOptions($options); + } + + /** + * Set the options of the renderer to those supplied in the passed in array. + * Options are merged with the default to ensure that there aren't any missing + * options. + * + * @param array $options Array of options to set. + */ + public function setOptions(array $options) + { + $this->options = array_merge($this->defaultOptions, $options); + } +} \ No newline at end of file diff --git a/library/vendor/php-diff/lib/Diff/Renderer/Html/Array.php b/library/vendor/php-diff/lib/Diff/Renderer/Html/Array.php new file mode 100644 index 0000000..76a314d --- /dev/null +++ b/library/vendor/php-diff/lib/Diff/Renderer/Html/Array.php @@ -0,0 +1,231 @@ + + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the Chris Boulton nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package DiffLib + * @author Chris Boulton + * @copyright (c) 2009 Chris Boulton + * @license New BSD License http://www.opensource.org/licenses/bsd-license.php + * @version 1.1 + * @link http://github.com/chrisboulton/php-diff + */ + +require_once dirname(__FILE__).'/../Abstract.php'; + +class Diff_Renderer_Html_Array extends Diff_Renderer_Abstract +{ + /** + * @var array Array of the default options that apply to this renderer. + */ + protected $defaultOptions = array( + 'tabSize' => 4 + ); + + /** + * Render and return an array structure suitable for generating HTML + * based differences. Generally called by subclasses that generate a + * HTML based diff and return an array of the changes to show in the diff. + * + * @return array An array of the generated chances, suitable for presentation in HTML. + */ + public function render() + { + // As we'll be modifying a & b to include our change markers, + // we need to get the contents and store them here. That way + // we're not going to destroy the original data + $a = $this->diff->getA(); + $b = $this->diff->getB(); + + $changes = array(); + $opCodes = $this->diff->getGroupedOpcodes(); + foreach($opCodes as $group) { + $blocks = array(); + $lastTag = null; + $lastBlock = 0; + foreach($group as $code) { + list($tag, $i1, $i2, $j1, $j2) = $code; + + if($tag == 'replace' && $i2 - $i1 == $j2 - $j1) { + for($i = 0; $i < ($i2 - $i1); ++$i) { + $fromLine = $a[$i1 + $i]; + $toLine = $b[$j1 + $i]; + + list($start, $end) = $this->getChangeExtent($fromLine, $toLine); + if($start != 0 || $end != 0) { + $last = $end + strlen($fromLine); + $fromLine = substr_replace($fromLine, "\0", $start, 0); + $fromLine = substr_replace($fromLine, "\1", $last + 1, 0); + $last = $end + strlen($toLine); + $toLine = substr_replace($toLine, "\0", $start, 0); + $toLine = substr_replace($toLine, "\1", $last + 1, 0); + $a[$i1 + $i] = $fromLine; + $b[$j1 + $i] = $toLine; + } + } + } + + if($tag != $lastTag) { + $blocks[] = array( + 'tag' => $tag, + 'base' => array( + 'offset' => $i1, + 'lines' => array() + ), + 'changed' => array( + 'offset' => $j1, + 'lines' => array() + ) + ); + $lastBlock = count($blocks)-1; + } + + $lastTag = $tag; + + if($tag == 'equal') { + $lines = array_slice($a, $i1, ($i2 - $i1)); + $blocks[$lastBlock]['base']['lines'] += $this->formatLines($lines); + $lines = array_slice($b, $j1, ($j2 - $j1)); + $blocks[$lastBlock]['changed']['lines'] += $this->formatLines($lines); + } + else { + if($tag == 'replace' || $tag == 'delete') { + $lines = array_slice($a, $i1, ($i2 - $i1)); + $lines = $this->formatLines($lines); + $lines = str_replace(array("\0", "\1"), array('', ''), $lines); + $blocks[$lastBlock]['base']['lines'] += $lines; + } + + if($tag == 'replace' || $tag == 'insert') { + $lines = array_slice($b, $j1, ($j2 - $j1)); + $lines = $this->formatLines($lines); + $lines = str_replace(array("\0", "\1"), array('', ''), $lines); + $blocks[$lastBlock]['changed']['lines'] += $lines; + } + } + } + $changes[] = $blocks; + } + return $changes; + } + + /** + * Given two strings, determine where the changes in the two strings + * begin, and where the changes in the two strings end. + * + * @param string $fromLine The first string. + * @param string $toLine The second string. + * @return array Array containing the starting position (0 by default) and the ending position (-1 by default) + */ + private function getChangeExtent($fromLine, $toLine) + { + $start = 0; + $limit = min(strlen($fromLine), strlen($toLine)); + while($start < $limit && $fromLine{$start} == $toLine{$start}) { + ++$start; + } + $end = -1; + $limit = $limit - $start; + while(-$end <= $limit && substr($fromLine, $end, 1) == substr($toLine, $end, 1)) { + --$end; + } + return array( + $start, + $end + 1 + ); + } + + /** + * Format a series of lines suitable for output in a HTML rendered diff. + * This involves replacing tab characters with spaces, making the HTML safe + * for output, ensuring that double spaces are replaced with   etc. + * + * @param array $lines Array of lines to format. + * @return array Array of the formatted lines. + */ + protected function formatLines($lines) + { + $lines = array_map(array($this, 'ExpandTabs'), $lines); + $lines = array_map(array($this, 'HtmlSafe'), $lines); + $callback = array($this, 'fixMatchedSpaces'); + foreach($lines as &$line) { + // $line = preg_replace('# ( +)|^ #e', "\$this->fixSpaces('\\1')", $line); + $line = preg_replace_callback('# ( +)|^ #', $callback, $line); + } + return $lines; + } + + protected function fixMatchedSpaces($m) + { + return $this->fixSpaces($m[1]); + } + + /** + * Replace a string containing spaces with a HTML representation using  . + * + * @param string $spaces The string of spaces. + * @return string The HTML representation of the string. + */ + function fixSpaces($spaces='') + { + $count = strlen($spaces); + if($count == 0) { + return ''; + } + + $div = floor($count / 2); + $mod = $count % 2; + return str_repeat('  ', $div).str_repeat(' ', $mod); + } + + /** + * Replace tabs in a single line with a number of spaces as defined by the tabSize option. + * + * @param string $line The containing tabs to convert. + * @return string The line with the tabs converted to spaces. + */ + private function expandTabs($line) + { + return str_replace("\t", str_repeat(' ', $this->options['tabSize']), $line); + } + + /** + * Make a string containing HTML safe for output on a page. + * + * @param string $string The string. + * @return string The string with the HTML characters replaced by entities. + */ + private function htmlSafe($string) + { + return htmlspecialchars($string, ENT_NOQUOTES, 'UTF-8'); + } +} diff --git a/library/vendor/php-diff/lib/Diff/Renderer/Html/Inline.php b/library/vendor/php-diff/lib/Diff/Renderer/Html/Inline.php new file mode 100644 index 0000000..60e8005 --- /dev/null +++ b/library/vendor/php-diff/lib/Diff/Renderer/Html/Inline.php @@ -0,0 +1,143 @@ + + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the Chris Boulton nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package DiffLib + * @author Chris Boulton + * @copyright (c) 2009 Chris Boulton + * @license New BSD License http://www.opensource.org/licenses/bsd-license.php + * @version 1.1 + * @link http://github.com/chrisboulton/php-diff + */ + +require_once dirname(__FILE__).'/Array.php'; + +class Diff_Renderer_Html_Inline extends Diff_Renderer_Html_Array +{ + /** + * Render a and return diff with changes between the two sequences + * displayed inline (under each other) + * + * @return string The generated inline diff. + */ + public function render() + { + $changes = parent::render(); + $html = ''; + if(empty($changes)) { + return $html; + } + + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + foreach($changes as $i => $blocks) { + // If this is a separate block, we're condensing code so output ..., + // indicating a significant portion of the code has been collapsed as + // it is the same + if($i > 0) { + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + + foreach($blocks as $change) { + $html .= ''; + // Equal changes should be shown on both sides of the diff + if($change['tag'] == 'equal') { + foreach($change['base']['lines'] as $no => $line) { + $fromLine = $change['base']['offset'] + $no + 1; + $toLine = $change['changed']['offset'] + $no + 1; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + } + // Added lines only on the right side + else if($change['tag'] == 'insert') { + foreach($change['changed']['lines'] as $no => $line) { + $toLine = $change['changed']['offset'] + $no + 1; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + } + // Show deleted lines only on the left side + else if($change['tag'] == 'delete') { + foreach($change['base']['lines'] as $no => $line) { + $fromLine = $change['base']['offset'] + $no + 1; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + } + // Show modified lines on both sides + else if($change['tag'] == 'replace') { + foreach($change['base']['lines'] as $no => $line) { + $fromLine = $change['base']['offset'] + $no + 1; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + + foreach($change['changed']['lines'] as $no => $line) { + $toLine = $change['changed']['offset'] + $no + 1; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + } + $html .= ''; + } + } + $html .= '
OldNewDifferences
 
'.$fromLine.''.$toLine.''.$line.'
 '.$toLine.''.$line.' 
'.$fromLine.' '.$line.' 
'.$fromLine.' '.$line.'
'.$toLine.' '.$line.'
'; + return $html; + } +} \ No newline at end of file diff --git a/library/vendor/php-diff/lib/Diff/Renderer/Html/SideBySide.php b/library/vendor/php-diff/lib/Diff/Renderer/Html/SideBySide.php new file mode 100644 index 0000000..307af1c --- /dev/null +++ b/library/vendor/php-diff/lib/Diff/Renderer/Html/SideBySide.php @@ -0,0 +1,163 @@ + + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the Chris Boulton nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package DiffLib + * @author Chris Boulton + * @copyright (c) 2009 Chris Boulton + * @license New BSD License http://www.opensource.org/licenses/bsd-license.php + * @version 1.1 + * @link http://github.com/chrisboulton/php-diff + */ + +require_once dirname(__FILE__).'/Array.php'; + +class Diff_Renderer_Html_SideBySide extends Diff_Renderer_Html_Array +{ + /** + * Render a and return diff with changes between the two sequences + * displayed side by side. + * + * @return string The generated side by side diff. + */ + public function render() + { + $changes = parent::render(); + + $html = ''; + if(empty($changes)) { + return $html; + } + + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + foreach($changes as $i => $blocks) { + if($i > 0) { + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + + foreach($blocks as $change) { + $html .= ''; + // Equal changes should be shown on both sides of the diff + if($change['tag'] == 'equal') { + foreach($change['base']['lines'] as $no => $line) { + $fromLine = $change['base']['offset'] + $no + 1; + $toLine = $change['changed']['offset'] + $no + 1; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + } + // Added lines only on the right side + else if($change['tag'] == 'insert') { + foreach($change['changed']['lines'] as $no => $line) { + $toLine = $change['changed']['offset'] + $no + 1; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + } + // Show deleted lines only on the left side + else if($change['tag'] == 'delete') { + foreach($change['base']['lines'] as $no => $line) { + $fromLine = $change['base']['offset'] + $no + 1; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + } + } + // Show modified lines on both sides + else if($change['tag'] == 'replace') { + if(count($change['base']['lines']) >= count($change['changed']['lines'])) { + foreach($change['base']['lines'] as $no => $line) { + $fromLine = $change['base']['offset'] + $no + 1; + $html .= ''; + $html .= ''; + $html .= ''; + if(!isset($change['changed']['lines'][$no])) { + $toLine = ' '; + $changedLine = ' '; + } + else { + $toLine = $change['base']['offset'] + $no + 1; + $changedLine = ''.$change['changed']['lines'][$no].''; + } + $html .= ''; + $html .= ''; + $html .= ''; + } + } + else { + foreach($change['changed']['lines'] as $no => $changedLine) { + if(!isset($change['base']['lines'][$no])) { + $fromLine = ' '; + $line = ' '; + } + else { + $fromLine = $change['base']['offset'] + $no + 1; + $line = ''.$change['base']['lines'][$no].''; + } + $html .= ''; + $html .= ''; + $html .= ''; + $toLine = $change['changed']['offset'] + $no + 1; + $html .= ''; + $html .= ''; + $html .= ''; + } + } + } + $html .= ''; + } + } + $html .= '
Old VersionNew Version
  
'.$fromLine.''.$line.' '.$toLine.''.$line.' 
  '.$toLine.''.$line.' 
'.$fromLine.''.$line.'   
'.$fromLine.''.$line.' '.$toLine.''.$changedLine.'
'.$fromLine.''.$line.' '.$toLine.''.$changedLine.'
'; + return $html; + } +} \ No newline at end of file diff --git a/library/vendor/php-diff/lib/Diff/Renderer/Text/Context.php b/library/vendor/php-diff/lib/Diff/Renderer/Text/Context.php new file mode 100644 index 0000000..1200b01 --- /dev/null +++ b/library/vendor/php-diff/lib/Diff/Renderer/Text/Context.php @@ -0,0 +1,128 @@ + + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the Chris Boulton nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package DiffLib + * @author Chris Boulton + * @copyright (c) 2009 Chris Boulton + * @license New BSD License http://www.opensource.org/licenses/bsd-license.php + * @version 1.1 + * @link http://github.com/chrisboulton/php-diff + */ + +require_once dirname(__FILE__).'/../Abstract.php'; + +class Diff_Renderer_Text_Context extends Diff_Renderer_Abstract +{ + /** + * @var array Array of the different opcode tags and how they map to the context diff equivalent. + */ + private $tagMap = array( + 'insert' => '+', + 'delete' => '-', + 'replace' => '!', + 'equal' => ' ' + ); + + /** + * Render and return a context formatted (old school!) diff file. + * + * @return string The generated context diff. + */ + public function render() + { + $diff = ''; + $opCodes = $this->diff->getGroupedOpcodes(); + foreach($opCodes as $group) { + $diff .= "***************\n"; + $lastItem = count($group)-1; + $i1 = $group[0][1]; + $i2 = $group[$lastItem][2]; + $j1 = $group[0][3]; + $j2 = $group[$lastItem][4]; + + if($i2 - $i1 >= 2) { + $diff .= '*** '.($group[0][1] + 1).','.$i2." ****\n"; + } + else { + $diff .= '*** '.$i2." ****\n"; + } + + if($j2 - $j1 >= 2) { + $separator = '--- '.($j1 + 1).','.$j2." ----\n"; + } + else { + $separator = '--- '.$j2." ----\n"; + } + + $hasVisible = false; + foreach($group as $code) { + if($code[0] == 'replace' || $code[0] == 'delete') { + $hasVisible = true; + break; + } + } + + if($hasVisible) { + foreach($group as $code) { + list($tag, $i1, $i2, $j1, $j2) = $code; + if($tag == 'insert') { + continue; + } + $diff .= $this->tagMap[$tag].' '.implode("\n".$this->tagMap[$tag].' ', $this->diff->GetA($i1, $i2))."\n"; + } + } + + $hasVisible = false; + foreach($group as $code) { + if($code[0] == 'replace' || $code[0] == 'insert') { + $hasVisible = true; + break; + } + } + + $diff .= $separator; + + if($hasVisible) { + foreach($group as $code) { + list($tag, $i1, $i2, $j1, $j2) = $code; + if($tag == 'delete') { + continue; + } + $diff .= $this->tagMap[$tag].' '.implode("\n".$this->tagMap[$tag].' ', $this->diff->GetB($j1, $j2))."\n"; + } + } + } + return $diff; + } +} \ No newline at end of file diff --git a/library/vendor/php-diff/lib/Diff/Renderer/Text/Unified.php b/library/vendor/php-diff/lib/Diff/Renderer/Text/Unified.php new file mode 100644 index 0000000..e94d951 --- /dev/null +++ b/library/vendor/php-diff/lib/Diff/Renderer/Text/Unified.php @@ -0,0 +1,87 @@ + + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the Chris Boulton nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package DiffLib + * @author Chris Boulton + * @copyright (c) 2009 Chris Boulton + * @license New BSD License http://www.opensource.org/licenses/bsd-license.php + * @version 1.1 + * @link http://github.com/chrisboulton/php-diff + */ + +require_once dirname(__FILE__).'/../Abstract.php'; + +class Diff_Renderer_Text_Unified extends Diff_Renderer_Abstract +{ + /** + * Render and return a unified diff. + * + * @return string The unified diff. + */ + public function render() + { + $diff = ''; + $opCodes = $this->diff->getGroupedOpcodes(); + foreach($opCodes as $group) { + $lastItem = count($group)-1; + $i1 = $group[0][1]; + $i2 = $group[$lastItem][2]; + $j1 = $group[0][3]; + $j2 = $group[$lastItem][4]; + + if($i1 == 0 && $i2 == 0) { + $i1 = -1; + $i2 = -1; + } + + $diff .= '@@ -'.($i1 + 1).','.($i2 - $i1).' +'.($j1 + 1).','.($j2 - $j1)." @@\n"; + foreach($group as $code) { + list($tag, $i1, $i2, $j1, $j2) = $code; + if($tag == 'equal') { + $diff .= ' '.implode("\n ", $this->diff->GetA($i1, $i2))."\n"; + } + else { + if($tag == 'replace' || $tag == 'delete') { + $diff .= '-'.implode("\n-", $this->diff->GetA($i1, $i2))."\n"; + } + + if($tag == 'replace' || $tag == 'insert') { + $diff .= '+'.implode("\n+", $this->diff->GetB($j1, $j2))."\n"; + } + } + } + } + return $diff; + } +} \ No newline at end of file diff --git a/library/vendor/php-diff/lib/Diff/SequenceMatcher.php b/library/vendor/php-diff/lib/Diff/SequenceMatcher.php new file mode 100644 index 0000000..e819e81 --- /dev/null +++ b/library/vendor/php-diff/lib/Diff/SequenceMatcher.php @@ -0,0 +1,742 @@ + + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * - Neither the name of the Chris Boulton nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package Diff + * @author Chris Boulton + * @copyright (c) 2009 Chris Boulton + * @license New BSD License http://www.opensource.org/licenses/bsd-license.php + * @version 1.1 + * @link http://github.com/chrisboulton/php-diff + */ + +class Diff_SequenceMatcher +{ + /** + * @var string|array Either a string or an array containing a callback function to determine if a line is "junk" or not. + */ + private $junkCallback = null; + + /** + * @var array The first sequence to compare against. + */ + private $a = null; + + /** + * @var array The second sequence. + */ + private $b = null; + + /** + * @var array Array of characters that are considered junk from the second sequence. Characters are the array key. + */ + private $junkDict = array(); + + /** + * @var array Array of indices that do not contain junk elements. + */ + private $b2j = array(); + + private $options = array(); + + private $defaultOptions = array( + 'ignoreNewLines' => false, + 'ignoreWhitespace' => false, + 'ignoreCase' => false + ); + + /** + * The constructor. With the sequences being passed, they'll be set for the + * sequence matcher and it will perform a basic cleanup & calculate junk + * elements. + * + * @param string|array $a A string or array containing the lines to compare against. + * @param string|array $b A string or array containing the lines to compare. + * @param string|array $junkCallback Either an array or string that references a callback function (if there is one) to determine 'junk' characters. + */ + public function __construct($a, $b, $junkCallback=null, $options) + { + $this->a = null; + $this->b = null; + $this->junkCallback = $junkCallback; + $this->setOptions($options); + $this->setSequences($a, $b); + } + + public function setOptions($options) + { + $this->options = array_merge($this->defaultOptions, $options); + } + + /** + * Set the first and second sequences to use with the sequence matcher. + * + * @param string|array $a A string or array containing the lines to compare against. + * @param string|array $b A string or array containing the lines to compare. + */ + public function setSequences($a, $b) + { + $this->setSeq1($a); + $this->setSeq2($b); + } + + /** + * Set the first sequence ($a) and reset any internal caches to indicate that + * when calling the calculation methods, we need to recalculate them. + * + * @param string|array $a The sequence to set as the first sequence. + */ + public function setSeq1($a) + { + if(!is_array($a)) { + $a = str_split($a); + } + if($a == $this->a) { + return; + } + + $this->a= $a; + $this->matchingBlocks = null; + $this->opCodes = null; + } + + /** + * Set the second sequence ($b) and reset any internal caches to indicate that + * when calling the calculation methods, we need to recalculate them. + * + * @param string|array $b The sequence to set as the second sequence. + */ + public function setSeq2($b) + { + if(!is_array($b)) { + $b = str_split($b); + } + if($b == $this->b) { + return; + } + + $this->b = $b; + $this->matchingBlocks = null; + $this->opCodes = null; + $this->fullBCount = null; + $this->chainB(); + } + + /** + * Generate the internal arrays containing the list of junk and non-junk + * characters for the second ($b) sequence. + */ + private function chainB() + { + $length = count ($this->b); + $this->b2j = array(); + $popularDict = array(); + + for($i = 0; $i < $length; ++$i) { + $char = $this->b[$i]; + if(isset($this->b2j[$char])) { + if($length >= 200 && count($this->b2j[$char]) * 100 > $length) { + $popularDict[$char] = 1; + unset($this->b2j[$char]); + } + else { + $this->b2j[$char][] = $i; + } + } + else { + $this->b2j[$char] = array( + $i + ); + } + } + + // Remove leftovers + foreach(array_keys($popularDict) as $char) { + unset($this->b2j[$char]); + } + + $this->junkDict = array(); + if(is_callable($this->junkCallback)) { + foreach(array_keys($popularDict) as $char) { + if(call_user_func($this->junkCallback, $char)) { + $this->junkDict[$char] = 1; + unset($popularDict[$char]); + } + } + + foreach(array_keys($this->b2j) as $char) { + if(call_user_func($this->junkCallback, $char)) { + $this->junkDict[$char] = 1; + unset($this->b2j[$char]); + } + } + } + } + + /** + * Checks if a particular character is in the junk dictionary + * for the list of junk characters. + * + * @return boolean $b True if the character is considered junk. False if not. + */ + private function isBJunk($b) + { + if(isset($this->juncDict[$b])) { + return true; + } + + return false; + } + + /** + * Find the longest matching block in the two sequences, as defined by the + * lower and upper constraints for each sequence. (for the first sequence, + * $alo - $ahi and for the second sequence, $blo - $bhi) + * + * Essentially, of all of the maximal matching blocks, return the one that + * startest earliest in $a, and all of those maximal matching blocks that + * start earliest in $a, return the one that starts earliest in $b. + * + * If the junk callback is defined, do the above but with the restriction + * that the junk element appears in the block. Extend it as far as possible + * by matching only junk elements in both $a and $b. + * + * @param int $alo The lower constraint for the first sequence. + * @param int $ahi The upper constraint for the first sequence. + * @param int $blo The lower constraint for the second sequence. + * @param int $bhi The upper constraint for the second sequence. + * @return array Array containing the longest match that includes the starting position in $a, start in $b and the length/size. + */ + public function findLongestMatch($alo, $ahi, $blo, $bhi) + { + $a = $this->a; + $b = $this->b; + + $bestI = $alo; + $bestJ = $blo; + $bestSize = 0; + + $j2Len = array(); + $nothing = array(); + + for($i = $alo; $i < $ahi; ++$i) { + $newJ2Len = array(); + $jDict = $this->arrayGetDefault($this->b2j, $a[$i], $nothing); + foreach($jDict as $jKey => $j) { + if($j < $blo) { + continue; + } + else if($j >= $bhi) { + break; + } + + $k = $this->arrayGetDefault($j2Len, $j -1, 0) + 1; + $newJ2Len[$j] = $k; + if($k > $bestSize) { + $bestI = $i - $k + 1; + $bestJ = $j - $k + 1; + $bestSize = $k; + } + } + + $j2Len = $newJ2Len; + } + + while($bestI > $alo && $bestJ > $blo && !$this->isBJunk($b[$bestJ - 1]) && + !$this->linesAreDifferent($bestI - 1, $bestJ - 1)) { + --$bestI; + --$bestJ; + ++$bestSize; + } + + while($bestI + $bestSize < $ahi && ($bestJ + $bestSize) < $bhi && + !$this->isBJunk($b[$bestJ + $bestSize]) && !$this->linesAreDifferent($bestI + $bestSize, $bestJ + $bestSize)) { + ++$bestSize; + } + + while($bestI > $alo && $bestJ > $blo && $this->isBJunk($b[$bestJ - 1]) && + !$this->isLineDifferent($bestI - 1, $bestJ - 1)) { + --$bestI; + --$bestJ; + ++$bestSize; + } + + while($bestI + $bestSize < $ahi && $bestJ + $bestSize < $bhi && + $this->isBJunk($b[$bestJ + $bestSize]) && !$this->linesAreDifferent($bestI + $bestSize, $bestJ + $bestSize)) { + ++$bestSize; + } + + return array( + $bestI, + $bestJ, + $bestSize + ); + } + + /** + * Check if the two lines at the given indexes are different or not. + * + * @param int $aIndex Line number to check against in a. + * @param int $bIndex Line number to check against in b. + * @return boolean True if the lines are different and false if not. + */ + public function linesAreDifferent($aIndex, $bIndex) + { + $lineA = $this->a[$aIndex]; + $lineB = $this->b[$bIndex]; + + if($this->options['ignoreWhitespace']) { + $replace = array("\t", ' '); + $lineA = str_replace($replace, '', $lineA); + $lineB = str_replace($replace, '', $lineB); + } + + if($this->options['ignoreCase']) { + $lineA = strtolower($lineA); + $lineB = strtolower($lineB); + } + + if($lineA != $lineB) { + return true; + } + + return false; + } + + /** + * Return a nested set of arrays for all of the matching sub-sequences + * in the strings $a and $b. + * + * Each block contains the lower constraint of the block in $a, the lower + * constraint of the block in $b and finally the number of lines that the + * block continues for. + * + * @return array Nested array of the matching blocks, as described by the function. + */ + public function getMatchingBlocks() + { + if(!empty($this->matchingBlocks)) { + return $this->matchingBlocks; + } + + $aLength = count($this->a); + $bLength = count($this->b); + + $queue = array( + array( + 0, + $aLength, + 0, + $bLength + ) + ); + + $matchingBlocks = array(); + while(!empty($queue)) { + list($alo, $ahi, $blo, $bhi) = array_pop($queue); + $x = $this->findLongestMatch($alo, $ahi, $blo, $bhi); + list($i, $j, $k) = $x; + if($k) { + $matchingBlocks[] = $x; + if($alo < $i && $blo < $j) { + $queue[] = array( + $alo, + $i, + $blo, + $j + ); + } + + if($i + $k < $ahi && $j + $k < $bhi) { + $queue[] = array( + $i + $k, + $ahi, + $j + $k, + $bhi + ); + } + } + } + + usort($matchingBlocks, array($this, 'tupleSort')); + + $i1 = 0; + $j1 = 0; + $k1 = 0; + $nonAdjacent = array(); + foreach($matchingBlocks as $block) { + list($i2, $j2, $k2) = $block; + if($i1 + $k1 == $i2 && $j1 + $k1 == $j2) { + $k1 += $k2; + } + else { + if($k1) { + $nonAdjacent[] = array( + $i1, + $j1, + $k1 + ); + } + + $i1 = $i2; + $j1 = $j2; + $k1 = $k2; + } + } + + if($k1) { + $nonAdjacent[] = array( + $i1, + $j1, + $k1 + ); + } + + $nonAdjacent[] = array( + $aLength, + $bLength, + 0 + ); + + $this->matchingBlocks = $nonAdjacent; + return $this->matchingBlocks; + } + + /** + * Return a list of all of the opcodes for the differences between the + * two strings. + * + * The nested array returned contains an array describing the opcode + * which includes: + * 0 - The type of tag (as described below) for the opcode. + * 1 - The beginning line in the first sequence. + * 2 - The end line in the first sequence. + * 3 - The beginning line in the second sequence. + * 4 - The end line in the second sequence. + * + * The different types of tags include: + * replace - The string from $i1 to $i2 in $a should be replaced by + * the string in $b from $j1 to $j2. + * delete - The string in $a from $i1 to $j2 should be deleted. + * insert - The string in $b from $j1 to $j2 should be inserted at + * $i1 in $a. + * equal - The two strings with the specified ranges are equal. + * + * @return array Array of the opcodes describing the differences between the strings. + */ + public function getOpCodes() + { + if(!empty($this->opCodes)) { + return $this->opCodes; + } + + $i = 0; + $j = 0; + $this->opCodes = array(); + + $blocks = $this->getMatchingBlocks(); + foreach($blocks as $block) { + list($ai, $bj, $size) = $block; + $tag = ''; + if($i < $ai && $j < $bj) { + $tag = 'replace'; + } + else if($i < $ai) { + $tag = 'delete'; + } + else if($j < $bj) { + $tag = 'insert'; + } + + if($tag) { + $this->opCodes[] = array( + $tag, + $i, + $ai, + $j, + $bj + ); + } + + $i = $ai + $size; + $j = $bj + $size; + + if($size) { + $this->opCodes[] = array( + 'equal', + $ai, + $i, + $bj, + $j + ); + } + } + return $this->opCodes; + } + + /** + * Return a series of nested arrays containing different groups of generated + * opcodes for the differences between the strings with up to $context lines + * of surrounding content. + * + * Essentially what happens here is any big equal blocks of strings are stripped + * out, the smaller subsets of changes are then arranged in to their groups. + * This means that the sequence matcher and diffs do not need to include the full + * content of the different files but can still provide context as to where the + * changes are. + * + * @param int $context The number of lines of context to provide around the groups. + * @return array Nested array of all of the grouped opcodes. + */ + public function getGroupedOpcodes($context=3) + { + $opCodes = $this->getOpCodes(); + if(empty($opCodes)) { + $opCodes = array( + array( + 'equal', + 0, + 1, + 0, + 1 + ) + ); + } + + if($opCodes[0][0] == 'equal') { + $opCodes[0] = array( + $opCodes[0][0], + max($opCodes[0][1], $opCodes[0][2] - $context), + $opCodes[0][2], + max($opCodes[0][3], $opCodes[0][4] - $context), + $opCodes[0][4] + ); + } + + $lastItem = count($opCodes) - 1; + if($opCodes[$lastItem][0] == 'equal') { + list($tag, $i1, $i2, $j1, $j2) = $opCodes[$lastItem]; + $opCodes[$lastItem] = array( + $tag, + $i1, + min($i2, $i1 + $context), + $j1, + min($j2, $j1 + $context) + ); + } + + $maxRange = $context * 2; + $groups = array(); + $group = array(); + foreach($opCodes as $code) { + list($tag, $i1, $i2, $j1, $j2) = $code; + if($tag == 'equal' && $i2 - $i1 > $maxRange) { + $group[] = array( + $tag, + $i1, + min($i2, $i1 + $context), + $j1, + min($j2, $j1 + $context) + ); + $groups[] = $group; + $group = array(); + $i1 = max($i1, $i2 - $context); + $j1 = max($j1, $j2 - $context); + } + $group[] = array( + $tag, + $i1, + $i2, + $j1, + $j2 + ); + } + + if(!empty($group) && !(count($group) == 1 && $group[0][0] == 'equal')) { + $groups[] = $group; + } + + return $groups; + } + + /** + * Return a measure of the similarity between the two sequences. + * This will be a float value between 0 and 1. + * + * Out of all of the ratio calculation functions, this is the most + * expensive to call if getMatchingBlocks or getOpCodes is yet to be + * called. The other calculation methods (quickRatio and realquickRatio) + * can be used to perform quicker calculations but may be less accurate. + * + * The ratio is calculated as (2 * number of matches) / total number of + * elements in both sequences. + * + * @return float The calculated ratio. + */ + public function Ratio() + { + $matches = array_reduce($this->getMatchingBlocks(), array($this, 'ratioReduce'), 0); + return $this->calculateRatio($matches, count ($this->a) + count ($this->b)); + } + + /** + * Helper function to calculate the number of matches for Ratio(). + * + * @param int $sum The running total for the number of matches. + * @param array $triple Array containing the matching block triple to add to the running total. + * @return int The new running total for the number of matches. + */ + private function ratioReduce($sum, $triple) + { + return $sum + ($triple[count($triple) - 1]); + } + + /** + * Quickly return an upper bound ratio for the similarity of the strings. + * This is quicker to compute than Ratio(). + * + * @return float The calculated ratio. + */ + private function quickRatio() + { + if($this->fullBCount === null) { + $this->fullBCount = array(); + $bLength = count ($b); + for($i = 0; $i < $bLength; ++$i) { + $char = $this->b[$i]; + $this->fullBCount[$char] = $this->arrayGetDefault($this->fullBCount, $char, 0) + 1; + } + } + + $avail = array(); + $matches = 0; + $aLength = count ($this->a); + for($i = 0; $i < $aLength; ++$i) { + $char = $this->a[$i]; + if(isset($avail[$char])) { + $numb = $avail[$char]; + } + else { + $numb = $this->arrayGetDefault($this->fullBCount, $char, 0); + } + $avail[$char] = $numb - 1; + if($numb > 0) { + ++$matches; + } + } + + $this->calculateRatio($matches, count ($this->a) + count ($this->b)); + } + + /** + * Return an upper bound ratio really quickly for the similarity of the strings. + * This is quicker to compute than Ratio() and quickRatio(). + * + * @return float The calculated ratio. + */ + private function realquickRatio() + { + $aLength = count ($this->a); + $bLength = count ($this->b); + + return $this->calculateRatio(min($aLength, $bLength), $aLength + $bLength); + } + + /** + * Helper function for calculating the ratio to measure similarity for the strings. + * The ratio is defined as being 2 * (number of matches / total length) + * + * @param int $matches The number of matches in the two strings. + * @param int $length The length of the two strings. + * @return float The calculated ratio. + */ + private function calculateRatio($matches, $length=0) + { + if($length) { + return 2 * ($matches / $length); + } + else { + return 1; + } + } + + /** + * Helper function that provides the ability to return the value for a key + * in an array of it exists, or if it doesn't then return a default value. + * Essentially cleaner than doing a series of if(isset()) {} else {} calls. + * + * @param array $array The array to search. + * @param string $key The key to check that exists. + * @param mixed $default The value to return as the default value if the key doesn't exist. + * @return mixed The value from the array if the key exists or otherwise the default. + */ + private function arrayGetDefault($array, $key, $default) + { + if(isset($array[$key])) { + return $array[$key]; + } + else { + return $default; + } + } + + /** + * Sort an array by the nested arrays it contains. Helper function for getMatchingBlocks + * + * @param array $a First array to compare. + * @param array $b Second array to compare. + * @return int -1, 0 or 1, as expected by the usort function. + */ + private function tupleSort($a, $b) + { + $max = max(count($a), count($b)); + for($i = 0; $i < $max; ++$i) { + if($a[$i] < $b[$i]) { + return -1; + } + else if($a[$i] > $b[$i]) { + return 1; + } + } + + if(count($a) == $count($b)) { + return 0; + } + else if(count($a) < count($b)) { + return -1; + } + else { + return 1; + } + } +} \ No newline at end of file diff --git a/public/css/module.less b/public/css/module.less index 79b1ee7..76244ce 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -1005,4 +1005,104 @@ form { } } -/** End of forms **/ +/** END of forms **/ + +/** php-diff **/ +.Differences { + width: 100%; + table-layout: fixed; + empty-cells: show; +} + +.Differences thead { + display: none; +} + +.Differences thead th { + text-align: left; + padding-left: 4 / 14 * 16em; +} +.Differences tbody th { + text-align: right; + width: 4em; + padding: 1px 2px; + border-right: 1px solid @gray-light; + background: @gray-lightest; + font-weight: normal; + vertical-align: top; +} + +.Differences tbody td { + width: 50%; + .preformatted(); + word-break: break-all; +} + +@color-diff-ins: #bfb; +@color-diff-del: #faa; +@color-diff-changed-old: #fdd; +@color-diff-changed-new: #efe; + +.DifferencesSideBySide { + ins, del { + text-decoration: none; + } + + .ChangeInsert { + td.Left { + background: @gray-lighter; + } + td.Right { + background: @color-diff-ins; + } + } + + .ChangeDelete { + td.Left { + background: @color-diff-del; + } + td.Right { + background: @gray-lighter; + } + } + + .ChangeReplace { + td.Left { + background: @color-diff-changed-old; + del { + background: @color-diff-del; + } + } + + td.Right { + background: @color-diff-changed-new; + ins { + background: @color-diff-ins; + } + } + + } +} + +.Differences .Skipped { + background: @gray-lightest; +} + +.DifferencesInline .ChangeReplace .Left, +.DifferencesInline .ChangeDelete .Left { + background: #fdd; +} + +.DifferencesInline .ChangeReplace .Right, +.DifferencesInline .ChangeInsert .Right { + background: #dfd; +} + +.DifferencesInline .ChangeReplace ins { + background: #9e9; +} + +.DifferencesInline .ChangeReplace del { + background: #e99; +} +/** END of php-diff **/ From dcba547f8f32c3f1f10ef593befb6ddf3df5ddc5 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:05:09 +0100 Subject: [PATCH 198/256] LegacyStorageTest: be strict on natsort and test... ...imported nodes --- .../businessprocess/processes/combined.conf | 1 + .../Businessprocess/Storage/LegacyStorageTest.php | 14 +++++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 test/config/modules/businessprocess/processes/combined.conf diff --git a/test/config/modules/businessprocess/processes/combined.conf b/test/config/modules/businessprocess/processes/combined.conf new file mode 100644 index 0000000..3b0fc5d --- /dev/null +++ b/test/config/modules/businessprocess/processes/combined.conf @@ -0,0 +1 @@ +all = @simple_with-header:top & @simple_without-header:minTwo & @simple_with-header:minTwo \ No newline at end of file diff --git a/test/php/library/Businessprocess/Storage/LegacyStorageTest.php b/test/php/library/Businessprocess/Storage/LegacyStorageTest.php index b0254f4..4c5425a 100644 --- a/test/php/library/Businessprocess/Storage/LegacyStorageTest.php +++ b/test/php/library/Businessprocess/Storage/LegacyStorageTest.php @@ -29,10 +29,10 @@ class LegacyStorageTest extends BaseTestCase public function testAllAvailableProcessesAreListed() { $keys = array_keys($this->makeInstance()->listProcesses()); - sort($keys); $this->assertEquals( array( 'broken_wrong-operator', + 'combined', 'simple_with-header', 'simple_without-header', ), @@ -43,11 +43,11 @@ class LegacyStorageTest extends BaseTestCase public function testHeaderTitlesAreRespectedInProcessList() { $keys = array_values($this->makeInstance()->listProcesses()); - sort($keys); $this->assertEquals( array( - 'Simple with header (simple_with-header)', 'broken_wrong-operator', + 'combined', + 'Simple with header (simple_with-header)', 'simple_without-header', ), $keys @@ -119,4 +119,12 @@ class LegacyStorageTest extends BaseTestCase $this->makeLoop() ); } + + public function testImportedNodesCanBeParsed() + { + $this->assertInstanceOf( + $this->processClass, + $this->makeInstance()->loadProcess('combined') + ); + } } From 4f32818488a1b95cbf8083ed013665145d8dbc74 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:08:38 +0100 Subject: [PATCH 199/256] NodeController: drop it --- application/controllers/NodeController.php | 35 ---------------------- application/views/scripts/node/edit.phtml | 15 ---------- 2 files changed, 50 deletions(-) delete mode 100644 application/controllers/NodeController.php delete mode 100644 application/views/scripts/node/edit.phtml diff --git a/application/controllers/NodeController.php b/application/controllers/NodeController.php deleted file mode 100644 index e44f20a..0000000 --- a/application/controllers/NodeController.php +++ /dev/null @@ -1,35 +0,0 @@ - -process = - -*/ -class NodeController extends Controller -{ - // rename to config - public function editAction() - { - $bp = $this->loadModifiedBpConfig(); - $node = $bp->getNode($this->getParam('node')); - $url = Url::fromPath( - 'businessprocess/process/show?unlocked', - array('config' => $bp->getName()) - ); - - $this->view->form = $this->loadForm('process') - ->setProcess($bp) - ->setSession($this->session()) - ->setNode($node) - ->setSuccessUrl($url) - ->handleRequest(); - - $this->view->node = $node; - } -} diff --git a/application/views/scripts/node/edit.phtml b/application/views/scripts/node/edit.phtml deleted file mode 100644 index ad2ea47..0000000 --- a/application/views/scripts/node/edit.phtml +++ /dev/null @@ -1,15 +0,0 @@ -
-

escape( - sprintf( - $this->translate('Modify process node: %s'), - $node - ) -) ?>

-form ?> -
From 33908bead02fb78db66ea00785a6cf66b193520f Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:10:07 +0100 Subject: [PATCH 200/256] js: allow to click on whole tile --- public/js/module.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/public/js/module.js b/public/js/module.js index 197a983..f524d6d 100644 --- a/public/js/module.js +++ b/public/js/module.js @@ -33,6 +33,8 @@ 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', 'div.tiles > div', this.tileClick); + this.module.icinga.logger.debug('BP module initialized'); }, @@ -77,6 +79,10 @@ $container.find('dd').not('.active').find('p.description').hide(); }, + tileClick: function(event) { + $(event.currentTarget).find('> a').first().trigger('click'); + }, + /** * Add 'hovered' class to hovered title elements * From 5f0ef2f5fbdf0975204835d44809f17e9572d616 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:10:27 +0100 Subject: [PATCH 201/256] css: make links inside forms visible --- public/css/module.less | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/public/css/module.less b/public/css/module.less index 76244ce..a4ce519 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -14,6 +14,10 @@ a:focus { margin-right: 1em; } +form a { + color: @icinga-blue; +} + div.bp { margin-bottom: 4px; } From 898576f63a937ade3ee12e9c9b88dbd629251ca1 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:12:20 +0100 Subject: [PATCH 202/256] RenderedProcessActionBar: introduce new class... ...to keep ActionBar more generic --- application/controllers/ProcessController.php | 7 +- .../Web/Component/ActionBar.php | 99 ---------------- .../Component/RenderedProcessActionBar.php | 108 ++++++++++++++++++ 3 files changed, 113 insertions(+), 101 deletions(-) create mode 100644 library/Businessprocess/Web/Component/RenderedProcessActionBar.php diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 00f3c80..6816d77 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -8,14 +8,15 @@ use Icinga\Module\Businessprocess\Html\Element; use Icinga\Module\Businessprocess\Html\HtmlString; use Icinga\Module\Businessprocess\Html\HtmlTag; use Icinga\Module\Businessprocess\Html\Icon; +use Icinga\Module\Businessprocess\Html\Link; use Icinga\Module\Businessprocess\Node; use Icinga\Module\Businessprocess\Renderer\Breadcrumb; use Icinga\Module\Businessprocess\Renderer\Renderer; use Icinga\Module\Businessprocess\Renderer\TileRenderer; use Icinga\Module\Businessprocess\Renderer\TreeRenderer; use Icinga\Module\Businessprocess\Simulation; -use Icinga\Module\Businessprocess\Html\Link; use Icinga\Module\Businessprocess\Web\Component\ActionBar; +use Icinga\Module\Businessprocess\Web\Component\RenderedProcessActionBar; use Icinga\Module\Businessprocess\Web\Component\Tabs; use Icinga\Module\Businessprocess\Web\Controller; use Icinga\Module\Businessprocess\Web\Url; @@ -122,7 +123,9 @@ class ProcessController extends Controller } $controls->add(Breadcrumb::create($renderer)); if (! $this->showFullscreen && ! $this->view->compact) { - $controls->add(new ActionBar($bp, $renderer, $this->Auth(), $this->url())); + $controls->add( + new RenderedProcessActionBar($bp, $renderer, $this->Auth(), $this->url()) + ); } } diff --git a/library/Businessprocess/Web/Component/ActionBar.php b/library/Businessprocess/Web/Component/ActionBar.php index 7536b9f..3453af2 100644 --- a/library/Businessprocess/Web/Component/ActionBar.php +++ b/library/Businessprocess/Web/Component/ActionBar.php @@ -2,12 +2,7 @@ namespace Icinga\Module\Businessprocess\Web\Component; -use Icinga\Authentication\Auth; -use Icinga\Module\Businessprocess\BusinessProcess; use Icinga\Module\Businessprocess\Html\BaseElement; -use Icinga\Module\Businessprocess\Html\Link; -use Icinga\Module\Businessprocess\Renderer\Renderer; -use Icinga\Module\Businessprocess\Renderer\TreeRenderer; class ActionBar extends BaseElement { @@ -17,98 +12,4 @@ class ActionBar extends BaseElement protected $tag = 'div'; protected $defaultAttributes = array('class' => 'action-bar'); - - public function __construct(BusinessProcess $config, Renderer $renderer, Auth $auth, $url) - { - $meta = $config->getMetadata(); - - if ($renderer instanceof TreeRenderer) { - $this->add( - Link::create( - $this->translate('Tiles'), - $url->with('mode', 'tile'), - null, - array('class' => 'icon-dashboard') - ) - ); - } else { - $this->add( - Link::create( - $this->translate('Tree'), - $url->with('mode', 'tree'), - null, - array('class' => 'icon-sitemap') - ) - ); - } - - $this->add( - Link::create( - $this->translate('Fullscreen'), - $url->with('showFullscreen', true), - null, - array( - 'class' => 'icon-resize-full-alt', - 'title' => $this->translate('Switch to fullscreen mode'), - 'data-base-target' => '_main', - ) - ) - ); - - $hasChanges = $config->hasSimulations() || $config->hasBeenChanged(); - - if ($renderer->isLocked()) { - $this->add( - Link::create( - $this->translate('Unlock'), - $url->with('unlocked', true), - null, - array( - 'class' => 'icon-lock-open', - 'title' => $this->translate('Unlock this process'), - ) - ) - ); - } elseif (! $hasChanges) { - $this->add( - Link::create( - $this->translate('Lock'), - $url->without('unlocked')->without('action'), - null, - array( - 'class' => 'icon-lock', - 'title' => $this->translate('Lock this process'), - ) - ) - ); - } - - if ($hasChanges || (! $renderer->isLocked()) && $meta->canModify()) { - $this->add( - Link::create( - $this->translate('Config'), - 'businessprocess/process/config', - $this->currentProcessParams($url), - array( - 'class' => 'icon-wrench', - 'title' => $this->translate('Modify this process'), - 'data-base-target' => '_next', - ) - ) - ); - } - } - - protected function currentProcessParams($url) - { - $urlParams = $url->getParams(); - $params = array(); - foreach (array('config', 'node') as $name) { - if ($value = $urlParams->get($name)) { - $params[$name] = $value; - } - } - - return $params; - } } diff --git a/library/Businessprocess/Web/Component/RenderedProcessActionBar.php b/library/Businessprocess/Web/Component/RenderedProcessActionBar.php new file mode 100644 index 0000000..3d67e62 --- /dev/null +++ b/library/Businessprocess/Web/Component/RenderedProcessActionBar.php @@ -0,0 +1,108 @@ +getMetadata(); + + if ($renderer instanceof TreeRenderer) { + $this->add( + Link::create( + $this->translate('Tiles'), + $url->with('mode', 'tile'), + null, + array('class' => 'icon-dashboard') + ) + ); + } else { + $this->add( + Link::create( + $this->translate('Tree'), + $url->with('mode', 'tree'), + null, + array('class' => 'icon-sitemap') + ) + ); + } + + $this->add( + Link::create( + $this->translate('Fullscreen'), + $url->with('showFullscreen', true), + null, + array( + 'class' => 'icon-resize-full-alt', + 'title' => $this->translate('Switch to fullscreen mode'), + 'data-base-target' => '_main', + ) + ) + ); + + $hasChanges = $config->hasSimulations() || $config->hasBeenChanged(); + + if ($renderer->isLocked()) { + $this->add( + Link::create( + $this->translate('Unlock'), + $url->with('unlocked', true), + null, + array( + 'class' => 'icon-lock-open', + 'title' => $this->translate('Unlock this process'), + ) + ) + ); + } elseif (! $hasChanges) { + $this->add( + Link::create( + $this->translate('Lock'), + $url->without('unlocked')->without('action'), + null, + array( + 'class' => 'icon-lock', + 'title' => $this->translate('Lock this process'), + ) + ) + ); + } + + if ($hasChanges || (! $renderer->isLocked()) && $meta->canModify()) { + $this->add( + Link::create( + $this->translate('Config'), + 'businessprocess/process/config', + $this->currentProcessParams($url), + array( + 'class' => 'icon-wrench', + 'title' => $this->translate('Modify this process'), + 'data-base-target' => '_next', + ) + ) + ); + } + } + + protected function currentProcessParams($url) + { + $urlParams = $url->getParams(); + $params = array(); + foreach (array('config', 'node') as $name) { + if ($value = $urlParams->get($name)) { + $params[$name] = $value; + } + } + + return $params; + } +} From 047e9dd70d27f877cf06011c66844f7c98bc51a1 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:16:00 +0100 Subject: [PATCH 203/256] LegacyStorage: throw error on incomplete imports --- .../Businessprocess/Storage/LegacyStorage.php | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/library/Businessprocess/Storage/LegacyStorage.php b/library/Businessprocess/Storage/LegacyStorage.php index 8d009cc..b914de4 100644 --- a/library/Businessprocess/Storage/LegacyStorage.php +++ b/library/Businessprocess/Storage/LegacyStorage.php @@ -463,10 +463,19 @@ class LegacyStorage extends Storage $bp->createService($host, $service); } } - if ($val[0] === '@' && strpos($val, ':') !== false) { - list($config, $nodeName) = preg_split('~:\s*~', substr($val, 1), 2); - $bp->createImportedNode($config, $nodeName); - $val = $nodeName; + if ($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); + } + $val = $node->getName(); } $childNames[] = $val; From e1ec6c7b6ab37b08d500f4c219b3bb506c6ccbf7 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:17:17 +0100 Subject: [PATCH 204/256] ImportedNode: make imports more robust --- library/Businessprocess/ImportedNode.php | 72 ++++++++++++++++++++---- 1 file changed, 62 insertions(+), 10 deletions(-) diff --git a/library/Businessprocess/ImportedNode.php b/library/Businessprocess/ImportedNode.php index 6134394..c41c272 100644 --- a/library/Businessprocess/ImportedNode.php +++ b/library/Businessprocess/ImportedNode.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Businessprocess; use Icinga\Application\Config; use Icinga\Module\Businessprocess\Html\Link; use Icinga\Module\Businessprocess\Storage\LegacyStorage; +use Icinga\Module\Businessprocess\Web\Url; use Exception; class ImportedNode extends Node @@ -12,19 +13,32 @@ class ImportedNode extends Node /** @var string */ protected $configName; + /** @var string */ + protected $nodeName; + /** @var BpNode */ private $node; protected $className = 'subtree'; + /** @var BusinessProcess */ + protected $config; + /** * @inheritdoc */ public function __construct(BusinessProcess $bp, $object) { - $this->name = $object->name; + $this->bp = $bp; $this->configName = $object->configName; - $this->bp = $bp; + $this->name = '@' . $object->configName; + if (property_exists($object, 'node')) { + $this->nodeName = $object->node; + $this->name .= ':' . $object->node; + } else { + $this->useAllRootNodes(); + } + if (isset($object->state)) { $this->setState($object->state); } else { @@ -32,6 +46,11 @@ class ImportedNode extends Node } } + public function hasNode() + { + return $this->nodeName !== null; + } + /** * @return string */ @@ -46,6 +65,11 @@ class ImportedNode extends Node public function getState() { if ($this->state === null) { + try { + $this->importedConfig()->retrieveStatesFromBackend(); + } catch (Exception $e) { + } + $this->state = $this->importedNode()->getState(); } return $this->state; @@ -84,8 +108,10 @@ class ImportedNode extends Node public function isInDowntime() { if ($this->downtime === null) { + $this->getState(); $this->downtime = $this->importedNode()->isInDowntime(); } + return $this->downtime; } @@ -95,8 +121,10 @@ class ImportedNode extends Node public function isAcknowledged() { if ($this->ack === null) { + $this->getState(); $this->downtime = $this->importedNode()->isAcknowledged(); } + return $this->ack; } @@ -118,6 +146,32 @@ class ImportedNode extends Node 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); + } + } + + protected function importedConfig() + { + if ($this->config === null) { $import = $this->storage()->loadProcess($this->configName); if ($this->bp->usesSoftStates()) { $import->useSoftStates(); @@ -125,13 +179,10 @@ class ImportedNode extends Node $import->useHardStates(); } - $import->retrieveStatesFromBackend(); - - return $import->getNode($this->name); - } catch (Exception $e) { - - return $this->createFailedNode($e); + $this->config = $import; } + + return $this->config; } /** @@ -151,8 +202,9 @@ class ImportedNode extends Node */ protected function createFailedNode(Exception $e) { - $node = new BpNode($this->bp, (object) array( - 'name' => $this->name, + $this->bp->addError($e->getMessage()); + $node = new BpNode($this->importedConfig(), (object) array( + 'name' => $this->getName(), 'operator' => '&', 'child_names' => array() )); From 7b0b550f4f4bf9a6c61ea78e54963d8fade86402 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:17:46 +0100 Subject: [PATCH 205/256] AddNodeForm: waste less space for multiselects --- application/forms/AddNodeForm.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/forms/AddNodeForm.php b/application/forms/AddNodeForm.php index 726a38a..96606a0 100644 --- a/application/forms/AddNodeForm.php +++ b/application/forms/AddNodeForm.php @@ -165,7 +165,7 @@ class AddNodeForm extends QuickForm $this->addElement('multiselect', 'children', array( 'label' => $this->translate('Hosts'), 'required' => true, - 'size' => 14, + 'size' => 8, 'style' => 'width: 25em', 'multiOptions' => $this->enumHostList(), 'description' => $this->translate( @@ -200,7 +200,7 @@ class AddNodeForm extends QuickForm $this->addElement('multiselect', 'children', array( 'label' => $this->translate('Services'), 'required' => true, - 'size' => 14, + 'size' => 8, 'style' => 'width: 25em', 'multiOptions' => $this->enumServiceList($host), 'description' => $this->translate( @@ -214,7 +214,7 @@ class AddNodeForm extends QuickForm $this->addElement('multiselect', 'children', array( 'label' => $this->translate('Process nodes'), 'required' => true, - 'size' => 14, + 'size' => 8, 'style' => 'width: 25em', 'multiOptions' => $this->enumProcesses(), 'description' => $this->translate( From a45bb7c6cb943e5022b2936057ae922a2201600e Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:18:37 +0100 Subject: [PATCH 206/256] BusinessProcess: provide a listRootNodes() method --- library/Businessprocess/BusinessProcess.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index 99f7b5e..c411dec 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -4,6 +4,7 @@ namespace Icinga\Module\Businessprocess; use Icinga\Application\Benchmark; use Icinga\Exception\IcingaException; +use Icinga\Exception\NotFoundError; use Icinga\Exception\ProgrammingError; use Icinga\Module\Businessprocess\Exception\NestingError; use Icinga\Module\Businessprocess\Modification\ProcessChanges; @@ -431,6 +432,13 @@ class BusinessProcess return $this->root_nodes; } + public function listRootNodes() + { + $names = array_keys($this->root_nodes); + sort($names); + return $names; + } + public function getNodes() { return $this->nodes; From 65524e7a8bf0da0d69c5a4365bc170e130c75ac3 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:20:01 +0100 Subject: [PATCH 207/256] NodeTile: tweak some links --- .../Renderer/TileRenderer/NodeTile.php | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/library/Businessprocess/Renderer/TileRenderer/NodeTile.php b/library/Businessprocess/Renderer/TileRenderer/NodeTile.php index ba466ae..95bc651 100644 --- a/library/Businessprocess/Renderer/TileRenderer/NodeTile.php +++ b/library/Businessprocess/Renderer/TileRenderer/NodeTile.php @@ -110,7 +110,7 @@ class NodeTile extends BaseElement } } - protected function makeBpUrl(BpNode $node) + protected function buildBaseNodeUrl(Node $node) { $path = $this->path; $name = $this->name; // TODO: ?? @@ -137,6 +137,11 @@ class NodeTile extends BaseElement return $url; } + protected function makeBpUrl(BpNode $node) + { + return $this->buildBaseNodeUrl($node); + } + protected function makeMonitoredNodeUrl(MonitoredNode $node) { $path = $this->path; @@ -243,22 +248,30 @@ class NodeTile extends BaseElement if ($node instanceof BpNode) { $this->actions()->add(Link::create( Icon::create('edit'), - $renderer->getUrl()->with('action', 'edit'), + $renderer->getUrl()->with('action', 'edit')->with('editnode', $node->getName()), null, array('title' => $this->translate('Modify this business process node')) )); - } else { + } elseif ($node instanceof MonitoredNode) { $this->actions()->add(Link::create( Icon::create('magic'), - $renderer->getUrl()->with('action', 'simulation')->with('simulationnode', $this->name), + $renderer->getUrl()->with('action', 'simulation') + ->with('simulationnode', $this->name), null, - array('title' => $this->translate('Show the business impact of this node by simulating a specific state')) + array('title' => $this->translate( + 'Show the business impact of this node by simulating a specific state' + )) )); } + $params = array( + 'action' => 'delete', + 'deletenode' => $node->getName(), + ); + $this->actions()->add(Link::create( Icon::create('cancel'), - $renderer->getUrl()->with('action', 'delete'), + $renderer->getUrl()->with($params), null, array('title' => $this->translate('Delete this node')) )); From 8bd1e10f95f6f7e031fbb2bc416d3651060e929a Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:23:05 +0100 Subject: [PATCH 208/256] DeleteNode: quite some rework, provide more... ...possibilities, remove "not implemented" exception --- application/forms/DeleteNodeForm.php | 69 +++++++++++++++---- library/Businessprocess/BpNode.php | 18 +++++ library/Businessprocess/BusinessProcess.php | 51 +++++++++++++- .../Modification/NodeRemoveAction.php | 32 +++++---- 4 files changed, 141 insertions(+), 29 deletions(-) diff --git a/application/forms/DeleteNodeForm.php b/application/forms/DeleteNodeForm.php index 2cb9c08..5d4c5c6 100644 --- a/application/forms/DeleteNodeForm.php +++ b/application/forms/DeleteNodeForm.php @@ -5,6 +5,7 @@ namespace Icinga\Module\Businessprocess\Forms; use Icinga\Module\Businessprocess\BpNode; use Icinga\Module\Businessprocess\BusinessProcess; use Icinga\Module\Businessprocess\Modification\ProcessChanges; +use Icinga\Module\Businessprocess\Node; use Icinga\Module\Businessprocess\Web\Form\QuickForm; use Icinga\Module\Monitoring\Backend\MonitoringBackend; use Icinga\Web\Session\SessionNamespace; @@ -17,32 +18,62 @@ class DeleteNodeForm extends QuickForm /** @var BusinessProcess */ protected $bp; - /** @var BpNode */ + /** @var Node */ protected $node; - /** @var array */ - protected $path; + /** @var BpNode */ + protected $parentNode; /** @var SessionNamespace */ protected $session; public function setup() { + $node = $this->node; + $view = $this->getView(); $this->addHtml( - '

' . $this->getView()->escape( - sprintf($this->translate('Delete %s'), $this->node->getAlias()) + '

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

' ); + + $biLink = $view->qlink( + $node->getAlias(), + 'director/node/impact', + array('node' => $node->getName()), + array('data-base-target' => '_next') + ); + $this->addHtml( + '

' . sprintf( + $view->escape( + $this->translate('Unsure? Show business impact of "%s"') + ), + $biLink + ) . '

' + ); + + if ($this->parentNode) { + $yesMsg = sprintf( + $this->translate('Delete from %s'), + $this->parentNode->getAlias() + ); + } else { + $yesMsg = sprintf( + $this->translate('Delete root node "%s"'), + $this->node->getAlias() + ); + } + $this->addElement('select', 'confirm', array( 'label' => $this->translate('Are you sure?'), 'required' => true, 'description' => $this->translate( 'Do you really want to delete this node?' ), - 'multiOptions' => $this->optionalEnum( - array( + 'multiOptions' => $this->optionalEnum(array( 'no' => $this->translate('No'), - 'yes' => $this->translate('Yes'), + 'yes' => $yesMsg, + 'all' => sprintf($this->translate('Delete all occurrences of %s'), $node->getAlias()), )) )); } @@ -69,22 +100,22 @@ class DeleteNodeForm extends QuickForm } /** - * @param BpNode $node + * @param Node $node * @return $this */ - public function setNode(BpNode $node) + public function setNode(Node $node) { $this->node = $node; return $this; } /** - * @param array $path + * @param BpNode|null $node * @return $this */ - public function setPath(array $path) + public function setParentNode(BpNode $node = null) { - $this->path = $path; + $this->parentNode = $node; return $this; } @@ -101,7 +132,17 @@ class DeleteNodeForm extends QuickForm public function onSuccess() { $changes = ProcessChanges::construct($this->bp, $this->session); - $changes->deleteNode($this->node, $this->path); + + switch ($this->getValue('confirm')) { + case 'yes': + $changes->deleteNode($this->node, $this->path); + break; + case 'all': + $changes->deleteNode($this->node); + break; + case 'no': + $this->setSuccessMessage($this->translate('Well, maybe next time')); + } // Trigger session desctruction to make sure it get's stored. // TODO: figure out why this is necessary, might be an unclean shutdown on redirect unset($changes); diff --git a/library/Businessprocess/BpNode.php b/library/Businessprocess/BpNode.php index 7c8b9c7..1a8d91e 100644 --- a/library/Businessprocess/BpNode.php +++ b/library/Businessprocess/BpNode.php @@ -132,6 +132,24 @@ class BpNode extends Node return $problems; } + public function hasChild($name) + { + return in_array($name, $this->childNames); + } + + public function removeChild($name) + { + if (($key = array_search($name, $this->childNames)) !== false) { + unset($this->childNames[$key]); + + if (! empty($this->children)) { + unset($this->children[$name]); + } + } + + return $this; + } + public function getProblemTree() { $tree = array(); diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index c411dec..30687bd 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -498,6 +498,47 @@ class BusinessProcess return $node; } + public function hasNodeByPath($nodeName, $path = array()) + { + if (! $this->hasNode($nodeName)) { + return false; + } + + $node = $this->getNode($nodeName); + $parents = $node->getParents(); + foreach ($parents as $parent) { + + } + while (! empty($path)) { + + } + + return empty($path); + } + + public function getNodeByPath($nodeName, $path = array()) + { + if (! $this->hasNode($nodeName)) { + throw new NotFoundError( + 'Node %s not found at %s', + $nodeName, + implode(' -> ', $path) + ); + } + } + + public function getPathsToNode($node) + { + $paths = array(); + foreach ($node->getParents() as $parent) { + foreach ($parent->getPathsToNode() as $path) { + $paths[] = $path; + } + } + + return $paths; + } + /** * @param $name * @return Node @@ -577,7 +618,15 @@ class BusinessProcess public function removeNode($name) { - throw new ProgrammingError('Not implemented yet'); + unset($this->nodes[$name]); + if (array_key_exists($name, $this->root_nodes)) { + unset($this->root_nodes[$name]); + } + foreach ($this->getBpNodes() as $node) { + if ($node->hasChild($name)) { + $node->removeChild($name); + } + } } /** diff --git a/library/Businessprocess/Modification/NodeRemoveAction.php b/library/Businessprocess/Modification/NodeRemoveAction.php index 62a48bf..a8a27b1 100644 --- a/library/Businessprocess/Modification/NodeRemoveAction.php +++ b/library/Businessprocess/Modification/NodeRemoveAction.php @@ -13,26 +13,26 @@ use Icinga\Module\Businessprocess\BusinessProcess; */ class NodeRemoveAction extends NodeAction { - protected $preserveProperties = array('path'); + protected $preserveProperties = array('parentName'); - protected $path; + protected $parentName; /** - * @param array $path + * @param $parentName * @return $this */ - public function setPath(array $path) + public function setParentName($parentName = null) { - $this->path = $path; + $this->parentName = $parentName; return $this; } /** * @return mixed */ - public function getPath() + public function getParentName() { - return $this->path; + return $this->parentName; } /** @@ -40,11 +40,11 @@ class NodeRemoveAction extends NodeAction */ public function appliesTo(BusinessProcess $bp) { - $path = $this->getPath(); - if ($path === null) { - return $bp->hasNodeByPath($this->getNodeName(), $this->getPath()); - } else { + $parent = $this->getParentName(); + if ($parent === null) { return $bp->hasNode($this->getNodeName()); + } else { + return $bp->hasNode($this->getNodeName()) && $bp->hasNode($this->getParentName()) ; } } @@ -53,11 +53,15 @@ class NodeRemoveAction extends NodeAction */ public function applyTo(BusinessProcess $bp) { - $path = $this->getPath(); - if ($path === null) { + $parent = $this->getParentName(); + if ($parent === null) { $bp->removeNode($this->getNodeName()); } else { - $bp->removeNodeByPath($this->getNodeName(), $this->getPath()); + $node = $bp->getNode($this->getNodeName()); + $node->removeParent($parent); + if (! $node->hasParents()) { + $bp->removeNode($this->getNodeName()); + } } } } From 9c22289bf3c735635f98e1ad685bdf4f6afc9d31 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:27:58 +0100 Subject: [PATCH 209/256] BpNode: tweak hasInfoUrl() --- library/Businessprocess/BpNode.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Businessprocess/BpNode.php b/library/Businessprocess/BpNode.php index 1a8d91e..7776f34 100644 --- a/library/Businessprocess/BpNode.php +++ b/library/Businessprocess/BpNode.php @@ -242,7 +242,7 @@ class BpNode extends Node public function hasInfoUrl() { - return $this->url !== null; + return ! empty($this->url); } public function getInfoUrl() From 5b9748c2081b18ee843078fdc0e6acf2162a953d Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:28:58 +0100 Subject: [PATCH 210/256] Controller: fix class usage, add new helper --- library/Businessprocess/Web/Controller.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/library/Businessprocess/Web/Controller.php b/library/Businessprocess/Web/Controller.php index b7ba8b3..4dd2564 100644 --- a/library/Businessprocess/Web/Controller.php +++ b/library/Businessprocess/Web/Controller.php @@ -75,7 +75,7 @@ class Controller extends ModuleController protected function actions() { if ($this->view->actions === null) { - $this->view->actions = ActionBar::create(); + $this->view->actions = new ActionBar(); } return $this->view->actions; @@ -195,6 +195,13 @@ class Controller extends ModuleController return $bp; } + protected function doNotRender() + { + $this->_helper->layout()->disableLayout(); + $this->_helper->viewRenderer->setNoRender(true); + return $this; + } + protected function loadBpConfig() { $name = $this->params->get('config'); From 8770ff05cec21176f2c38bb2a3afa0a3c717f2f3 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:29:19 +0100 Subject: [PATCH 211/256] ConfigDiff: just a newline at end of file --- library/Businessprocess/Storage/ConfigDiff.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Businessprocess/Storage/ConfigDiff.php b/library/Businessprocess/Storage/ConfigDiff.php index 35b8280..fc3b1d9 100644 --- a/library/Businessprocess/Storage/ConfigDiff.php +++ b/library/Businessprocess/Storage/ConfigDiff.php @@ -88,4 +88,4 @@ class ConfigDiff implements Renderable $diff = new static($a, $b); return $diff; } -} \ No newline at end of file +} From 549ec814ab5417e9caa9523f250cd45c4db2bd78 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:30:03 +0100 Subject: [PATCH 212/256] TileRenderer: show unbound nodes only at root level --- library/Businessprocess/Renderer/TileRenderer.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/library/Businessprocess/Renderer/TileRenderer.php b/library/Businessprocess/Renderer/TileRenderer.php index c1c3725..c544d79 100644 --- a/library/Businessprocess/Renderer/TileRenderer.php +++ b/library/Businessprocess/Renderer/TileRenderer.php @@ -36,10 +36,12 @@ class TileRenderer extends Renderer $this->add(new NodeTile($this, $name, $node, $path)); } - $unbound = $this->createUnboundParent($bp); - if ($unbound->hasChildren()) { - $name = $unbound->getAlias(); - $this->add($this->add(new NodeTile($this, $name, $unbound))); + if ($this->wantsRootNodes()) { + $unbound = $this->createUnboundParent($bp); + if ($unbound->hasChildren()) { + $name = $unbound->getAlias(); + $this->add(new NodeTile($this, $name, $unbound)); + } } if (! $this->isLocked()) { From eb866bf4b1459136899a605c082aaa28ade13f5c Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:30:27 +0100 Subject: [PATCH 213/256] Renderer: blacklist some more parameters --- library/Businessprocess/Renderer/Renderer.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/library/Businessprocess/Renderer/Renderer.php b/library/Businessprocess/Renderer/Renderer.php index 894b2af..655a108 100644 --- a/library/Businessprocess/Renderer/Renderer.php +++ b/library/Businessprocess/Renderer/Renderer.php @@ -76,6 +76,14 @@ abstract class Renderer extends Html return $this->parent !== null; } + /** + * @return BpNode + */ + public function getParentNode() + { + return $this->parent; + } + /** * @return BpNode[] */ @@ -196,7 +204,12 @@ abstract class Renderer extends Html */ public function setUrl(Url $url) { - $this->url = $url->without(array('simulationnode', 'deletenode')); + $this->url = $url->without(array( + 'deletenode', + 'deleteparent', + 'editnode', + 'simulationnode' + )); $this->setBaseUrl($url); return $this; } From 2dc08be767c48dbf48116014ebfa3eb5ca44dd15 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:30:48 +0100 Subject: [PATCH 214/256] ProcessChanges: make path optional for deleteNode --- library/Businessprocess/Modification/ProcessChanges.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/library/Businessprocess/Modification/ProcessChanges.php b/library/Businessprocess/Modification/ProcessChanges.php index 78061ae..0720460 100644 --- a/library/Businessprocess/Modification/ProcessChanges.php +++ b/library/Businessprocess/Modification/ProcessChanges.php @@ -96,13 +96,17 @@ class ProcessChanges /** * @param Node $node + * @param array $path * * @return $this */ - public function deleteNode(Node $node, array $path) + public function deleteNode(Node $node, array $path = null) { $action = new NodeRemoveAction($node); - $action->setPath($path); + if ($path !== null) { + $action->setPath($path); + } + return $this->push($action); } From d4d46ac0c10fbf67867842a596b86a07d8090f39 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:31:12 +0100 Subject: [PATCH 215/256] configuration: use names not keys for menu --- configuration.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configuration.php b/configuration.php index 775146f..3cd5f94 100644 --- a/configuration.php +++ b/configuration.php @@ -2,6 +2,7 @@ use Icinga\Module\Businessprocess\Storage\LegacyStorage; +/** @var \Icinga\Application\Modules\Module $this */ $section = $this->menuSection(N_('Business Processes'), array( 'url' => 'businessprocess', 'icon' => 'sitemap', @@ -14,7 +15,7 @@ try { ); $prio = 0; - foreach ($storage->listProcesses() as $name) { + foreach ($storage->listProcessNames() as $name) { $prio++; $meta = $storage->loadMetadata($name); From 7e11ead8a8b0293fdfa68aef43bee2dd5bc98cb7 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:32:02 +0100 Subject: [PATCH 216/256] ProcessController: unify view scripts for config... ...and diff --- application/controllers/ProcessController.php | 74 +++++++++++++++---- .../views/scripts/process/config.phtml | 18 +---- .../views/scripts/process/source.phtml | 16 +--- 3 files changed, 65 insertions(+), 43 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 6816d77..991b760 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -296,27 +296,71 @@ class ProcessController extends Controller */ public function sourceAction() { - $this->tabsForConfig()->activate('source'); $bp = $this->loadModifiedBpConfig(); + $this->view->showDiff = $showDiff = (bool) $this->params->get('showDiff', false); $this->view->source = $this->storage()->render($bp); - $this->view->showDiff = (bool) $this->params->get('showDiff', false); - if ($this->view->showDiff) { $this->view->diff = ConfigDiff::create( $this->storage()->getSource($this->view->configName), $this->view->source ); - $this->view->title = sprintf( + $title = sprintf( $this->translate('%s: Source Code Differences'), $bp->getTitle() ); } else { - $this->view->title = sprintf( + $title = sprintf( $this->translate('%s: Source Code'), $bp->getTitle() ); } + + $actionBar = new ActionBar(); + $this->setTitle($title); + $this->controls() + ->add($this->tabsForConfig()->activate('source')) + ->add(HtmlTag::h1($title)) + ->add($actionBar); + + if ($showDiff) { + $actionBar->add( + Link::create( + $this->translate('Source'), + $this->url()->without('showDiff'), + null, + array( + 'class' => 'icon-doc-text', + 'title' => $this->translate('Show source code'), + ) + ) + ); + } else { + $actionBar->add( + Link::create( + $this->translate('Diff'), + $this->url()->with('showDiff', true), + null, + array( + 'class' => 'icon-flapping', + 'title' => $this->translate('Highlight changes'), + ) + ) + ); + } + + $actionBar->add( + Link::create( + $this->translate('Download'), + 'businessprocess/process/download', + array('config' => $bp->getName()), + array( + 'target' => '_blank', + 'class' => 'icon-download', + 'title' => $this->translate('Download process configuration') + ) + ) + ); } /** @@ -345,24 +389,28 @@ class ProcessController extends Controller */ public function configAction() { - $this->tabsForConfig()->activate('config'); $bp = $this->loadModifiedBpConfig(); - $this->setTitle( + $title = sprintf( $this->translate('%s: Configuration'), $bp->getTitle() ); + $this->setTitle($title); + $this->controls() + ->add($this->tabsForConfig()->activate('config')) + ->add(HtmlTag::h1($title)); $url = Url::fromPath( 'businessprocess/process/show?unlocked', array('config' => $bp->getName()) ); - - $this->view->form = $this->loadForm('bpConfig') - ->setProcessConfig($bp) - ->setStorage($this->storage()) - ->setSuccessUrl($url) - ->handleRequest(); + $this->content()->add( + $this->loadForm('bpConfig') + ->setProcessConfig($bp) + ->setStorage($this->storage()) + ->setSuccessUrl($url) + ->handleRequest() + ); } /** diff --git a/application/views/scripts/process/config.phtml b/application/views/scripts/process/config.phtml index c788759..3e2cc59 100644 --- a/application/views/scripts/process/config.phtml +++ b/application/views/scripts/process/config.phtml @@ -1,16 +1,2 @@ -
-tabs ?> -

escape($this->title) ?> - icon('doc-text') ?> -

-
- -
-form ?> -
-deleteForm ?> -
-
- +controls->render() ?> +content->render() ?> diff --git a/application/views/scripts/process/source.phtml b/application/views/scripts/process/source.phtml index d4f7323..d5ba6bb 100644 --- a/application/views/scripts/process/source.phtml +++ b/application/views/scripts/process/source.phtml @@ -1,21 +1,9 @@ -
-tabs ?> -

escape($this->title) ?> - icon('download') ?> -showDiff): ?> - icon('doc-text') ?> - - icon('flapping') ?> - -

-
+controls->render() ?>
showDiff): ?>
-diff->renderHtml() ?> +diff->render() ?>
From 32f50c85cf682783c074feb6649f0109f9fec8a2 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:35:09 +0100 Subject: [PATCH 217/256] Node: add helpers related to deletion --- library/Businessprocess/Node.php | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/library/Businessprocess/Node.php b/library/Businessprocess/Node.php index 51f37cb..0d3794d 100644 --- a/library/Businessprocess/Node.php +++ b/library/Businessprocess/Node.php @@ -288,11 +288,38 @@ abstract class Node return count($this->parents) > 0; } + public function hasParentName($name) + { + foreach ($this->getParents() as $parent) { + if ($parent->getName() === $name) { + return true; + } + } + + return false; + } + + public function removeParent($name) + { + $this->parents = array_filter( + $this->parents, + function (BpNode $parent) use ($name) { + return $parent->getName() !== $name; + } + ); + } + public function getParents() { return $this->parents; } + public function getPaths() + { + // TODO! -> for delete etc + return $this->parents; + } + protected function stateToSortState($state) { if (array_key_exists($state, $this->stateToSortStateMap)) { From 37ed2cd15d18c8ecbd6dc4a67ea8c4640b878790 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:35:43 +0100 Subject: [PATCH 218/256] ProcessForm: remove child-related code --- application/forms/ProcessForm.php | 194 ++++-------------------------- 1 file changed, 24 insertions(+), 170 deletions(-) diff --git a/application/forms/ProcessForm.php b/application/forms/ProcessForm.php index b289ada..d34ecfe 100644 --- a/application/forms/ProcessForm.php +++ b/application/forms/ProcessForm.php @@ -30,13 +30,21 @@ class ProcessForm extends QuickForm public function setup() { - $this->addElement('text', 'name', array( - 'label' => $this->translate('Name'), - 'required' => true, - 'description' => $this->translate( - 'This is the unique identifier of this process' - ), - )); + if ($this->node === null) { + $this->addElement('text', 'name', array( + 'label' => $this->translate('Name'), + 'required' => true, + 'description' => $this->translate( + 'This is the unique identifier of this process' + ), + )); + } else { + $this->addHtml( + '

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

' + ); + } $this->addElement('text', 'alias', array( 'label' => $this->translate('Title'), @@ -84,90 +92,18 @@ class ProcessForm extends QuickForm ) )); - $this->addElement('select', 'object_type', array( - 'label' => $this->translate('Add children'), - 'required' => $this->node === null || ! $this->node->hasChildren(), - 'ignore' => true, - 'class' => 'autosubmit', - 'multiOptions' => $this->optionalEnum( - array( - 'hosts' => $this->translate('Host'), - 'service' => $this->translate('Service'), - 'process' => $this->translate('Another process'), - 'include' => $this->translate('External process'), - ) - ) - )); - - switch ($this->getSentValue('object_type')) { - case 'hosts': - $this->addHostsElement(); - break; - case 'service': - $this->addHostElement(); - if ($host = $this->getSentValue('host')) { - $this->addServicesElement($host); - } - break; - case 'process': - $this->addProcessesElement(); - break; + if ($node = $this->node) { + 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()); + } } } - protected function addHostsElement() - { - $this->addElement('multiselect', 'children', array( - 'label' => $this->translate('Hosts'), - 'required' => true, - 'size' => 14, - 'style' => 'width: 25em', - 'multiOptions' => $this->enumHostList(), - 'description' => $this->translate( - 'Hosts that should be part of this business process node' - ) - )); - } - - protected function addHostElement() - { - $this->addElement('select', 'host', array( - 'label' => $this->translate('Host'), - 'required' => true, - 'ignore' => true, - 'class' => 'autosubmit', - 'multiOptions' => $this->optionalEnum($this->enumHostList()), - )); - } - - protected function addServicesElement($host) - { - $this->addElement('multiselect', 'children', array( - 'label' => $this->translate('Services'), - 'required' => true, - 'size' => 14, - 'style' => 'width: 25em', - 'multiOptions' => $this->enumServiceList($host), - 'description' => $this->translate( - 'Services that should be part of this business process node' - ) - )); - } - - protected function addProcessesElement() - { - $this->addElement('multiselect', 'children', array( - 'label' => $this->translate('Process nodes'), - 'required' => true, - 'size' => 14, - '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 @@ -209,88 +145,11 @@ class ProcessForm extends QuickForm return $this; } - 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 - return array_combine((array) $names, (array) $names); - } - - 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 enumProcesses() - { - $list = array(); - - foreach ($this->bp->getNodes() as $node) { - if ($node instanceof BpNode) { - // TODO: Blacklist parents - $list[(string) $node] = (string) $node; // display name? - } - } - - natsort($list); - return $list; - } - - 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; - } - public function onSuccess() { $changes = ProcessChanges::construct($this->bp, $this->session); $modifications = array(); - $children = $this->getValue('children'); $alias = $this->getValue('alias'); $operator = $this->getValue('operator'); $display = $this->getValue('display'); @@ -301,7 +160,6 @@ class ProcessForm extends QuickForm if (empty($alias)) { $alias = null; } - ksort($children); // TODO: rename if ($node = $this->node) { @@ -312,9 +170,6 @@ class ProcessForm extends QuickForm if ($operator !== $node->getOperator()) { $modifications['operator'] = $operator; } - if ($children !== $node->getChildNames()) { - $modifications['childNames'] = $children; - } if ($url !== $node->getInfoUrl()) { $modifications['infoUrl'] = $url; } @@ -325,7 +180,6 @@ class ProcessForm extends QuickForm $modifications = array( 'display' => $display, 'operator' => $operator, - 'childNames' => $children, 'infoUrl' => $url, 'alias' => $alias, ); From 141c043b041e666c72d9edd72135c523fb4451fd Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:36:43 +0100 Subject: [PATCH 219/256] BpConfigForm: render to main after removal --- application/forms/BpConfigForm.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/application/forms/BpConfigForm.php b/application/forms/BpConfigForm.php index bac2c19..1f9e884 100644 --- a/application/forms/BpConfigForm.php +++ b/application/forms/BpConfigForm.php @@ -109,9 +109,9 @@ class BpConfigForm extends QuickForm ); $label = $this->translate('Delete'); - $el = $this->createElement('submit', $label) - ->setLabel($label) - ->setDecorators(array('ViewHelper')); + $el = $this->createElement('submit', $label, array( + 'data-base-target' => '_main' + ))->setLabel($label)->setDecorators(array('ViewHelper')); $this->deleteButtonName = $el->getName(); $this->addElement($el); } From 5f26440a006e03b413c1d3f71ba080e3a80fb388 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:37:39 +0100 Subject: [PATCH 220/256] NodeAddChildrenAction: do nothing for missing node --- .../Businessprocess/Modification/NodeAddChildrenAction.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/Businessprocess/Modification/NodeAddChildrenAction.php b/library/Businessprocess/Modification/NodeAddChildrenAction.php index 77b7080..fdc70a4 100644 --- a/library/Businessprocess/Modification/NodeAddChildrenAction.php +++ b/library/Businessprocess/Modification/NodeAddChildrenAction.php @@ -31,6 +31,11 @@ class NodeAddChildrenAction extends NodeAction public function applyTo(BusinessProcess $bp) { /** @var BpNode $node */ + if (! $this->hasNode()) { + // TODO: We end up here when defining "top nodes", but that would probably + // be a different action + return $this; + } $node = $bp->getNode($this->getNodeName()); $existing = $node->getChildNames(); foreach ($this->children as $name) { From 40de5a2b4dbb6122a06d13fc2eff8aa1db581ca7 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:38:17 +0100 Subject: [PATCH 221/256] ProcessController: provide edit action --- application/controllers/ProcessController.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 991b760..40d48e0 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -202,10 +202,16 @@ class ProcessController extends Controller } elseif ($action === 'delete') { $form =$this->loadForm('DeleteNode') ->setProcess($bp) - ->setNode($node) - ->setPath($this->params->getValues('path')) + ->setNode($bp->getNode($this->params->get('deletenode'))) + ->setParentNode($node) ->setSession($this->session()) ->handleRequest(); + } elseif ($action === 'edit') { + $form =$this->loadForm('Process') + ->setProcess($bp) + ->setNode($bp->getNode($this->params->get('editnode'))) + ->setSession($this->session()) + ->handleRequest(); } elseif ($action === 'simulation') { $form = $this->loadForm('simulation') ->setNode($bp->getNode($this->params->get('simulationnode'))) From 7d82bb05bd83300a94db66f13c0139ee87c43cd9 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:38:41 +0100 Subject: [PATCH 222/256] ProcessCommand: align usage of list methods --- application/clicommands/ProcessCommand.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/clicommands/ProcessCommand.php b/application/clicommands/ProcessCommand.php index e96eff7..4f398bd 100644 --- a/application/clicommands/ProcessCommand.php +++ b/application/clicommands/ProcessCommand.php @@ -49,7 +49,7 @@ class ProcessCommand extends Command public function listAction() { $noTitle = $this->params->shift('no-title'); - foreach ($this->storage->listProcesses() as $key => $title) { + foreach ($this->storage->listProcessNames() as $key => $title) { if ($noTitle) { echo $key . "\n"; } else { @@ -146,7 +146,7 @@ class ProcessCommand extends Command protected function getFirstProcessName() { - $list = $this->storage->listProcesses(); + $list = $this->storage->listProcessNames(); return key($list); } } From f16715814fab277f66b994590364807dc69b2ed1 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:39:36 +0100 Subject: [PATCH 223/256] BusinessProcess: provide createImportedNode helper --- library/Businessprocess/BusinessProcess.php | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index 30687bd..423500f 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -491,10 +491,20 @@ class BusinessProcess return $node; } - public function createImportedNode($config, $name) + public function createMissingBp($name) { - $node = new ImportedNode($this, (object) array('name' => $name, 'configName' => $config)); - $this->nodes[$name] = $node; + return $this->createBp($name)->setMissing(); + } + + public function createImportedNode($config, $name = null) + { + $params = (object) array('configName' => $config); + if ($name !== null) { + $params->node = $name; + } + + $node = new ImportedNode($this, $params); + $this->nodes[$node->getName()] = $node; return $node; } From e4043d94a24a78b4939e5136573f938632f24ff1 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 3 Jan 2017 11:43:22 +0100 Subject: [PATCH 224/256] ProcessController: improve download link --- application/controllers/ProcessController.php | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 40d48e0..a19d6e8 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -375,18 +375,17 @@ class ProcessController extends Controller public function downloadAction() { $bp = $this->loadModifiedBpConfig(); - - header( + $response = $this->getResponse(); + $response->setHeader( + 'Content-Disposition', sprintf( - 'Content-Disposition: attachment; filename="%s.conf";', + 'attachment; filename="%s.conf";', $bp->getName() ) ); - header('Content-Type: text/plain'); + $response->setHeader('Content-Type', 'text/plain'); echo $this->storage()->render($bp); - // Didn't have time to lookup how to correctly disable our renderers - // TODO: no exit :) $this->doNotRender(); } From 52e8eccca487cc7ad1a6144a20b42721449410c4 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 4 Jan 2017 18:50:30 +0100 Subject: [PATCH 225/256] run.php: remove superfluous newline --- run.php | 1 - 1 file changed, 1 deletion(-) diff --git a/run.php b/run.php index 6905547..0daf506 100644 --- a/run.php +++ b/run.php @@ -1,4 +1,3 @@ provideHook('director/shipConfigFiles'); - From 4ff382626c37f94dbc5c55ef3995df419d54cf7d Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 4 Jan 2017 18:41:21 +0100 Subject: [PATCH 226/256] gitlab-ci: run PHP CodeSniffer tests fixes #13893 --- .gitlab-ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c3c8035..e47d1b2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,14 @@ stages: +- Coding Standards - Unit-Tests +PSR2 CS Test: + stage: Coding Standards + tags: + - xenial + script: + - phpcs -p --standard=PSR2 --extensions=php --encoding=utf-8 -w -s library/Businessprocess/ application/ configuration.php run.php test + Xenial: stage: Unit-Tests tags: From 26d41a445d9bea2e6c7dbf3c7e4e3aa68ef4a588 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Sun, 8 Jan 2017 23:09:01 +0100 Subject: [PATCH 227/256] views/scripts: remove unified scripts --- application/controllers/ProcessController.php | 2 ++ application/views/scripts/{index/index.phtml => default.phtml} | 0 application/views/scripts/process/config.phtml | 2 -- application/views/scripts/process/create.phtml | 2 -- application/views/scripts/process/show.phtml | 2 -- application/views/scripts/process/upload.phtml | 2 -- library/Businessprocess/Web/Controller.php | 1 + 7 files changed, 3 insertions(+), 8 deletions(-) rename application/views/scripts/{index/index.phtml => default.phtml} (100%) delete mode 100644 application/views/scripts/process/config.phtml delete mode 100644 application/views/scripts/process/create.phtml delete mode 100644 application/views/scripts/process/show.phtml delete mode 100644 application/views/scripts/process/upload.phtml diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index a19d6e8..6c2c1f4 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -367,6 +367,8 @@ class ProcessController extends Controller ) ) ); + + $this->setViewScript('process/source'); } /** diff --git a/application/views/scripts/index/index.phtml b/application/views/scripts/default.phtml similarity index 100% rename from application/views/scripts/index/index.phtml rename to application/views/scripts/default.phtml diff --git a/application/views/scripts/process/config.phtml b/application/views/scripts/process/config.phtml deleted file mode 100644 index 3e2cc59..0000000 --- a/application/views/scripts/process/config.phtml +++ /dev/null @@ -1,2 +0,0 @@ -controls->render() ?> -content->render() ?> diff --git a/application/views/scripts/process/create.phtml b/application/views/scripts/process/create.phtml deleted file mode 100644 index 3e2cc59..0000000 --- a/application/views/scripts/process/create.phtml +++ /dev/null @@ -1,2 +0,0 @@ -controls->render() ?> -content->render() ?> diff --git a/application/views/scripts/process/show.phtml b/application/views/scripts/process/show.phtml deleted file mode 100644 index 3e2cc59..0000000 --- a/application/views/scripts/process/show.phtml +++ /dev/null @@ -1,2 +0,0 @@ -controls->render() ?> -content->render() ?> diff --git a/application/views/scripts/process/upload.phtml b/application/views/scripts/process/upload.phtml deleted file mode 100644 index 3e2cc59..0000000 --- a/application/views/scripts/process/upload.phtml +++ /dev/null @@ -1,2 +0,0 @@ -controls->render() ?> -content->render() ?> diff --git a/library/Businessprocess/Web/Controller.php b/library/Businessprocess/Web/Controller.php index 4dd2564..8ccba88 100644 --- a/library/Businessprocess/Web/Controller.php +++ b/library/Businessprocess/Web/Controller.php @@ -53,6 +53,7 @@ class Controller extends ModuleController = (bool) $this->_helper->layout()->showFullscreen; $this->view->compact = $this->params->get('view') === 'compact'; + $this->setViewScript('default'); } /** From 97d1e9969dddeeb4f833395b941510a08ece958c Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Sun, 8 Jan 2017 23:09:38 +0100 Subject: [PATCH 228/256] css: more padding for breadcrumb root --- public/css/module.less | 1 + 1 file changed, 1 insertion(+) diff --git a/public/css/module.less b/public/css/module.less index a4ce519..c288a78 100644 --- a/public/css/module.less +++ b/public/css/module.less @@ -635,6 +635,7 @@ div.bp .badges { .breadcrumb li:first-child a { padding-left: 1em; + padding-right: 0.5em; } .breadcrumb li:last-child a { cursor: default; From e4b773e827bdcd73154289810938a4a5250448f6 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 10 Jan 2017 15:54:03 +0100 Subject: [PATCH 229/256] BusinessProcess: add hasRootNode helper --- library/Businessprocess/BusinessProcess.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index 423500f..fd8d870 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -449,6 +449,11 @@ class BusinessProcess return array_key_exists($name, $this->nodes); } + public function hasRootNode($name) + { + return array_key_exists($name, $this->root_nodes); + } + public function createService($host, $service) { $node = new ServiceNode( From 6155bb5e2b7facbb173560f4203e92ff132582b0 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 10 Jan 2017 15:56:19 +0100 Subject: [PATCH 230/256] Controller: remove obsolete code --- library/Businessprocess/Web/Controller.php | 32 ---------------------- 1 file changed, 32 deletions(-) diff --git a/library/Businessprocess/Web/Controller.php b/library/Businessprocess/Web/Controller.php index 8ccba88..abb2ab9 100644 --- a/library/Businessprocess/Web/Controller.php +++ b/library/Businessprocess/Web/Controller.php @@ -21,9 +21,6 @@ class Controller extends ModuleController /** @var View */ public $view; - /** @deprecated, obsolete */ - protected $backend; - /** @var BusinessProcess */ protected $bp; @@ -256,33 +253,4 @@ class Controller extends ModuleController return $this->storage; } - - /** - * @deprecated - */ - protected function loadSlas() - { - $bpconf = $this->bpconf; - - if (! isset($bpconf->slahosts)) { - return array(); - } - - // TODO: This doesn't work right now - $sla_hosts = preg_split('~\s*,s*~', $bpconf->slahosts, -1, PREG_SPLIT_NO_EMPTY); - - if (isset($bpconf->sla_year)) { - $start = mktime(0, 0, 0, 1, 1, $bpconf->sla_year); - $end = mktime(23, 59, 59, 1, 0, $bpconf->sla_year + 1); - } else { - $start = mktime(0, 0, 0, 1, 1, (int) date('Y')); - $end = null; - // Bis zum Jahresende hochrechnen: - // $end = mktime(23, 59, 59, 1, 0, (int) date('Y') + 1); - } - - return $this->backend - ->module('BpAddon') - ->getBpSlaValues($sla_hosts, $start, $end); - } } From b3df39d1b076721f6ea5e6da84915ff1b4b1a18c Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 10 Jan 2017 16:49:53 +0100 Subject: [PATCH 231/256] NodeController: add business impact action Show all paths to a specific node to visualize it's business impact fixes #8573 --- application/controllers/NodeController.php | 50 +++++++++++++++++++ library/Businessprocess/Node.php | 20 +++++++- .../ProvidedHook/Monitoring/HostActions.php | 18 +++++++ .../Monitoring/ServiceActions.php | 25 ++++++++++ library/Businessprocess/Web/Controller.php | 14 +++++- run.php | 2 + 6 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 application/controllers/NodeController.php create mode 100644 library/Businessprocess/ProvidedHook/Monitoring/HostActions.php create mode 100644 library/Businessprocess/ProvidedHook/Monitoring/ServiceActions.php diff --git a/application/controllers/NodeController.php b/application/controllers/NodeController.php new file mode 100644 index 0000000..d2dc8b6 --- /dev/null +++ b/application/controllers/NodeController.php @@ -0,0 +1,50 @@ +content(); + $this->controls()->add( + $this->singleTab($this->translate('Node Impact')) + ); + $name = $this->params->get('name'); + $this->addTitle($this->translate('Business Impact (%s)'), $name); + + foreach ($this->storage()->listProcessNames() as $configName) { + $config = $this->storage()->loadProcess($configName); + + // TODO: Fix issues with children, they do not exist unless resolved :-/ + // This is a workaround: + foreach ($config->getRootNodes() as $node) { + $node->getState(); + } + + if (! $config->hasNode($name)) { + continue; + } + + foreach ($config->getNode($name)->getPaths() as $path) { + $node = array_pop($path); + $renderer = new TileRenderer($config, $config->getNode($node)); + $renderer->setUrl( + Url::fromPath( + 'businessprocess/process/show', + array('config' => $configName) + ) + )->setPath($path); + + $bc = Breadcrumb::create($renderer); + $bc->attributes()->set('data-base-target', '_next'); + $content->add($bc); + } + } + } +} diff --git a/library/Businessprocess/Node.php b/library/Businessprocess/Node.php index 0d3794d..7062c97 100644 --- a/library/Businessprocess/Node.php +++ b/library/Businessprocess/Node.php @@ -309,15 +309,33 @@ abstract class Node ); } + /** + * @return Node[] + */ public function getParents() { return $this->parents; } + /** + * @return array + */ public function getPaths() { + if ($this->bp->hasRootNode($this->getName())) { + return array(array($this->getName())); + } + + $paths = array(); + foreach ($this->parents as $parent) { + foreach ($parent->getPaths() as $path) { + // $path[] = $this->getName(); + $paths[] = $path; + } + + } // TODO! -> for delete etc - return $this->parents; + return $paths; } protected function stateToSortState($state) diff --git a/library/Businessprocess/ProvidedHook/Monitoring/HostActions.php b/library/Businessprocess/ProvidedHook/Monitoring/HostActions.php new file mode 100644 index 0000000..57ce8f5 --- /dev/null +++ b/library/Businessprocess/ProvidedHook/Monitoring/HostActions.php @@ -0,0 +1,18 @@ + 'businessprocess/node/impact?name=' + . rawurlencode($host->getName() . ';Hoststatus') + ); + } +} diff --git a/library/Businessprocess/ProvidedHook/Monitoring/ServiceActions.php b/library/Businessprocess/ProvidedHook/Monitoring/ServiceActions.php new file mode 100644 index 0000000..69a93ae --- /dev/null +++ b/library/Businessprocess/ProvidedHook/Monitoring/ServiceActions.php @@ -0,0 +1,25 @@ + sprintf( + 'businessprocess/node/impact?name=%s', + rawurlencode( + sprintf('%s;%s', $service->getHost()->getName(), $service->getName()) + ) + ) + ); + } +} diff --git a/library/Businessprocess/Web/Controller.php b/library/Businessprocess/Web/Controller.php index abb2ab9..7748d7f 100644 --- a/library/Businessprocess/Web/Controller.php +++ b/library/Businessprocess/Web/Controller.php @@ -4,6 +4,7 @@ namespace Icinga\Module\Businessprocess\Web; use Icinga\Application\Icinga; use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\Html\HtmlTag; use Icinga\Module\Businessprocess\Modification\ProcessChanges; use Icinga\Module\Businessprocess\Storage\LegacyStorage; use Icinga\Module\Businessprocess\Storage\Storage; @@ -42,8 +43,8 @@ class Controller extends ModuleController if (! $m->hasLoaded('monitoring') && $m->hasInstalled('monitoring')) { $m->loadModule('monitoring'); } - $this->view->errors = array(); - + $this->controls(); + $this->content(); $this->url(); $this->view->showFullscreen = $this->showFullscreen @@ -175,6 +176,15 @@ class Controller extends ModuleController return $this; } + protected function addTitle($title) + { + $args = func_get_args(); + array_shift($args); + $this->view->title = vsprintf($title, $args); + $this->controls()->add(HtmlTag::h1($this->view->title)); + return $this; + } + protected function loadModifiedBpConfig() { $bp = $this->loadBpConfig(); diff --git a/run.php b/run.php index 0daf506..2cde2ee 100644 --- a/run.php +++ b/run.php @@ -1,3 +1,5 @@ provideHook('monitoring/HostActions'); +$this->provideHook('monitoring/ServiceActions'); //$this->provideHook('director/shipConfigFiles'); From f0dbac0a9825d9204184ebc1309146e715bccfd3 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Mon, 9 Jan 2017 11:34:32 +0100 Subject: [PATCH 232/256] packaging: provide Debian build instructions --- .gitignore | 2 ++ .gitlab-ci.yml | 41 +++++++++++++++++++++++++++++++++- packaging/debian/README.1st | 18 +++++++++++++++ packaging/debian/changelog | 6 +++++ packaging/debian/compat | 1 + packaging/debian/control | 15 +++++++++++++ packaging/debian/docs | 1 + packaging/debian/install | 10 +++++++++ packaging/debian/rules | 26 +++++++++++++++++++++ packaging/debian/source/format | 1 + 10 files changed, 120 insertions(+), 1 deletion(-) create mode 100644 packaging/debian/README.1st create mode 100644 packaging/debian/changelog create mode 100644 packaging/debian/compat create mode 100644 packaging/debian/control create mode 100644 packaging/debian/docs create mode 100644 packaging/debian/install create mode 100755 packaging/debian/rules create mode 100644 packaging/debian/source/format diff --git a/.gitignore b/.gitignore index 9f11b75..1df28f0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ .idea/ +/debian +/build diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e47d1b2..51495ee 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,11 @@ stages: - Coding Standards - Unit-Tests +- Build Packages + +variables: + BASE_VERSION: "2.0.0" + VERSION_SUFFIX: "-b${CI_BUILD_ID}-${CI_BUILD_REF_SLUG}" PSR2 CS Test: stage: Coding Standards @@ -9,7 +14,7 @@ PSR2 CS Test: script: - phpcs -p --standard=PSR2 --extensions=php --encoding=utf-8 -w -s library/Businessprocess/ application/ configuration.php run.php test -Xenial: +Ubuntu Xenial: stage: Unit-Tests tags: - xenial @@ -40,3 +45,37 @@ CentOS 7: - businessprocess script: - phpunit --testdox || phpunit --verbose + +Xenial Packages: + stage: Build Packages + tags: + - xenial + - businessprocess + script: + - cp -a packaging/debian debian + - dch --no-conf -U -M --empty -v "${BASE_VERSION}${VERSION_SUFFIX}-${CI_BUILD_REF:0:7}" "Automated build triggered by ${GITLAB_USER_ID} <${GITLAB_USER_EMAIL}>" + - cp LICENSE debian/copyright + - dpkg-buildpackage -us -uc + - mkdir build + - mv ../icingaweb2-module-businessprocess*.deb build/ + artifacts: + expire_in: 1 week + paths: + - build/* + +Jessie Packages: + stage: Build Packages + tags: + - jessie + - businessprocess + script: + - cp -a packaging/debian debian + - dch --no-conf -U -M --empty -v "${BASE_VERSION}${VERSION_SUFFIX}-${CI_BUILD_REF:0:7}" "Automated build triggered by ${GITLAB_USER_ID} <${GITLAB_USER_EMAIL}>" + - cp LICENSE debian/copyright + - dpkg-buildpackage -us -uc + - mkdir build + - mv ../icingaweb2-module-businessprocess*.deb build/ + artifacts: + expire_in: 1 week + paths: + - build/* diff --git a/packaging/debian/README.1st b/packaging/debian/README.1st new file mode 100644 index 0000000..9e96470 --- /dev/null +++ b/packaging/debian/README.1st @@ -0,0 +1,18 @@ +Building Debian packages +======================== + +This is work in progress, please expect build instructions to change any time +soon. Currently, to build custom Debian or Ubuntu packages, please proceed as +follows: + +```sh +apt-get install --no-install-recommends \ + debhelper devscripts build-essential fakeroot libparse-debcontrol-perl +# Eventually adjust debian/changelog +cp -a packaging/debian debian +dpkg-buildpackage -us -uc +rm -rf debian +``` + +Please move to your parent directory (`cd ..`) to find your new Debian packages. + diff --git a/packaging/debian/changelog b/packaging/debian/changelog new file mode 100644 index 0000000..9051952 --- /dev/null +++ b/packaging/debian/changelog @@ -0,0 +1,6 @@ +icingaweb2-module-businessprocesss (2.0.0-rc1) stable; urgency=low + + * First packaged release + + -- Thomas Gelf Fri, 09 Jan 2016 10:37:31 +0100 + diff --git a/packaging/debian/compat b/packaging/debian/compat new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/packaging/debian/compat @@ -0,0 +1 @@ +9 diff --git a/packaging/debian/control b/packaging/debian/control new file mode 100644 index 0000000..2e3c046 --- /dev/null +++ b/packaging/debian/control @@ -0,0 +1,15 @@ +Source: icingaweb2-module-businessprocesss +Section: admin +Maintainer: Icinga Development Team +Priority: optional +Build-Depends: debhelper (>=9) +Standards-Version: 3.9.4 +Homepage: https://www.icinga.com + +Package: icingaweb2-module-businessprocess +Architecture: all +Depends: icingaweb2-common (>= 2.2.0), php-curl|php5-curl, ${misc:Depends} +Suggests: icingaweb2 +Description: A businessprocess viewer and modeler + Supports legacy BPaddon config files + diff --git a/packaging/debian/docs b/packaging/debian/docs new file mode 100644 index 0000000..3959d9d --- /dev/null +++ b/packaging/debian/docs @@ -0,0 +1 @@ +REAMDE.md diff --git a/packaging/debian/install b/packaging/debian/install new file mode 100644 index 0000000..8f7da70 --- /dev/null +++ b/packaging/debian/install @@ -0,0 +1,10 @@ +application usr/share/icingaweb2/modules/businessprocess +doc usr/share/icingaweb2/modules/businessprocess +library usr/share/icingaweb2/modules/businessprocess +public usr/share/icingaweb2/modules/businessprocess +test usr/share/icingaweb2/modules/businessprocess +run.php usr/share/icingaweb2/modules/businessprocess +configuration.php usr/share/icingaweb2/modules/businessprocess +module.info usr/share/icingaweb2/modules/businessprocess +phpunit.xml usr/share/icingaweb2/modules/businessprocess +README.md usr/share/icingaweb2/modules/businessprocess diff --git a/packaging/debian/rules b/packaging/debian/rules new file mode 100755 index 0000000..615fcf8 --- /dev/null +++ b/packaging/debian/rules @@ -0,0 +1,26 @@ +#!/usr/bin/make -f +#export DH_VERBOSE=1 + +%: + dh $@ + +clean: + dh_testdir + dh_clean + +build: + dh_testdir + +binary: + dh_testroot + dh_prep + dh_installdirs + dh_install + dh_installchangelogs + dh_installinfo + dh_installinit + dh_fixperms + dh_installdeb + dh_gencontrol + dh_md5sums + dh_builddeb diff --git a/packaging/debian/source/format b/packaging/debian/source/format new file mode 100644 index 0000000..af745b3 --- /dev/null +++ b/packaging/debian/source/format @@ -0,0 +1 @@ +3.0 (git) From d464a4a746165cbb69685bd394848f2d420fad88 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Tue, 10 Jan 2017 18:17:06 +0100 Subject: [PATCH 233/256] gitlab-ci: blame, coverage --- .gitignore | 1 + .gitlab-ci.yml | 9 +++++++-- phpunit.xml | 11 +++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 1df28f0..49fd402 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea/ /debian /build +/coverage diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 51495ee..aed4be7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,7 +12,7 @@ PSR2 CS Test: tags: - xenial script: - - phpcs -p --standard=PSR2 --extensions=php --encoding=utf-8 -w -s library/Businessprocess/ application/ configuration.php run.php test + - phpcs --report-width=auto --report-full --report-gitblame --report-summary -p --standard=PSR2 --extensions=php --encoding=utf-8 -w -s library/Businessprocess/ application/ configuration.php run.php test Ubuntu Xenial: stage: Unit-Tests @@ -20,7 +20,12 @@ Ubuntu Xenial: - xenial - businessprocess script: - - phpunit --testdox || phpunit --verbose + - phpunit --testdox --coverage-html=coverage || phpunit --verbose + artifacts: + expire_in: 1 week + name: code-coverage + paths: + - coverage/* Debian Jessie: stage: Unit-Tests diff --git a/phpunit.xml b/phpunit.xml index 72f31b3..064e01c 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -15,4 +15,15 @@ test/php + + + library/Businessprocess + + library/Businessprocess/Director + + + library/Businessprocess/ProvidedHook + + + From 024618e3a5158a0ad9297e3a71a03b5a0fb64850 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 08:35:07 +0100 Subject: [PATCH 234/256] DeleteNodeForm: adjust impact link --- application/forms/DeleteNodeForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/forms/DeleteNodeForm.php b/application/forms/DeleteNodeForm.php index 5d4c5c6..b3f758a 100644 --- a/application/forms/DeleteNodeForm.php +++ b/application/forms/DeleteNodeForm.php @@ -40,7 +40,7 @@ class DeleteNodeForm extends QuickForm $biLink = $view->qlink( $node->getAlias(), 'director/node/impact', - array('node' => $node->getName()), + array('name' => $node->getName()), array('data-base-target' => '_next') ); $this->addHtml( From 4c01909cf865c0b8b4361595482a91864bae8e61 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 08:35:45 +0100 Subject: [PATCH 235/256] test/php: simple new tests --- .../Businessprocess/Html/HtmlTagTest.php | 18 +++++++++++++ .../library/Businessprocess/Html/TextTest.php | 25 +++++++++++++++++++ 2 files changed, 43 insertions(+) create mode 100644 test/php/library/Businessprocess/Html/HtmlTagTest.php create mode 100644 test/php/library/Businessprocess/Html/TextTest.php diff --git a/test/php/library/Businessprocess/Html/HtmlTagTest.php b/test/php/library/Businessprocess/Html/HtmlTagTest.php new file mode 100644 index 0000000..4b24558 --- /dev/null +++ b/test/php/library/Businessprocess/Html/HtmlTagTest.php @@ -0,0 +1,18 @@ +assertEquals( + $h1->render(), + '

Hea & der

' + ); + } +} diff --git a/test/php/library/Businessprocess/Html/TextTest.php b/test/php/library/Businessprocess/Html/TextTest.php new file mode 100644 index 0000000..4970ec6 --- /dev/null +++ b/test/php/library/Businessprocess/Html/TextTest.php @@ -0,0 +1,25 @@ +assertEquals( + 'A & O', + Text::create('A & O')->getText() + ); + } + + public function testTextIsEscapedWhenRendered() + { + $this->assertEquals( + 'A & O', + Text::create('A & O')->render() + ); + } +} From 1ca7e3e1d1eec5ebc8ea961abc16c4144dc0d457 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 09:48:16 +0100 Subject: [PATCH 236/256] Add a couple of comments --- library/Businessprocess/BusinessProcess.php | 96 +++++++++++++++++++ .../Businessprocess/Renderer/Breadcrumb.php | 5 +- 2 files changed, 100 insertions(+), 1 deletion(-) diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index fd8d870..3bc4eaa 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -121,6 +121,11 @@ class BusinessProcess { } + /** + * Retrieve metadata for this configuration + * + * @return Metadata + */ public function getMetadata() { if ($this->metadata === null) { @@ -130,12 +135,26 @@ class BusinessProcess return $this->metadata; } + /** + * Set metadata + * + * @param Metadata $metadata + * + * @return $this + */ public function setMetadata(Metadata $metadata) { $this->metadata = $metadata; return $this; } + /** + * Apply pending process changes + * + * @param ProcessChanges $changes + * + * @return $this + */ public function applyChanges(ProcessChanges $changes) { $cnt = 0; @@ -150,6 +169,13 @@ class BusinessProcess return $this; } + /** + * Apply a state simulation + * + * @param Simulation $simulation + * + * @return $this + */ public function applySimulation(Simulation $simulation) { $cnt = 0; @@ -167,12 +193,25 @@ class BusinessProcess } $this->simulationCount = $cnt; + + return $this; } + + /** + * Number of applied changes + * + * @return int + */ public function countChanges() { return $this->changeCount; } + /** + * Whether changes have been applied to this configuration + * + * @return int + */ public function hasChanges() { return $this->countChanges() > 0; @@ -418,6 +457,9 @@ class BusinessProcess return $this->getRootNodes(); } + /** + * @return int + */ public function countChildren() { return count($this->root_nodes); @@ -604,6 +646,14 @@ class BusinessProcess return $this; } + /** + * Add the given node to the given BpNode + * + * @param $name + * @param BpNode $node + * + * @return $this + */ public function addNode($name, BpNode $node) { if (array_key_exists($name, $this->nodes)) { @@ -631,12 +681,18 @@ class BusinessProcess return $this; } + /** + * Remove all occurrences of a specific node by name + * + * @param $name + */ public function removeNode($name) { unset($this->nodes[$name]); if (array_key_exists($name, $this->root_nodes)) { unset($this->root_nodes[$name]); } + foreach ($this->getBpNodes() as $node) { if ($node->hasChild($name)) { $node->removeChild($name); @@ -645,6 +701,8 @@ class BusinessProcess } /** + * Get all business process nodes + * * @return BpNode[] */ public function getBpNodes() @@ -743,11 +801,23 @@ class BusinessProcess return $errors; } + /** + * Translation helper + * + * @param $msg + * + * @return mixed|string + */ public function translate($msg) { return mt('businessprocess', $msg); } + /** + * Add a message to our warning stack + * + * @param $msg + */ protected function warn($msg) { $args = func_get_args(); @@ -775,12 +845,28 @@ class BusinessProcess return $this; } + /** + * Decide whether errors should be thrown or collected + * + * @param bool $throw + * + * @return $this + */ public function throwErrors($throw = true) { $this->throwErrors = $throw; return $this; } + /** + * Begin loop detection for the given name + * + * Will throw a NestingError in case this node will be met again below itself + * + * @param $name + * + * @throws NestingError + */ public function beginLoopDetection($name) { // echo "Begin loop $name\n"; @@ -794,12 +880,22 @@ class BusinessProcess $this->loopDetection[$name] = true; } + /** + * Remove the given name from the loop detection stack + * + * @param $name + */ public function endLoopDetection($name) { // echo "End loop $this->name\n"; unset($this->loopDetection[$name]); } + /** + * Whether this configuration has any Nodes + * + * @return bool + */ public function isEmpty() { return $this->countChildren() === 0; diff --git a/library/Businessprocess/Renderer/Breadcrumb.php b/library/Businessprocess/Renderer/Breadcrumb.php index d146819..a68c442 100644 --- a/library/Businessprocess/Renderer/Breadcrumb.php +++ b/library/Businessprocess/Renderer/Breadcrumb.php @@ -47,7 +47,10 @@ class Breadcrumb extends BaseElement } /** - * @param BpNode $parent + * @param BpNode $node + * @param array $path + * @param Renderer $renderer + * * @return NodeTile */ protected static function renderNode(BpNode $node, $path, Renderer $renderer) From 8f1ce049defb307ed2d8ae0af270475f1879ace7 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 09:51:39 +0100 Subject: [PATCH 237/256] MonitoringState: introduce new helper class Removes logic from BusinessProcess class --- application/clicommands/ProcessCommand.php | 3 +- application/controllers/ProcessController.php | 3 +- library/Businessprocess/BusinessProcess.php | 166 +----------------- .../Businessprocess/State/MonitoringState.php | 133 ++++++++++++++ 4 files changed, 142 insertions(+), 163 deletions(-) create mode 100644 library/Businessprocess/State/MonitoringState.php diff --git a/application/clicommands/ProcessCommand.php b/application/clicommands/ProcessCommand.php index 4f398bd..67c63b1 100644 --- a/application/clicommands/ProcessCommand.php +++ b/application/clicommands/ProcessCommand.php @@ -6,6 +6,7 @@ use Icinga\Cli\Command; use Icinga\Module\Businessprocess\BpNode; use Icinga\Module\Businessprocess\HostNode; use Icinga\Module\Businessprocess\Node; +use Icinga\Module\Businessprocess\State\MonitoringState; use Icinga\Module\Businessprocess\Storage\LegacyStorage; class ProcessCommand extends Command @@ -94,7 +95,7 @@ class ProcessCommand extends Command /** @var BpNode $node */ $node = $bp->getNode($this->params->shift()); - $bp->retrieveStatesFromBackend(); + MonitoringState::apply($bp); if ($bp->hasErrors()) { printf( "Checking Business Process %s failed: %s\n", diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 6c2c1f4..fa3ad5f 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Businessprocess\Controllers; use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\State\MonitoringState; use Icinga\Module\Businessprocess\Storage\ConfigDiff; use Icinga\Module\Businessprocess\Html\Element; use Icinga\Module\Businessprocess\Html\HtmlString; @@ -76,7 +77,7 @@ class ProcessController extends Controller $bp = $this->loadModifiedBpConfig(); $node = $this->getNode($bp); $this->redirectOnConfigSwitch(); - $bp->retrieveStatesFromBackend(); + MonitoringState::apply($bp); $this->handleSimulations($bp); $this->setTitle($this->translate('Business Process "%s"'), $bp->getTitle()); diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index 3bc4eaa..6ec903e 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -2,14 +2,10 @@ namespace Icinga\Module\Businessprocess; -use Icinga\Application\Benchmark; use Icinga\Exception\IcingaException; -use Icinga\Exception\NotFoundError; -use Icinga\Exception\ProgrammingError; use Icinga\Module\Businessprocess\Exception\NestingError; use Icinga\Module\Businessprocess\Modification\ProcessChanges; use Icinga\Module\Monitoring\Backend\MonitoringBackend; -use Icinga\Data\Filter\Filter; use Exception; class BusinessProcess @@ -84,13 +80,6 @@ class BusinessProcess */ protected $root_nodes = array(); - /** - * All check names { 'hostA;ping' => true, ... } - * - * @var array - */ - protected $all_checks = array(); - /** * All host names { 'hostA' => true, ... } * @@ -346,109 +335,6 @@ class BusinessProcess return array_key_exists($name, $this->root_nodes); } - public function retrieveStatesFromBackend() - { - try { - $this->reallyRetrieveStatesFromBackend(); - } catch (Exception $e) { - $this->addError( - $this->translate('Could not retrieve process state: %s'), - $e->getMessage() - ); - } - } - - public function reallyRetrieveStatesFromBackend() - { - Benchmark::measure('Retrieving states for business process ' . $this->getName()); - $backend = $this->getBackend(); - // TODO: Split apart, create a dedicated function. - // Separate "parse-logic" from "retrieve-state-logic" - // Allow DB-based backend - // Use IcingaWeb2 Multi-Backend-Support - $hostFilter = array_keys($this->hosts); - - if ($this->state_type === self::HARD_STATE) { - $hostStateColumn = 'host_hard_state'; - $hostStateChangeColumn = 'host_last_hard_state_change'; - $serviceStateColumn = 'service_hard_state'; - $serviceStateChangeColumn = 'service_last_hard_state_change'; - } else { - $hostStateColumn = 'host_state'; - $hostStateChangeColumn = 'host_last_state_change'; - $serviceStateColumn = 'service_state'; - $serviceStateChangeColumn = 'service_last_state_change'; - } - $filter = Filter::matchAny(); - foreach ($hostFilter as $host) { - $filter->addFilter(Filter::where('host_name', $host)); - } - - if ($filter->isEmpty()) { - return $this; - } - - $hostStatus = $backend->select()->from('hostStatus', array( - 'hostname' => 'host_name', - 'last_state_change' => $hostStateChangeColumn, - 'in_downtime' => 'host_in_downtime', - 'ack' => 'host_acknowledged', - 'state' => $hostStateColumn - ))->applyFilter($filter)->getQuery()->fetchAll(); - - $serviceStatus = $backend->select()->from('serviceStatus', array( - 'hostname' => 'host_name', - 'service' => 'service_description', - 'last_state_change' => $serviceStateChangeColumn, - 'in_downtime' => 'service_in_downtime', - 'ack' => 'service_acknowledged', - 'state' => $serviceStateColumn - ))->applyFilter($filter)->getQuery()->fetchAll(); - - foreach ($serviceStatus as $row) { - $this->handleDbRow($row); - } - - foreach ($hostStatus as $row) { - $this->handleDbRow($row); - } - // TODO: Union, single query? - ksort($this->root_nodes); - Benchmark::measure('Got states for business process ' . $this->getName()); - - return $this; - } - - protected function handleDbRow($row) - { - $key = $row->hostname; - if (property_exists($row, 'service')) { - $key .= ';' . $row->service; - } else { - $key .= ';Hoststatus'; - } - - // We fetch more states than we need, so skip unknown ones - if (! $this->hasNode($key)) { - return; - } - - $node = $this->getNode($key); - - if ($row->state !== null) { - $node->setState($row->state)->setMissing(false); - } - if ($row->last_state_change !== null) { - $node->setLastStateChange($row->last_state_change); - } - if ((int) $row->in_downtime === 1) { - $node->setDowntime(true); - } - if ((int) $row->ack === 1) { - $node->setAck(true); - } - } - /** * @return BpNode[] */ @@ -518,6 +404,11 @@ class BusinessProcess return $node; } + public function listInvolvedHostNames() + { + return array_keys($this->hosts); + } + /** * Create and attach a new process (BpNode) * @@ -555,47 +446,6 @@ class BusinessProcess return $node; } - public function hasNodeByPath($nodeName, $path = array()) - { - if (! $this->hasNode($nodeName)) { - return false; - } - - $node = $this->getNode($nodeName); - $parents = $node->getParents(); - foreach ($parents as $parent) { - - } - while (! empty($path)) { - - } - - return empty($path); - } - - public function getNodeByPath($nodeName, $path = array()) - { - if (! $this->hasNode($nodeName)) { - throw new NotFoundError( - 'Node %s not found at %s', - $nodeName, - implode(' -> ', $path) - ); - } - } - - public function getPathsToNode($node) - { - $paths = array(); - foreach ($node->getParents() as $parent) { - foreach ($parent->getPathsToNode() as $path) { - $paths[] = $path; - } - } - - return $paths; - } - /** * @param $name * @return Node @@ -640,12 +490,6 @@ class BusinessProcess return $this; } - public function addObjectName($name) - { - $this->all_checks[$name] = 1; - return $this; - } - /** * Add the given node to the given BpNode * diff --git a/library/Businessprocess/State/MonitoringState.php b/library/Businessprocess/State/MonitoringState.php new file mode 100644 index 0000000..e0e5d1b --- /dev/null +++ b/library/Businessprocess/State/MonitoringState.php @@ -0,0 +1,133 @@ +config = $config; + $this->backend = $config->getBackend(); + } + + public static function apply(BusinessProcess $config) + { + $self = new static($config); + $self->retrieveStatesFromBackend(); + return $config; + } + + public function retrieveStatesFromBackend() + { + $config = $this->config; + + try { + $this->reallyRetrieveStatesFromBackend(); + } catch (Exception $e) { + $config->addError( + $config->translate('Could not retrieve process state: %s'), + $e->getMessage() + ); + } + } + + public function reallyRetrieveStatesFromBackend() + { + $config = $this->config; + + Benchmark::measure('Retrieving states for business process ' . $config->getName()); + $backend = $this->backend; + $hostFilter = $config->listInvolvedHostNames(); + + if ($config->usesHardStates()) { + $hostStateColumn = 'host_hard_state'; + $hostStateChangeColumn = 'host_last_hard_state_change'; + $serviceStateColumn = 'service_hard_state'; + $serviceStateChangeColumn = 'service_last_hard_state_change'; + } else { + $hostStateColumn = 'host_state'; + $hostStateChangeColumn = 'host_last_state_change'; + $serviceStateColumn = 'service_state'; + $serviceStateChangeColumn = 'service_last_state_change'; + } + $filter = Filter::matchAny(); + foreach ($hostFilter as $host) { + $filter->addFilter(Filter::where('host_name', $host)); + } + + if ($filter->isEmpty()) { + return $this; + } + + $hostStatus = $backend->select()->from('hostStatus', array( + 'hostname' => 'host_name', + 'last_state_change' => $hostStateChangeColumn, + 'in_downtime' => 'host_in_downtime', + 'ack' => 'host_acknowledged', + 'state' => $hostStateColumn + ))->applyFilter($filter)->getQuery()->fetchAll(); + + $serviceStatus = $backend->select()->from('serviceStatus', array( + 'hostname' => 'host_name', + 'service' => 'service_description', + 'last_state_change' => $serviceStateChangeColumn, + 'in_downtime' => 'service_in_downtime', + 'ack' => 'service_acknowledged', + 'state' => $serviceStateColumn + ))->applyFilter($filter)->getQuery()->fetchAll(); + + foreach ($serviceStatus as $row) { + $this->handleDbRow($row, $config); + } + + foreach ($hostStatus as $row) { + $this->handleDbRow($row, $config); + } + // TODO: Union, single query? + Benchmark::measure('Got states for business process ' . $config->getName()); + + return $this; + } + + protected function handleDbRow($row, BusinessProcess $config) + { + $key = $row->hostname; + if (property_exists($row, 'service')) { + $key .= ';' . $row->service; + } else { + $key .= ';Hoststatus'; + } + + // We fetch more states than we need, so skip unknown ones + if (! $config->hasNode($key)) { + return; + } + + $node = $config->getNode($key); + + if ($row->state !== null) { + $node->setState($row->state)->setMissing(false); + } + if ($row->last_state_change !== null) { + $node->setLastStateChange($row->last_state_change); + } + if ((int) $row->in_downtime === 1) { + $node->setDowntime(true); + } + if ((int) $row->ack === 1) { + $node->setAck(true); + } + } +} From a03feb03c5740d5b3135c2ecdca80e4716488ddb Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 11:57:10 +0100 Subject: [PATCH 238/256] Storage: clean up interface --- .../Businessprocess/Storage/LegacyStorage.php | 18 ++------- library/Businessprocess/Storage/Storage.php | 38 ++++++++++++++++++- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/library/Businessprocess/Storage/LegacyStorage.php b/library/Businessprocess/Storage/LegacyStorage.php index b914de4..8c66e43 100644 --- a/library/Businessprocess/Storage/LegacyStorage.php +++ b/library/Businessprocess/Storage/LegacyStorage.php @@ -60,12 +60,7 @@ class LegacyStorage extends Storage } /** - * All processes readable by the current user - * - * The returned array has the form => , sorted - * by title - * - * @return array + * @inheritdoc */ public function listProcesses() { @@ -85,12 +80,7 @@ class LegacyStorage extends Storage } /** - * All process names readable by the current user - * - * The returned array has the form => and is - * sorted - * - * @return array + * @inheritdoc */ public function listProcessNames() { @@ -110,9 +100,7 @@ class LegacyStorage extends Storage } /** - * All available process names, regardless of eventual restrictions - * - * @return array + * @inheritdoc */ public function listAllProcessNames() { diff --git a/library/Businessprocess/Storage/Storage.php b/library/Businessprocess/Storage/Storage.php index 7d8ccb6..71a7faf 100644 --- a/library/Businessprocess/Storage/Storage.php +++ b/library/Businessprocess/Storage/Storage.php @@ -28,10 +28,41 @@ abstract class Storage } /** + * All processes readable by the current user + * + * The returned array has the form => , sorted + * by title + * * @return array */ abstract public function listProcesses(); + /** + * All process names readable by the current user + * + * The returned array has the form => and is + * sorted + * + * @return array + */ + abstract public function listProcessesNames(); + + /** + * All available process names, regardless of eventual restrictions + * + * @return array + */ + abstract public function listAllProcessNames(); + + /** + * Whether a configuration with the given name exists + * + * @param $name + * + * @return bool + */ + abstract public function hasProcess($name); + /** * @param $name * @return BusinessProcess @@ -39,10 +70,13 @@ abstract class Storage abstract public function loadProcess($name); /** - * @param BusinessProcess $name + * Store eventual changes applied to the given configuration + * + * @param BusinessProcess $config + * * @return mixed */ - abstract public function storeProcess(BusinessProcess $name); + abstract public function storeProcess(BusinessProcess $config); /** * @param $name From a9331a1e05b6617103fa83a748e06078a983eaa9 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 12:00:20 +0100 Subject: [PATCH 239/256] LegacyConfigRenderer: move rendering to dedicated ...class, simplifies LegacyStorage --- .../Storage/LegacyConfigRenderer.php | 231 ++++++++++++++++++ .../Businessprocess/Storage/LegacyStorage.php | 23 +- 2 files changed, 233 insertions(+), 21 deletions(-) create mode 100644 library/Businessprocess/Storage/LegacyConfigRenderer.php diff --git a/library/Businessprocess/Storage/LegacyConfigRenderer.php b/library/Businessprocess/Storage/LegacyConfigRenderer.php new file mode 100644 index 0000000..4b84160 --- /dev/null +++ b/library/Businessprocess/Storage/LegacyConfigRenderer.php @@ -0,0 +1,231 @@ +config = $config; + } + + /** + * @return string + */ + public function render() + { + return $this->renderHeader() . $this->renderNodes(); + } + + /** + * @param BusinessProcess $config + * @return mixed + */ + public static function renderConfig(BusinessProcess $config) + { + $renderer = new static($config); + return $renderer->render(); + } + + /** + * @return string + */ + public function renderHeader() + { + $str = "### Business Process Config File ###\n#\n"; + + $meta = $this->config->getMetadata(); + foreach ($meta->getProperties() as $key => $value) { + if ($value === null) { + continue; + } + + $str .= sprintf("# %-11s : %s\n", $key, $value); + } + + $str .= "#\n###################################\n\n"; + + return $str; + } + + /** + * @return string + */ + public function renderNodes() + { + $this->renderedNodes = array(); + + $config = $this->config; + $str = ''; + + foreach ($config->getRootNodes() as $node) { + $str .= $this->requireRenderedBpNode($node); + } + + foreach ($config->getUnboundNodes() as $name => $node) { + $str .= $this->requireRenderedBpNode($node); + } + + return $str . "\n"; + } + + /** + * Rendered node definition, empty string if already rendered + * + * @param BpNode $node + * + * @return string + */ + protected function requireRenderedBpNode(BpNode $node) + { + $name = $node->getName(); + + if (array_key_exists($name, $this->renderedNodes)) { + + return ''; + } else { + + return $this->renderBpNode($node); + } + } + + /** + * @param BpNode $node + * @return string + */ + protected function renderBpNode(BpNode $node) + { + $name = $node->getName(); + + // Doing this before rendering children allows us to store loops + $this->renderedNodes[$name] = true; + $cfg = ''; + + foreach ($node->getChildBpNodes() as $name => $child) { + $cfg .= $this->renderBpNode($child) . "\n"; + } + + $cfg .= static::renderSingleBpNode($node); + + return $cfg; + } + + /** + * @param BpNode $node + * @return string + */ + public static function renderEqualSign(BpNode $node) + { + $op = $node->getOperator(); + if (is_numeric($op)) { + return '= ' . $op . ' of:'; + } else { + return '='; + } + } + + /** + * @param BpNode $node + * @return string + */ + public static function renderOperator(BpNode $node) + { + $op = $node->getOperator(); + if (is_numeric($op)) { + return '+'; + } else { + return $op; + } + } + + /** + * @param BpNode $node + * @return string + */ + public static function renderSingleBpNode(BpNode $node) + { + return static::renderExpression($node) + . static::renderDisplay($node) + . static::renderInfoUrl($node); + } + + /** + * @param BpNode $node + * @return string + */ + public static function renderExpression(BpNode $node) + { + return sprintf( + "%s %s %s\n", + $node->getName(), + static::renderEqualSign($node), + static::renderChildNames($node) + ); + } + + /** + * @param BpNode $node + * @return string + */ + public static function renderChildNames(BpNode $node) + { + $op = static::renderOperator($node); + $children = $node->getChildNames(); + $str = implode(' ' . $op . ' ', $children); + + if ((count($children) < 2) && $op !== '&') { + + return $op . ' ' . $str; + } else { + + return $str; + } + } + + /** + * @param BpNode $node + * @return string + */ + protected function renderDisplay(BpNode $node) + { + if ($node->hasAlias() || $node->getDisplay() > 0) { + $prio = $node->getDisplay(); + return sprintf( + "display %s;%s;%s\n", + $prio, + $node->getName(), + $node->getAlias() + ); + } else { + return ''; + } + } + + /** + * @param BpNode $node + * @return string + */ + protected function renderInfoUrl(BpNode $node) + { + if ($node->hasInfoUrl()) { + return sprintf( + "info_url;%s;%s\n", + $node->getName(), + $node->getInfoUrl() + ); + } else { + return ''; + } + } +} diff --git a/library/Businessprocess/Storage/LegacyStorage.php b/library/Businessprocess/Storage/LegacyStorage.php index 8c66e43..b8bcd48 100644 --- a/library/Businessprocess/Storage/LegacyStorage.php +++ b/library/Businessprocess/Storage/LegacyStorage.php @@ -3,10 +3,7 @@ namespace Icinga\Module\Businessprocess\Storage; use DirectoryIterator; -use Icinga\Application\Benchmark; use Icinga\Application\Icinga; -use Icinga\Exception\ConfigurationError; -use Icinga\Module\Businessprocess\BpNode; use Icinga\Module\Businessprocess\BusinessProcess; use Icinga\Exception\SystemPermissionException; use Icinga\Module\Businessprocess\Metadata; @@ -173,15 +170,13 @@ class LegacyStorage extends Storage } /** - * @param BusinessProcess $process - * - * @return void + * @inheritdoc */ public function storeProcess(BusinessProcess $process) { file_put_contents( $this->getFilename($process->getName()), - $this->render($process) + LegacyConfigRenderer::renderConfig($process) ); } @@ -254,20 +249,6 @@ class LegacyStorage extends Storage return @unlink($this->getFilename($name)); } - /** - * @return BusinessProcess - */ - public function loadProcess($name) - { - Benchmark::measure('Loading business process ' . $name); - $bp = new BusinessProcess(); - $bp->setName($name); - $this->parseFile($name, $bp); - $this->loadHeader($name, $bp); - Benchmark::measure('Business process ' . $name . ' loaded'); - return $bp; - } - /** * @param $name * @return bool From 1acf06aaba4dcf85f1e8d7abde4081281d25a6da Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 12:57:38 +0100 Subject: [PATCH 240/256] Storage: fix method name --- library/Businessprocess/Storage/Storage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Businessprocess/Storage/Storage.php b/library/Businessprocess/Storage/Storage.php index 71a7faf..516a22c 100644 --- a/library/Businessprocess/Storage/Storage.php +++ b/library/Businessprocess/Storage/Storage.php @@ -45,7 +45,7 @@ abstract class Storage * * @return array */ - abstract public function listProcessesNames(); + abstract public function listProcessNames(); /** * All available process names, regardless of eventual restrictions From c52c9705284c1595bf3d250fac4557190435f820 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 13:36:38 +0100 Subject: [PATCH 241/256] LegacyConfigParser: new parser class Takes over and cleans up logic from LegacyStorage --- .../Storage/LegacyConfigParser.php | 331 +++++++++++++++++ .../Businessprocess/Storage/LegacyStorage.php | 332 ++---------------- 2 files changed, 352 insertions(+), 311 deletions(-) create mode 100644 library/Businessprocess/Storage/LegacyConfigParser.php diff --git a/library/Businessprocess/Storage/LegacyConfigParser.php b/library/Businessprocess/Storage/LegacyConfigParser.php new file mode 100644 index 0000000..a8756f7 --- /dev/null +++ b/library/Businessprocess/Storage/LegacyConfigParser.php @@ -0,0 +1,331 @@ +name = $name; + $this->config = new BusinessProcess(); + $this->config->setName($name); + } + + /** + * @return BusinessProcess + */ + public function getParsedConfig() + { + return $this->config; + } + + public static function parseFile($name, $filename) + { + Benchmark::measure('Loading business process ' . $name); + $parser = new static($name); + $parser->reallyParseFile($filename); + Benchmark::measure('Business process ' . $name . ' loaded'); + return $parser->getParsedConfig(); + } + + public static function parseString($name, $string) + { + Benchmark::measure('Loading BP config from file: ' . $name); + $parser = new static($name); + foreach (preg_split('/\n/', $string) as $line) { + $parser->parseLine($line); + } + + Benchmark::measure('Business process ' . $name . ' loaded'); + return $parser->getParsedConfig(); + } + + protected function reallyParseFile($filename) + { + $file = $this->currentFilename = $filename; + $fh = @fopen($file, 'r'); + if (! $fh) { + throw new SystemPermissionException('Could not open "%s"', $filename); + } + + $config = $this->config; + $config->setMetadata( + $this::readMetadataFromFileHeader($config->getName(), $filename) + ); + + $this->currentLineNumber = 0; + while ($line = fgets($fh)) { + $this->parseLine($line); + } + + fclose($fh); + unset($this->currentLineNumber); + unset($this->currentFilename); + } + + public static function readMetadataFromFileHeader($name, $filename) + { + $metadata = new Metadata($name); + $fh = fopen($filename, 'r'); + $cnt = 0; + while ($cnt < 15 && false !== ($line = fgets($fh))) { + $cnt++; + static::parseHeaderLine($line, $metadata); + } + + fclose($fh); + return $metadata; + } + + protected function splitCommaSeparated($string) + { + return preg_split('/\s*,\s*/', $string, -1, PREG_SPLIT_NO_EMPTY); + } + + protected function readHeaderString($string, Metadata $metadata) + { + foreach (preg_split('/\n/', $string) as $line) { + $this->parseHeaderLine($line, $metadata); + } + + return $metadata; + } + + /** + * @return array + */ + protected function emptyHeader() + { + return array( + 'Title' => null, + 'Description' => null, + 'Owner' => null, + 'AllowedUsers' => null, + 'AllowedGroups' => null, + 'AllowedRoles' => null, + 'Backend' => null, + 'Statetype' => 'soft', + 'SLAHosts' => null + ); + } + + /** + * @param $line + * @param Metadata $metadata + */ + protected static function parseHeaderLine($line, Metadata $metadata) + { + if (preg_match('/^\s*#\s+(.+?)\s*:\s*(.+)$/', $line, $m)) { + if ($metadata->hasKey($m[1])) { + $metadata->set($m[1], $m[2]); + } + } + } + + /** + * @param $line + * @param BusinessProcess $bp + */ + protected function parseDisplay(& $line, BusinessProcess $bp) + { + list($display, $name, $desc) = preg_split('~\s*;\s*~', substr($line, 8), 3); + $bp->getBpNode($name)->setAlias($desc)->setDisplay($display); + if ($display > 0) { + $bp->addRootNode($name); + } + } + + /** + * @param $line + * @param BusinessProcess $bp + */ + protected function parseExternalInfo(& $line, BusinessProcess $bp) + { + list($name, $script) = preg_split('~\s*;\s*~', substr($line, 14), 2); + $bp->getBpNode($name)->setInfoCommand($script); + } + + protected function parseExtraInfo(& $line, BusinessProcess $bp) + { + // TODO: Not yet + // list($name, $script) = preg_split('~\s*;\s*~', substr($line, 14), 2); + // $this->getNode($name)->setExtraInfo($script); + } + + protected function parseInfoUrl(& $line, BusinessProcess $bp) + { + list($name, $url) = preg_split('~\s*;\s*~', substr($line, 9), 2); + $bp->getBpNode($name)->setInfoUrl($url); + } + + protected function parseExtraLine(& $line, $typeLength, BusinessProcess $bp) + { + $type = substr($line, 0, $typeLength); + if (substr($type, 0, 7) === 'display') { + $this->parseDisplay($line, $bp); + return true; + } + + switch ($type) { + case 'external_info': + $this->parseExternalInfo($line, $bp); + break; + case 'extra_info': + $this->parseExtraInfo($line, $bp); + break; + case 'info_url': + $this->parseInfoUrl($line, $bp); + break; + default: + return false; + } + + return true; + } + + /** + * Parses a single line + * + * Adds eventual new knowledge to the given Business Process config + * + * @param $line + * + * @throws ConfigurationError + */ + protected function parseLine(& $line) + { + $bp = $this->config; + $line = trim($line); + + $this->currentLineNumber++; + + // Skip empty or comment-only lines + if (empty($line) || $line[0] === '#') { + return; + } + + // Semicolon found in the first 14 cols? Might be a line with extra information + $pos = strpos($line, ';'); + if ($pos !== false && $pos < 14) { + if ($this->parseExtraLine($line, $pos, $bp)) { + return; + } + } + + list($name, $value) = preg_split('~\s*=\s*~', $line, 2); + + if (strpos($name, ';') !== false) { + $this->parseError('No semicolon allowed in varname'); + } + + $op = '&'; + if (preg_match_all('~([\|\+&\!])~', $value, $m)) { + $op = implode('', $m[1]); + for ($i = 1; $i < strlen($op); $i++) { + if ($op[$i] !== $op[$i - 1]) { + $this->parseError('Mixing operators is not allowed'); + } + } + } + $op = $op[0]; + $op_name = $op; + + if ($op === '+') { + if (! preg_match('~^(\d+)(?::(\d+))?\s*of:\s*(.+?)$~', $value, $m)) { + $this->parseError('syntax: = of: + [+ ]*'); + } + $op_name = $m[1]; + // New feature: $minWarn = $m[2]; + $value = $m[3]; + } + $cmps = preg_split('~\s*\\' . $op . '\s*~', $value, -1, PREG_SPLIT_NO_EMPTY); + $childNames = array(); + + 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); + } else { + $bp->createService($host, $service); + } + } + if ($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); + } + $val = $node->getName(); + } + + $childNames[] = $val; + } + + $node = new BpNode($bp, (object) array( + 'name' => $name, + 'operator' => $op_name, + 'child_names' => $childNames + )); + + $bp->addNode($name, $node); + } + + /** + * @return string + */ + public function getFilename() + { + return $this->currentFilename ?: '[given string]'; + } + + /** + * @param $msg + * @throws ConfigurationError + */ + protected function parseError($msg) + { + throw new ConfigurationError( + sprintf( + 'Parse error on %s:%s: %s', + $this->getFilename(), + $this->currentLineNumber, + $msg + ) + ); + } +} diff --git a/library/Businessprocess/Storage/LegacyStorage.php b/library/Businessprocess/Storage/LegacyStorage.php index b8bcd48..1f232eb 100644 --- a/library/Businessprocess/Storage/LegacyStorage.php +++ b/library/Businessprocess/Storage/LegacyStorage.php @@ -6,19 +6,12 @@ use DirectoryIterator; use Icinga\Application\Icinga; use Icinga\Module\Businessprocess\BusinessProcess; use Icinga\Exception\SystemPermissionException; -use Icinga\Module\Businessprocess\Metadata; class LegacyStorage extends Storage { /** @var string */ protected $configDir; - /** @var int */ - protected $parsing_line_number; - - /** @var string */ - protected $currentFilename; - public function getConfigDir() { if ($this->configDir === null) { @@ -118,57 +111,17 @@ class LegacyStorage extends Storage return $files; } - protected function splitCommaSeparated($string) + /** + * @inheritdoc + */ + public function loadProcess($name) { - return preg_split('/\s*,\s*/', $string, -1, PREG_SPLIT_NO_EMPTY); - } - - protected function readHeader($file, Metadata $metadata) - { - $fh = fopen($file, 'r'); - $cnt = 0; - while ($cnt < 15 && false !== ($line = fgets($fh))) { - $cnt++; - $this->parseHeaderLine($line, $metadata); - } - - fclose($fh); - return $metadata; - } - - protected function readHeaderString($string, Metadata $metadata) - { - foreach (preg_split('/\n/', $string) as $line) { - $this->parseHeaderLine($line, $metadata); - } - - return $metadata; - } - - protected function emptyHeader() - { - return array( - 'Title' => null, - 'Description' => null, - 'Owner' => null, - 'AllowedUsers' => null, - 'AllowedGroups' => null, - 'AllowedRoles' => null, - 'Backend' => null, - 'Statetype' => 'soft', - 'SLAHosts' => null + return LegacyConfigParser::parseFile( + $name, + $this->getFilename($name) ); } - protected function parseHeaderLine($line, Metadata $metadata) - { - if (preg_match('/^\s*#\s+(.+?)\s*:\s*(.+)$/', $line, $m)) { - if ($metadata->hasKey($m[1])) { - $metadata->set($m[1], $m[2]); - } - } - } - /** * @inheritdoc */ @@ -180,46 +133,23 @@ class LegacyStorage extends Storage ); } - public function render(BusinessProcess $process) + /** + * @inheritdoc + */ + public function deleteProcess($name) { - return $this->renderHeader($process) - . $this->renderNodes($process); + return @unlink($this->getFilename($name)); } - public function renderHeader(BusinessProcess $process) + /** + * @inheritdoc + */ + public function loadMetadata($name) { - $conf = "### Business Process Config File ###\n#\n"; - - $meta = $process->getMetadata(); - foreach ($meta->getProperties() as $key => $value) { - if ($value === null) { - continue; - } - - $conf .= sprintf("# %-11s : %s\n", $key, $value); - } - - $conf .= "#\n###################################\n\n"; - - return $conf; - } - - public function renderNodes(BusinessProcess $bp) - { - $rendered = array(); - $conf = ''; - - foreach ($bp->getRootNodes() as $child) { - $conf .= $child->toLegacyConfigString($rendered); - $rendered[$child->getName()] = true; - } - - foreach ($bp->getUnboundNodes() as $name => $node) { - $conf .= $node->toLegacyConfigString($rendered); - $rendered[$name] = true; - } - - return $conf . "\n"; + return LegacyConfigParser::readMetadataFromFileHeader( + $name, + $this->getFilename($name) + ); } public function getSource($name) @@ -234,19 +164,7 @@ class LegacyStorage extends Storage public function loadFromString($name, $string) { - $bp = new BusinessProcess(); - $bp->setName($name); - $this->parseString($string, $bp); - $this->readHeaderString($string, $bp->getMetadata()); - return $bp; - } - - /** - * @inheritdoc - */ - public function deleteProcess($name) - { - return @unlink($this->getFilename($name)); + return LegacyConfigParser::parseString($name, $string); } /** @@ -262,212 +180,4 @@ class LegacyStorage extends Storage return $this->loadMetadata($name)->canRead(); } - - public function loadMetadata($name) - { - $metadata = new Metadata($name); - return $this->readHeader($this->getFilename($name), $metadata); - } - - /** - * @param string $name - * @param BusinessProcess $bp - */ - protected function loadHeader($name, $bp) - { - // TODO: do not open twice, this is quick and dirty based on existing code - $file = $this->currentFilename = $this->getFilename($name); - $this->readHeader($file, $bp->getMetadata()); - } - - protected function parseFile($name, $bp) - { - $file = $this->currentFilename = $this->getFilename($name); - $fh = @fopen($file, 'r'); - if (! $fh) { - throw new SystemPermissionException('Could not open "%s"', $file); - } - - $this->parsing_line_number = 0; - while ($line = fgets($fh)) { - $this->parseLine($line, $bp); - } - - fclose($fh); - unset($this->parsing_line_number); - unset($this->currentFilename); - } - - protected function parseString($string, BusinessProcess $bp) - { - foreach (preg_split('/\n/', $string) as $line) { - $this->parseLine($line, $bp); - } - } - - /** - * @param $line - * @param BusinessProcess $bp - */ - protected function parseDisplay(& $line, BusinessProcess $bp) - { - list($display, $name, $desc) = preg_split('~\s*;\s*~', substr($line, 8), 3); - $bp->getNode($name)->setAlias($desc)->setDisplay($display); - if ($display > 0) { - $bp->addRootNode($name); - } - } - - protected function parseExternalInfo(& $line, BusinessProcess $bp) - { - list($name, $script) = preg_split('~\s*;\s*~', substr($line, 14), 2); - $bp->getNode($name)->setInfoCommand($script); - } - - protected function parseExtraInfo(& $line, BusinessProcess $bp) - { - // TODO: Not yet - // list($name, $script) = preg_split('~\s*;\s*~', substr($line, 14), 2); - // $this->getNode($name)->setExtraInfo($script); - } - - protected function parseInfoUrl(& $line, BusinessProcess $bp) - { - list($name, $url) = preg_split('~\s*;\s*~', substr($line, 9), 2); - $bp->getNode($name)->setInfoUrl($url); - } - - protected function parseExtraLine(& $line, $typeLength, BusinessProcess $bp) - { - $type = substr($line, 0, $typeLength); - if (substr($type, 0, 7) === 'display') { - $this->parseDisplay($line, $bp); - return true; - } - - switch ($type) { - case 'external_info': - $this->parseExternalInfo($line, $bp); - break; - case 'extra_info': - $this->parseExtraInfo($line, $bp); - break; - case 'info_url': - $this->parseInfoUrl($line, $bp); - break; - default: - return false; - } - - return true; - } - - /** - * Parses a single line - * - * Adds eventual new knowledge to the given Business Process config - * - * @param $line - * @param $bp - - */ - protected function parseLine(& $line, BusinessProcess $bp) - { - $line = trim($line); - - $this->parsing_line_number++; - - // Skip empty or comment-only lines - if (empty($line) || $line[0] === '#') { - return; - } - - // Semicolon found in the first 14 cols? Might be a line with extra information - $pos = strpos($line, ';'); - if ($pos !== false && $pos < 14) { - if ($this->parseExtraLine($line, $pos, $bp)) { - return; - } - } - - list($name, $value) = preg_split('~\s*=\s*~', $line, 2); - - if (strpos($name, ';') !== false) { - $this->parseError('No semicolon allowed in varname'); - } - - $op = '&'; - if (preg_match_all('~([\|\+&\!])~', $value, $m)) { - $op = implode('', $m[1]); - for ($i = 1; $i < strlen($op); $i++) { - if ($op[$i] !== $op[$i - 1]) { - $this->parseError('Mixing operators is not allowed'); - } - } - } - $op = $op[0]; - $op_name = $op; - - if ($op === '+') { - if (! preg_match('~^(\d+)(?::(\d+))?\s*of:\s*(.+?)$~', $value, $m)) { - $this->parseError('syntax: = of: + [+ ]*'); - } - $op_name = $m[1]; - // New feature: $minWarn = $m[2]; - $value = $m[3]; - } - $cmps = preg_split('~\s*\\' . $op . '\s*~', $value, -1, PREG_SPLIT_NO_EMPTY); - $childNames = array(); - - foreach ($cmps as $val) { - if (strpos($val, ';') !== false) { - if ($bp->hasNode($val)) { - continue; - } - - list($host, $service) = preg_split('~;~', $val, 2); - if ($service === 'Hoststatus') { - $bp->createHost($host); - } else { - $bp->createService($host, $service); - } - } - if ($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); - } - $val = $node->getName(); - } - - $childNames[] = $val; - } - - $node = new BpNode($bp, (object) array( - 'name' => $name, - 'operator' => $op_name, - 'child_names' => $childNames - )); - - $bp->addNode($name, $node); - } - - protected function parseError($msg) - { - throw new ConfigurationError( - sprintf( - 'Parse error on %s:%s: %s', - $this->currentFilename, - $this->parsing_line_number, - $msg - ) - ); - } } From d8298417e42b7ad137aaeac4709c365d8803571e Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 13:37:21 +0100 Subject: [PATCH 242/256] TextTest: more tests --- .../library/Businessprocess/Html/TextTest.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/php/library/Businessprocess/Html/TextTest.php b/test/php/library/Businessprocess/Html/TextTest.php index 4970ec6..bd6e04e 100644 --- a/test/php/library/Businessprocess/Html/TextTest.php +++ b/test/php/library/Businessprocess/Html/TextTest.php @@ -15,6 +15,24 @@ class TextTest extends BaseTestCase ); } + public function testTextIsRenderedAsGivenWhenDeclaredBeingEscaped() + { + $this->assertEquals( + 'A & O', + Text::create('A & O')->setEscaped()->render() + ); + + $this->assertEquals( + 'A & O', + Text::create('A & O')->setEscaped(true)->render() + ); + + $this->assertEquals( + 'A & O', + Text::create('A & O')->setEscaped(false)->render() + ); + } + public function testTextIsEscapedWhenRendered() { $this->assertEquals( From 0b9e0a2d863a2b97520f20d38c62802f8cee907b Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 13:37:52 +0100 Subject: [PATCH 243/256] BusinessProcess: add new helpers for BpNodes --- library/Businessprocess/BusinessProcess.php | 38 +++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BusinessProcess.php index 6ec903e..bd2fa09 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BusinessProcess.php @@ -3,6 +3,7 @@ namespace Icinga\Module\Businessprocess; use Icinga\Exception\IcingaException; +use Icinga\Exception\NotFoundError; use Icinga\Module\Businessprocess\Exception\NestingError; use Icinga\Module\Businessprocess\Modification\ProcessChanges; use Icinga\Module\Monitoring\Backend\MonitoringBackend; @@ -206,17 +207,28 @@ class BusinessProcess return $this->countChanges() > 0; } + /** + * @param $name + * + * @return $this + */ public function setName($name) { $this->name = $name; return $this; } + /** + * @return string + */ public function getName() { return $this->name; } + /** + * @return string + */ public function getHtmlId() { return 'businessprocess-' . preg_replace('/[\r\n\t\s]/', '_', $this->getName()); @@ -476,6 +488,32 @@ class BusinessProcess ); } + /** + * @param $name + * @return BpNode + * + * @throws NotFoundError + */ + public function getBpNode($name) + { + if ($this->hasBpNode($name)) { + return $this->nodes[$name]; + } else { + throw new NotFoundError('Trying to access a missing business process node "%s"', $name); + } + } + + /** + * @param $name + * + * @return bool + */ + public function hasBpNode($name) + { + return array_key_exists($name, $this->nodes) + && $this->nodes[$name] instanceof BpNode; + } + /** * Set the state for a specific node * From e26daca0f4aef47f38cccd349fc798d67f1e8781 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 13:38:21 +0100 Subject: [PATCH 244/256] LegacyConfigRenderer: make some helpers static --- library/Businessprocess/Storage/LegacyConfigRenderer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/Businessprocess/Storage/LegacyConfigRenderer.php b/library/Businessprocess/Storage/LegacyConfigRenderer.php index 4b84160..0f08b2f 100644 --- a/library/Businessprocess/Storage/LegacyConfigRenderer.php +++ b/library/Businessprocess/Storage/LegacyConfigRenderer.php @@ -197,7 +197,7 @@ class LegacyConfigRenderer * @param BpNode $node * @return string */ - protected function renderDisplay(BpNode $node) + public static function renderDisplay(BpNode $node) { if ($node->hasAlias() || $node->getDisplay() > 0) { $prio = $node->getDisplay(); @@ -216,7 +216,7 @@ class LegacyConfigRenderer * @param BpNode $node * @return string */ - protected function renderInfoUrl(BpNode $node) + public static function renderInfoUrl(BpNode $node) { if ($node->hasInfoUrl()) { return sprintf( From 5443d73f083639692da8ae6160136f850c9c0d8a Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 13:38:47 +0100 Subject: [PATCH 245/256] Node: use concret implementation in type hint --- library/Businessprocess/Node.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/Businessprocess/Node.php b/library/Businessprocess/Node.php index 7062c97..8af2d1b 100644 --- a/library/Businessprocess/Node.php +++ b/library/Businessprocess/Node.php @@ -310,7 +310,7 @@ abstract class Node } /** - * @return Node[] + * @return BpNode[] */ public function getParents() { From 75dc5e0ef6ac5a7e02bc279a70a04a45ac2a44ad Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 13:39:36 +0100 Subject: [PATCH 246/256] ProcessController: use new LegacyConfigRenderer --- application/controllers/ProcessController.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index fa3ad5f..1b5b50a 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -16,6 +16,7 @@ use Icinga\Module\Businessprocess\Renderer\Renderer; use Icinga\Module\Businessprocess\Renderer\TileRenderer; use Icinga\Module\Businessprocess\Renderer\TreeRenderer; use Icinga\Module\Businessprocess\Simulation; +use Icinga\Module\Businessprocess\Storage\LegacyConfigRenderer; use Icinga\Module\Businessprocess\Web\Component\ActionBar; use Icinga\Module\Businessprocess\Web\Component\RenderedProcessActionBar; use Icinga\Module\Businessprocess\Web\Component\Tabs; @@ -306,7 +307,7 @@ class ProcessController extends Controller $bp = $this->loadModifiedBpConfig(); $this->view->showDiff = $showDiff = (bool) $this->params->get('showDiff', false); - $this->view->source = $this->storage()->render($bp); + $this->view->source = LegacyConfigRenderer::renderConfig($bp); if ($this->view->showDiff) { $this->view->diff = ConfigDiff::create( $this->storage()->getSource($this->view->configName), From 149696fb505f155667bdb3ac1c00c988bc1ee5cd Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 13:40:42 +0100 Subject: [PATCH 247/256] BpNode: remove code related to legacy config --- library/Businessprocess/BpNode.php | 72 +++++++----------------------- 1 file changed, 16 insertions(+), 56 deletions(-) diff --git a/library/Businessprocess/BpNode.php b/library/Businessprocess/BpNode.php index 7776f34..7f1c5d0 100644 --- a/library/Businessprocess/BpNode.php +++ b/library/Businessprocess/BpNode.php @@ -428,6 +428,22 @@ class BpNode extends Node return $this->children; } + /** + * return BpNode[] + */ + public function getChildBpNodes() + { + $children = array(); + + foreach ($this->getChildren() as $name => $child) { + if ($child instanceof BpNode) { + $children[$name] = $child; + } + } + + return $children; + } + protected function assertNumericOperator() { if (! is_numeric($this->operator)) { @@ -435,62 +451,6 @@ class BpNode extends Node } } - public function toLegacyConfigString(& $rendered = array()) - { - $cfg = ''; - if (array_key_exists($this->name, $rendered)) { - return $cfg; - } - $rendered[$this->name] = true; - $children = array(); - - foreach ($this->getChildren() as $name => $child) { - $children[] = (string) $child; - if (array_key_exists($name, $rendered)) { - continue; - } - - if ($child instanceof BpNode) { - $cfg .= $child->toLegacyConfigString($rendered) . "\n"; - } - } - $eq = '='; - $op = $this->operator; - if (is_numeric($op)) { - $eq = '= ' . $op . ' of:'; - $op = '+'; - } - - $strChildren = implode(' ' . $op . ' ', $children); - if ((count($children) < 2) && $op !== '&') { - $strChildren = $op . ' ' . $strChildren; - } - $cfg .= sprintf( - "%s %s %s\n", - $this->name, - $eq, - $strChildren - ); - if ($this->hasAlias() || $this->getDisplay() > 0) { - $prio = $this->getDisplay(); - $cfg .= sprintf( - "display %s;%s;%s\n", - $prio, - $this->name, - $this->getAlias() - ); - } - if ($this->hasInfoUrl()) { - $cfg .= sprintf( - "info_url;%s;%s\n", - $this->name, - $this->getInfoUrl() - ); - } - - return $cfg; - } - public function operatorHtml() { switch ($this->operator) { From 22583aa08324f170f232193d7d9a053f9d92cd8d Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 14:04:45 +0100 Subject: [PATCH 248/256] BpConfig: renamed from BusinessProcess --- application/controllers/ProcessController.php | 14 ++++++------ application/forms/AddNodeForm.php | 8 +++---- application/forms/BpConfigForm.php | 6 ++--- application/forms/BpUploadForm.php | 8 +++---- application/forms/DeleteNodeForm.php | 8 +++---- application/forms/ProcessForm.php | 8 +++---- .../{BusinessProcess.php => BpConfig.php} | 4 ++-- library/Businessprocess/BpNode.php | 2 +- library/Businessprocess/HostNode.php | 2 +- library/Businessprocess/ImportedNode.php | 4 ++-- library/Businessprocess/Metadata.php | 1 + .../Modification/NodeAction.php | 10 ++++----- .../Modification/NodeAddChildrenAction.php | 6 ++--- .../Modification/NodeCreateAction.php | 6 ++--- .../Modification/NodeModifyAction.php | 6 ++--- .../Modification/NodeRemoveAction.php | 6 ++--- .../Modification/ProcessChanges.php | 6 ++--- library/Businessprocess/Node.php | 4 ++-- library/Businessprocess/Renderer/Renderer.php | 12 +++++----- .../Businessprocess/Renderer/TreeRenderer.php | 16 +++++++------- library/Businessprocess/ServiceNode.php | 2 +- library/Businessprocess/Simulation.php | 4 ++-- .../Businessprocess/State/MonitoringState.php | 10 ++++----- .../Storage/LegacyConfigParser.php | 22 +++++++++---------- .../Storage/LegacyConfigRenderer.php | 10 ++++----- .../Businessprocess/Storage/LegacyStorage.php | 4 ++-- library/Businessprocess/Storage/Storage.php | 8 +++---- library/Businessprocess/Test/BaseTestCase.php | 4 ++-- .../Component/RenderedProcessActionBar.php | 4 ++-- library/Businessprocess/Web/Controller.php | 4 ++-- .../library/Businessprocess/HostNodeTest.php | 6 ++--- .../Operators/AndOperatorTest.php | 16 +++++++------- .../Operators/MinOperatorTest.php | 6 ++--- .../Operators/NotOperatorTest.php | 6 ++--- .../Operators/OrOperatorTest.php | 6 ++--- .../Businessprocess/ServiceNodeTest.php | 4 ++-- .../Storage/LegacyStorageTest.php | 2 +- 37 files changed, 128 insertions(+), 127 deletions(-) rename library/Businessprocess/{BusinessProcess.php => BpConfig.php} (99%) diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php index 1b5b50a..421b298 100644 --- a/application/controllers/ProcessController.php +++ b/application/controllers/ProcessController.php @@ -2,7 +2,7 @@ namespace Icinga\Module\Businessprocess\Controllers; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\State\MonitoringState; use Icinga\Module\Businessprocess\Storage\ConfigDiff; use Icinga\Module\Businessprocess\Html\Element; @@ -131,7 +131,7 @@ class ProcessController extends Controller } } - protected function getNode(BusinessProcess $bp) + protected function getNode(BpConfig $bp) { if ($nodeName = $this->params->get('node')) { return $bp->getNode($nodeName); @@ -158,7 +158,7 @@ class ProcessController extends Controller return $this->renderer; } - protected function getProcessTabs(BusinessProcess $bp, Renderer $renderer) + protected function getProcessTabs(BpConfig $bp, Renderer $renderer) { $tabs = $this->singleTab($bp->getTitle()); @@ -169,7 +169,7 @@ class ProcessController extends Controller return $tabs; } - protected function handleSimulations(BusinessProcess $bp) + protected function handleSimulations(BpConfig $bp) { $simulation = new Simulation($bp, $this->session()); @@ -187,7 +187,7 @@ class ProcessController extends Controller $bp->applySimulation($simulation); } - protected function loadActionForm(BusinessProcess $bp, Node $node = null) + protected function loadActionForm(BpConfig $bp, Node $node = null) { $action = $this->params->get('action'); $form = null; @@ -243,7 +243,7 @@ class ProcessController extends Controller } } - protected function showWarnings(BusinessProcess $bp) + protected function showWarnings(BpConfig $bp) { if ($bp->hasWarnings()) { $ul = Element::create('ul', array('class' => 'warning')); @@ -257,7 +257,7 @@ class ProcessController extends Controller } } - protected function showHints(BusinessProcess $bp) + protected function showHints(BpConfig $bp) { $ul = Element::create('ul', array('class' => 'error')); foreach ($bp->getErrors() as $error) { diff --git a/application/forms/AddNodeForm.php b/application/forms/AddNodeForm.php index 96606a0..4413421 100644 --- a/application/forms/AddNodeForm.php +++ b/application/forms/AddNodeForm.php @@ -3,7 +3,7 @@ namespace Icinga\Module\Businessprocess\Forms; use Icinga\Module\Businessprocess\BpNode; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\Modification\ProcessChanges; use Icinga\Module\Businessprocess\Web\Form\QuickForm; use Icinga\Module\Monitoring\Backend\MonitoringBackend; @@ -14,7 +14,7 @@ class AddNodeForm extends QuickForm /** @var MonitoringBackend */ protected $backend; - /** @var BusinessProcess */ + /** @var BpConfig */ protected $bp; /** @var BpNode */ @@ -234,10 +234,10 @@ class AddNodeForm extends QuickForm } /** - * @param BusinessProcess $process + * @param BpConfig $process * @return $this */ - public function setProcess(BusinessProcess $process) + public function setProcess(BpConfig $process) { $this->bp = $process; $this->setBackend($process->getBackend()); diff --git a/application/forms/BpConfigForm.php b/application/forms/BpConfigForm.php index 1f9e884..6c0f640 100644 --- a/application/forms/BpConfigForm.php +++ b/application/forms/BpConfigForm.php @@ -4,7 +4,7 @@ namespace Icinga\Module\Businessprocess\Forms; use Icinga\Application\Config; use Icinga\Authentication\Auth; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\Storage\Storage; use Icinga\Module\Businessprocess\Web\Form\QuickForm; @@ -15,7 +15,7 @@ class BpConfigForm extends QuickForm protected $backend; - /** @var BusinessProcess */ + /** @var BpConfig */ protected $config; protected $node; @@ -153,7 +153,7 @@ class BpConfigForm extends QuickForm if ($this->config === null) { // New config - $config = new BusinessProcess(); + $config = new BpConfig(); $config->setName($name); $config->getMetadata()->set('Owner', Auth::getInstance()->getUser()->getUsername()); $this->setSuccessUrl( diff --git a/application/forms/BpUploadForm.php b/application/forms/BpUploadForm.php index ee24582..54c39c4 100644 --- a/application/forms/BpUploadForm.php +++ b/application/forms/BpUploadForm.php @@ -3,7 +3,7 @@ namespace Icinga\Module\Businessprocess\Forms; use Icinga\Application\Config; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\Storage\LegacyStorage; use Icinga\Module\Businessprocess\Web\Form\QuickForm; use Icinga\Web\Notification; @@ -15,7 +15,7 @@ class BpUploadForm extends QuickForm protected $backend; - /** @var BusinessProcess */ + /** @var BpConfig */ protected $config; protected $node; @@ -133,7 +133,7 @@ class BpUploadForm extends QuickForm return $this; } - public function setProcessConfig(BusinessProcess $config) + public function setProcessConfig(BpConfig $config) { $this->config = $config; return $this; @@ -172,7 +172,7 @@ class BpUploadForm extends QuickForm if ($this->config === null) { // New config - $config = new BusinessProcess(); + $config = new BpConfig(); $config->setName($name); if ($title) { $config->setTitle($title); diff --git a/application/forms/DeleteNodeForm.php b/application/forms/DeleteNodeForm.php index b3f758a..d91f4d5 100644 --- a/application/forms/DeleteNodeForm.php +++ b/application/forms/DeleteNodeForm.php @@ -3,7 +3,7 @@ namespace Icinga\Module\Businessprocess\Forms; use Icinga\Module\Businessprocess\BpNode; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\Modification\ProcessChanges; use Icinga\Module\Businessprocess\Node; use Icinga\Module\Businessprocess\Web\Form\QuickForm; @@ -15,7 +15,7 @@ class DeleteNodeForm extends QuickForm /** @var MonitoringBackend */ protected $backend; - /** @var BusinessProcess */ + /** @var BpConfig */ protected $bp; /** @var Node */ @@ -89,10 +89,10 @@ class DeleteNodeForm extends QuickForm } /** - * @param BusinessProcess $process + * @param BpConfig $process * @return $this */ - public function setProcess(BusinessProcess $process) + public function setProcess(BpConfig $process) { $this->bp = $process; $this->setBackend($process->getBackend()); diff --git a/application/forms/ProcessForm.php b/application/forms/ProcessForm.php index d34ecfe..b3a5e7b 100644 --- a/application/forms/ProcessForm.php +++ b/application/forms/ProcessForm.php @@ -3,7 +3,7 @@ namespace Icinga\Module\Businessprocess\Forms; use Icinga\Module\Businessprocess\BpNode; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\Modification\ProcessChanges; use Icinga\Module\Businessprocess\Web\Form\QuickForm; use Icinga\Module\Monitoring\Backend\MonitoringBackend; @@ -15,7 +15,7 @@ class ProcessForm extends QuickForm /** @var MonitoringBackend */ protected $backend; - /** @var BusinessProcess */ + /** @var BpConfig */ protected $bp; /** @var BpNode */ @@ -115,10 +115,10 @@ class ProcessForm extends QuickForm } /** - * @param BusinessProcess $process + * @param BpConfig $process * @return $this */ - public function setProcess(BusinessProcess $process) + public function setProcess(BpConfig $process) { $this->bp = $process; $this->setBackend($process->getBackend()); diff --git a/library/Businessprocess/BusinessProcess.php b/library/Businessprocess/BpConfig.php similarity index 99% rename from library/Businessprocess/BusinessProcess.php rename to library/Businessprocess/BpConfig.php index bd2fa09..767e820 100644 --- a/library/Businessprocess/BusinessProcess.php +++ b/library/Businessprocess/BpConfig.php @@ -9,7 +9,7 @@ use Icinga\Module\Businessprocess\Modification\ProcessChanges; use Icinga\Module\Monitoring\Backend\MonitoringBackend; use Exception; -class BusinessProcess +class BpConfig { const SOFT_STATE = 0; @@ -476,7 +476,7 @@ class BusinessProcess $host = substr($name, 0, $pos); $service = substr($name, $pos + 1); // TODO: deactivated, this scares me, test it - if (false && $service === 'Hoststatus') { + if ($service === 'Hoststatus') { return $this->createHost($host); } else { return $this->createService($host, $service); diff --git a/library/Businessprocess/BpNode.php b/library/Businessprocess/BpNode.php index 7f1c5d0..efa8999 100644 --- a/library/Businessprocess/BpNode.php +++ b/library/Businessprocess/BpNode.php @@ -47,7 +47,7 @@ class BpNode extends Node protected $className = 'process'; - public function __construct(BusinessProcess $bp, $object) + public function __construct(BpConfig $bp, $object) { $this->bp = $bp; $this->name = $object->name; diff --git a/library/Businessprocess/HostNode.php b/library/Businessprocess/HostNode.php index b40e0f3..2e273c3 100644 --- a/library/Businessprocess/HostNode.php +++ b/library/Businessprocess/HostNode.php @@ -32,7 +32,7 @@ class HostNode extends MonitoredNode protected $className = 'host'; - public function __construct(BusinessProcess $bp, $object) + public function __construct(BpConfig $bp, $object) { $this->name = $object->hostname . ';Hoststatus'; $this->hostname = $object->hostname; diff --git a/library/Businessprocess/ImportedNode.php b/library/Businessprocess/ImportedNode.php index c41c272..e21a169 100644 --- a/library/Businessprocess/ImportedNode.php +++ b/library/Businessprocess/ImportedNode.php @@ -21,13 +21,13 @@ class ImportedNode extends Node protected $className = 'subtree'; - /** @var BusinessProcess */ + /** @var BpConfig */ protected $config; /** * @inheritdoc */ - public function __construct(BusinessProcess $bp, $object) + public function __construct(BpConfig $bp, $object) { $this->bp = $bp; $this->configName = $object->configName; diff --git a/library/Businessprocess/Metadata.php b/library/Businessprocess/Metadata.php index cfa9525..925c29d 100644 --- a/library/Businessprocess/Metadata.php +++ b/library/Businessprocess/Metadata.php @@ -9,6 +9,7 @@ use Icinga\User; class Metadata { + /** @var string Configuration name */ protected $name; protected $properties = array( diff --git a/library/Businessprocess/Modification/NodeAction.php b/library/Businessprocess/Modification/NodeAction.php index c895145..fc7ff00 100644 --- a/library/Businessprocess/Modification/NodeAction.php +++ b/library/Businessprocess/Modification/NodeAction.php @@ -2,7 +2,7 @@ namespace Icinga\Module\Businessprocess\Modification; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\Node; use Icinga\Exception\ProgrammingError; @@ -42,18 +42,18 @@ abstract class NodeAction /** * Every NodeAction must be able to apply itself to a BusinessProcess * - * @param BusinessProcess $bp + * @param BpConfig $bp * @return mixed */ - abstract public function applyTo(BusinessProcess $bp); + abstract public function applyTo(BpConfig $bp); /** * Every NodeAction must be able to tell whether it could be applied to a BusinessProcess * - * @param BusinessProcess $bp + * @param BpConfig $bp * @return bool */ - abstract public function appliesTo(BusinessProcess $bp); + abstract public function appliesTo(BpConfig $bp); /** * The name of the node this modification applies to diff --git a/library/Businessprocess/Modification/NodeAddChildrenAction.php b/library/Businessprocess/Modification/NodeAddChildrenAction.php index fdc70a4..c6e0444 100644 --- a/library/Businessprocess/Modification/NodeAddChildrenAction.php +++ b/library/Businessprocess/Modification/NodeAddChildrenAction.php @@ -3,7 +3,7 @@ namespace Icinga\Module\Businessprocess\Modification; use Icinga\Module\Businessprocess\BpNode; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; class NodeAddChildrenAction extends NodeAction { @@ -14,7 +14,7 @@ class NodeAddChildrenAction extends NodeAction /** * @inheritdoc */ - public function appliesTo(BusinessProcess $bp) + public function appliesTo(BpConfig $bp) { $name = $this->getNodeName(); @@ -28,7 +28,7 @@ class NodeAddChildrenAction extends NodeAction /** * @inheritdoc */ - public function applyTo(BusinessProcess $bp) + public function applyTo(BpConfig $bp) { /** @var BpNode $node */ if (! $this->hasNode()) { diff --git a/library/Businessprocess/Modification/NodeCreateAction.php b/library/Businessprocess/Modification/NodeCreateAction.php index 04ab474..ce4bdb1 100644 --- a/library/Businessprocess/Modification/NodeCreateAction.php +++ b/library/Businessprocess/Modification/NodeCreateAction.php @@ -2,7 +2,7 @@ namespace Icinga\Module\Businessprocess\Modification; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\BpNode; use Icinga\Module\Businessprocess\Node; @@ -70,7 +70,7 @@ class NodeCreateAction extends NodeAction /** * @inheritdoc */ - public function appliesTo(BusinessProcess $bp) + public function appliesTo(BpConfig $bp) { return ! $bp->hasNode($this->getNodeName()); } @@ -78,7 +78,7 @@ class NodeCreateAction extends NodeAction /** * @inheritdoc */ - public function applyTo(BusinessProcess $bp) + public function applyTo(BpConfig $bp) { $name = $this->getNodeName(); diff --git a/library/Businessprocess/Modification/NodeModifyAction.php b/library/Businessprocess/Modification/NodeModifyAction.php index 4e62c98..ff15856 100644 --- a/library/Businessprocess/Modification/NodeModifyAction.php +++ b/library/Businessprocess/Modification/NodeModifyAction.php @@ -2,7 +2,7 @@ namespace Icinga\Module\Businessprocess\Modification; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\Node; class NodeModifyAction extends NodeAction @@ -43,7 +43,7 @@ class NodeModifyAction extends NodeAction /** * @inheritdoc */ - public function appliesTo(BusinessProcess $bp) + public function appliesTo(BpConfig $bp) { $name = $this->getNodeName(); @@ -66,7 +66,7 @@ class NodeModifyAction extends NodeAction /** * @inheritdoc */ - public function applyTo(BusinessProcess $bp) + public function applyTo(BpConfig $bp) { $node = $bp->getNode($this->getNodeName()); diff --git a/library/Businessprocess/Modification/NodeRemoveAction.php b/library/Businessprocess/Modification/NodeRemoveAction.php index a8a27b1..0f9ffe3 100644 --- a/library/Businessprocess/Modification/NodeRemoveAction.php +++ b/library/Businessprocess/Modification/NodeRemoveAction.php @@ -2,7 +2,7 @@ namespace Icinga\Module\Businessprocess\Modification; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; /** * NodeRemoveAction @@ -38,7 +38,7 @@ class NodeRemoveAction extends NodeAction /** * @inheritdoc */ - public function appliesTo(BusinessProcess $bp) + public function appliesTo(BpConfig $bp) { $parent = $this->getParentName(); if ($parent === null) { @@ -51,7 +51,7 @@ class NodeRemoveAction extends NodeAction /** * @inheritdoc */ - public function applyTo(BusinessProcess $bp) + public function applyTo(BpConfig $bp) { $parent = $this->getParentName(); if ($parent === null) { diff --git a/library/Businessprocess/Modification/ProcessChanges.php b/library/Businessprocess/Modification/ProcessChanges.php index 0720460..b4ffbde 100644 --- a/library/Businessprocess/Modification/ProcessChanges.php +++ b/library/Businessprocess/Modification/ProcessChanges.php @@ -2,7 +2,7 @@ namespace Icinga\Module\Businessprocess\Modification; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\Node; use Icinga\Web\Session\SessionNamespace as Session; @@ -30,12 +30,12 @@ class ProcessChanges } /** - * @param BusinessProcess $bp + * @param BpConfig $bp * @param Session $session * * @return ProcessChanges */ - public static function construct(BusinessProcess $bp, Session $session) + public static function construct(BpConfig $bp, Session $session) { $key = 'changes.' . $bp->getName(); $changes = new ProcessChanges(); diff --git a/library/Businessprocess/Node.php b/library/Businessprocess/Node.php index 8af2d1b..f8a41f6 100644 --- a/library/Businessprocess/Node.php +++ b/library/Businessprocess/Node.php @@ -41,7 +41,7 @@ abstract class Node /** * Main business process object * - * @var BusinessProcess + * @var BpConfig */ protected $bp; @@ -102,7 +102,7 @@ abstract class Node 99 => 'PENDING' ); - abstract public function __construct(BusinessProcess $bp, $object); + abstract public function __construct(BpConfig $bp, $object); public function setMissing($missing = true) { diff --git a/library/Businessprocess/Renderer/Renderer.php b/library/Businessprocess/Renderer/Renderer.php index 655a108..1659c71 100644 --- a/library/Businessprocess/Renderer/Renderer.php +++ b/library/Businessprocess/Renderer/Renderer.php @@ -5,7 +5,7 @@ namespace Icinga\Module\Businessprocess\Renderer; use Icinga\Date\DateFormatter; use Icinga\Exception\ProgrammingError; use Icinga\Module\Businessprocess\BpNode; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\Html\Container; use Icinga\Module\Businessprocess\Html\Element; use Icinga\Module\Businessprocess\Html\Html; @@ -15,7 +15,7 @@ use Icinga\Module\Businessprocess\Web\Url; abstract class Renderer extends Html { - /** @var BusinessProcess */ + /** @var BpConfig */ protected $bp; /** @var BpNode */ @@ -39,17 +39,17 @@ abstract class Renderer extends Html /** * Renderer constructor. * - * @param BusinessProcess $bp + * @param BpConfig $bp * @param BpNode|null $parent */ - public function __construct(BusinessProcess $bp, BpNode $parent = null) + public function __construct(BpConfig $bp, BpNode $parent = null) { $this->bp = $bp; $this->parent = $parent; } /** - * @return BusinessProcess + * @return BpConfig */ public function getBusinessProcess() { @@ -299,7 +299,7 @@ abstract class Renderer extends Html )->setContent(DateFormatter::timeSince($time, $timeOnly)); } - protected function createUnboundParent(BusinessProcess $bp) + protected function createUnboundParent(BpConfig $bp) { $unbound = $bp->getUnboundNodes(); diff --git a/library/Businessprocess/Renderer/TreeRenderer.php b/library/Businessprocess/Renderer/TreeRenderer.php index b213e48..2b1ef26 100644 --- a/library/Businessprocess/Renderer/TreeRenderer.php +++ b/library/Businessprocess/Renderer/TreeRenderer.php @@ -3,7 +3,7 @@ namespace Icinga\Module\Businessprocess\Renderer; use Icinga\Module\Businessprocess\BpNode; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\Html\Container; use Icinga\Module\Businessprocess\Html\Element; use Icinga\Module\Businessprocess\Html\Icon; @@ -31,10 +31,10 @@ class TreeRenderer extends Renderer } /** - * @param BusinessProcess $bp + * @param BpConfig $bp * @return string */ - public function renderBp(BusinessProcess $bp) + public function renderBp(BpConfig $bp) { $html = array(); if ($this->wantsRootNodes()) { @@ -94,13 +94,13 @@ class TreeRenderer extends Renderer } /** - * @param BusinessProcess $bp + * @param BpConfig $bp * @param Node $node * @param array $path * * @return string */ - public function renderNode(BusinessProcess $bp, Node $node, $path = array()) + public function renderNode(BpConfig $bp, Node $node, $path = array()) { $table = Element::create( 'table', @@ -179,7 +179,7 @@ class TreeRenderer extends Renderer return $table; } - protected function getActionIcons(BusinessProcess $bp, Node $node) + protected function getActionIcons(BpConfig $bp, Node $node) { if ($node instanceof BpNode) { return $this->createEditAction($bp, $node); @@ -188,7 +188,7 @@ class TreeRenderer extends Renderer } } - protected function createEditAction(BusinessProcess $bp, BpNode $node) + protected function createEditAction(BpConfig $bp, BpNode $node) { return $this->actionIcon( 'wrench', @@ -200,7 +200,7 @@ class TreeRenderer extends Renderer ); } - protected function createSimulationAction(BusinessProcess $bp, Node $node) + protected function createSimulationAction(BpConfig $bp, Node $node) { return $this->actionIcon( 'magic', diff --git a/library/Businessprocess/ServiceNode.php b/library/Businessprocess/ServiceNode.php index 2a582e7..b827835 100644 --- a/library/Businessprocess/ServiceNode.php +++ b/library/Businessprocess/ServiceNode.php @@ -12,7 +12,7 @@ class ServiceNode extends MonitoredNode protected $className = 'service'; - public function __construct(BusinessProcess $bp, $object) + public function __construct(BpConfig $bp, $object) { $this->name = $object->hostname . ';' . $object->service; $this->hostname = $object->hostname; diff --git a/library/Businessprocess/Simulation.php b/library/Businessprocess/Simulation.php index 2d47470..ede70d2 100644 --- a/library/Businessprocess/Simulation.php +++ b/library/Businessprocess/Simulation.php @@ -13,7 +13,7 @@ class Simulation protected $session; /** - * @var BusinessProcess + * @var BpConfig */ protected $bp; @@ -27,7 +27,7 @@ class Simulation */ protected $simulations; - public function __construct(BusinessProcess $bp, SessionNamespace $session) + public function __construct(BpConfig $bp, SessionNamespace $session) { $this->bp = $bp; $this->session = $session; diff --git a/library/Businessprocess/State/MonitoringState.php b/library/Businessprocess/State/MonitoringState.php index e0e5d1b..68284d3 100644 --- a/library/Businessprocess/State/MonitoringState.php +++ b/library/Businessprocess/State/MonitoringState.php @@ -5,24 +5,24 @@ namespace Icinga\Module\Businessprocess\State; use Exception; use Icinga\Application\Benchmark; use Icinga\Data\Filter\Filter; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Monitoring\Backend\MonitoringBackend; class MonitoringState { - /** @var BusinessProcess */ + /** @var BpConfig */ protected $config; /** @var MonitoringBackend */ protected $backend; - private function __construct(BusinessProcess $config) + private function __construct(BpConfig $config) { $this->config = $config; $this->backend = $config->getBackend(); } - public static function apply(BusinessProcess $config) + public static function apply(BpConfig $config) { $self = new static($config); $self->retrieveStatesFromBackend(); @@ -101,7 +101,7 @@ class MonitoringState return $this; } - protected function handleDbRow($row, BusinessProcess $config) + protected function handleDbRow($row, BpConfig $config) { $key = $row->hostname; if (property_exists($row, 'service')) { diff --git a/library/Businessprocess/Storage/LegacyConfigParser.php b/library/Businessprocess/Storage/LegacyConfigParser.php index a8756f7..c49b627 100644 --- a/library/Businessprocess/Storage/LegacyConfigParser.php +++ b/library/Businessprocess/Storage/LegacyConfigParser.php @@ -6,7 +6,7 @@ use Icinga\Application\Benchmark; use Icinga\Exception\ConfigurationError; use Icinga\Exception\SystemPermissionException; use Icinga\Module\Businessprocess\BpNode; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\Metadata; class LegacyConfigParser @@ -19,7 +19,7 @@ class LegacyConfigParser protected $name; - /** @var BusinessProcess */ + /** @var BpConfig */ protected $config; /** @@ -30,12 +30,12 @@ class LegacyConfigParser private function __construct($name) { $this->name = $name; - $this->config = new BusinessProcess(); + $this->config = new BpConfig(); $this->config->setName($name); } /** - * @return BusinessProcess + * @return BpConfig */ public function getParsedConfig() { @@ -147,9 +147,9 @@ class LegacyConfigParser /** * @param $line - * @param BusinessProcess $bp + * @param BpConfig $bp */ - protected function parseDisplay(& $line, BusinessProcess $bp) + protected function parseDisplay(& $line, BpConfig $bp) { list($display, $name, $desc) = preg_split('~\s*;\s*~', substr($line, 8), 3); $bp->getBpNode($name)->setAlias($desc)->setDisplay($display); @@ -160,28 +160,28 @@ class LegacyConfigParser /** * @param $line - * @param BusinessProcess $bp + * @param BpConfig $bp */ - protected function parseExternalInfo(& $line, BusinessProcess $bp) + protected function parseExternalInfo(& $line, BpConfig $bp) { list($name, $script) = preg_split('~\s*;\s*~', substr($line, 14), 2); $bp->getBpNode($name)->setInfoCommand($script); } - protected function parseExtraInfo(& $line, BusinessProcess $bp) + protected function parseExtraInfo(& $line, BpConfig $bp) { // TODO: Not yet // list($name, $script) = preg_split('~\s*;\s*~', substr($line, 14), 2); // $this->getNode($name)->setExtraInfo($script); } - protected function parseInfoUrl(& $line, BusinessProcess $bp) + protected function parseInfoUrl(& $line, BpConfig $bp) { list($name, $url) = preg_split('~\s*;\s*~', substr($line, 9), 2); $bp->getBpNode($name)->setInfoUrl($url); } - protected function parseExtraLine(& $line, $typeLength, BusinessProcess $bp) + protected function parseExtraLine(& $line, $typeLength, BpConfig $bp) { $type = substr($line, 0, $typeLength); if (substr($type, 0, 7) === 'display') { diff --git a/library/Businessprocess/Storage/LegacyConfigRenderer.php b/library/Businessprocess/Storage/LegacyConfigRenderer.php index 0f08b2f..4bb7e18 100644 --- a/library/Businessprocess/Storage/LegacyConfigRenderer.php +++ b/library/Businessprocess/Storage/LegacyConfigRenderer.php @@ -3,7 +3,7 @@ namespace Icinga\Module\Businessprocess\Storage; use Icinga\Module\Businessprocess\BpNode; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; class LegacyConfigRenderer { @@ -13,9 +13,9 @@ class LegacyConfigRenderer /** * LecagyConfigRenderer constructor * - * @param BusinessProcess $config + * @param BpConfig $config */ - public function __construct(BusinessProcess $config) + public function __construct(BpConfig $config) { $this->config = $config; } @@ -29,10 +29,10 @@ class LegacyConfigRenderer } /** - * @param BusinessProcess $config + * @param BpConfig $config * @return mixed */ - public static function renderConfig(BusinessProcess $config) + public static function renderConfig(BpConfig $config) { $renderer = new static($config); return $renderer->render(); diff --git a/library/Businessprocess/Storage/LegacyStorage.php b/library/Businessprocess/Storage/LegacyStorage.php index 1f232eb..2c65347 100644 --- a/library/Businessprocess/Storage/LegacyStorage.php +++ b/library/Businessprocess/Storage/LegacyStorage.php @@ -4,7 +4,7 @@ namespace Icinga\Module\Businessprocess\Storage; use DirectoryIterator; use Icinga\Application\Icinga; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Exception\SystemPermissionException; class LegacyStorage extends Storage @@ -125,7 +125,7 @@ class LegacyStorage extends Storage /** * @inheritdoc */ - public function storeProcess(BusinessProcess $process) + public function storeProcess(BpConfig $process) { file_put_contents( $this->getFilename($process->getName()), diff --git a/library/Businessprocess/Storage/Storage.php b/library/Businessprocess/Storage/Storage.php index 516a22c..de3d939 100644 --- a/library/Businessprocess/Storage/Storage.php +++ b/library/Businessprocess/Storage/Storage.php @@ -3,7 +3,7 @@ namespace Icinga\Module\Businessprocess\Storage; use Icinga\Data\ConfigObject; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\Metadata; abstract class Storage @@ -65,18 +65,18 @@ abstract class Storage /** * @param $name - * @return BusinessProcess + * @return BpConfig */ abstract public function loadProcess($name); /** * Store eventual changes applied to the given configuration * - * @param BusinessProcess $config + * @param BpConfig $config * * @return mixed */ - abstract public function storeProcess(BusinessProcess $config); + abstract public function storeProcess(BpConfig $config); /** * @param $name diff --git a/library/Businessprocess/Test/BaseTestCase.php b/library/Businessprocess/Test/BaseTestCase.php index 2113494..807905d 100644 --- a/library/Businessprocess/Test/BaseTestCase.php +++ b/library/Businessprocess/Test/BaseTestCase.php @@ -5,7 +5,7 @@ namespace Icinga\Module\Businessprocess\Test; use Icinga\Application\Config; use Icinga\Application\ApplicationBootstrap; use Icinga\Application\Icinga; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\Storage\LegacyStorage; use Icinga\Module\Businessprocess\Web\FakeRequest; use PHPUnit_Framework_TestCase; @@ -30,7 +30,7 @@ abstract class BaseTestCase extends PHPUnit_Framework_TestCase } /*** - * @return BusinessProcess + * @return BpConfig */ protected function makeLoop() { diff --git a/library/Businessprocess/Web/Component/RenderedProcessActionBar.php b/library/Businessprocess/Web/Component/RenderedProcessActionBar.php index 3d67e62..9bb5ad3 100644 --- a/library/Businessprocess/Web/Component/RenderedProcessActionBar.php +++ b/library/Businessprocess/Web/Component/RenderedProcessActionBar.php @@ -3,7 +3,7 @@ namespace Icinga\Module\Businessprocess\Web\Component; use Icinga\Authentication\Auth; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\Html\BaseElement; use Icinga\Module\Businessprocess\Html\Link; use Icinga\Module\Businessprocess\Renderer\Renderer; @@ -12,7 +12,7 @@ use Icinga\Module\Businessprocess\Web\Url; class RenderedProcessActionBar extends ActionBar { - public function __construct(BusinessProcess $config, Renderer $renderer, Auth $auth, Url $url) + public function __construct(BpConfig $config, Renderer $renderer, Auth $auth, Url $url) { $meta = $config->getMetadata(); diff --git a/library/Businessprocess/Web/Controller.php b/library/Businessprocess/Web/Controller.php index 7748d7f..3f82512 100644 --- a/library/Businessprocess/Web/Controller.php +++ b/library/Businessprocess/Web/Controller.php @@ -3,7 +3,7 @@ namespace Icinga\Module\Businessprocess\Web; use Icinga\Application\Icinga; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\Html\HtmlTag; use Icinga\Module\Businessprocess\Modification\ProcessChanges; use Icinga\Module\Businessprocess\Storage\LegacyStorage; @@ -22,7 +22,7 @@ class Controller extends ModuleController /** @var View */ public $view; - /** @var BusinessProcess */ + /** @var BpConfig */ protected $bp; /** @var Tabs */ diff --git a/test/php/library/Businessprocess/HostNodeTest.php b/test/php/library/Businessprocess/HostNodeTest.php index f8c6cd9..2214f67 100644 --- a/test/php/library/Businessprocess/HostNodeTest.php +++ b/test/php/library/Businessprocess/HostNodeTest.php @@ -2,7 +2,7 @@ namespace Tests\Icinga\Module\Businessprocess; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\HostNode; use Icinga\Module\Businessprocess\Test\BaseTestCase; @@ -46,7 +46,7 @@ class HostNodeTest extends BaseTestCase */ public function testWhetherSettingAnInvalidStateFails() { - $bp = new BusinessProcess(); + $bp = new BpConfig(); $host = $bp->createHost('localhost')->setState(98); $bp->createBp('p')->addChild($host)->getState(); } @@ -56,7 +56,7 @@ class HostNodeTest extends BaseTestCase */ protected function localhost() { - $bp = new BusinessProcess(); + $bp = new BpConfig(); return new HostNode($bp, (object) array( 'hostname' => 'localhost', 'state' => 0, diff --git a/test/php/library/Businessprocess/Operators/AndOperatorTest.php b/test/php/library/Businessprocess/Operators/AndOperatorTest.php index 8e88c39..c9b3047 100644 --- a/test/php/library/Businessprocess/Operators/AndOperatorTest.php +++ b/test/php/library/Businessprocess/Operators/AndOperatorTest.php @@ -2,7 +2,7 @@ namespace Tests\Icinga\Module\Businessprocess\Operator; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\Test\BaseTestCase; use Icinga\Module\Businessprocess\Storage\LegacyStorage; @@ -18,7 +18,7 @@ class AndOperatorTest extends BaseTestCase foreach ($expressions as $expression) { $this->assertInstanceOf( - 'Icinga\\Module\\Businessprocess\\Businessprocess', + 'Icinga\\Module\\Businessprocess\\BpConfig', $storage->loadFromString('dummy', $expression) ); } @@ -104,7 +104,7 @@ class AndOperatorTest extends BaseTestCase public function testWhetherSimpleAndOperationWorks() { - $bp = new BusinessProcess(); + $bp = new BpConfig(); $bp->throwErrors(); $host = $bp->createHost('localhost')->setState(1); $service = $bp->createService('localhost', 'ping')->setState(1); @@ -130,7 +130,7 @@ class AndOperatorTest extends BaseTestCase public function testWhetherSimpleOrOperationWorks() { - $bp = new BusinessProcess(); + $bp = new BpConfig(); $bp->throwErrors(); $host = $bp->createHost('localhost')->setState(1); $service = $bp->createService('localhost', 'ping')->setState(1); @@ -145,7 +145,7 @@ class AndOperatorTest extends BaseTestCase public function testWhetherPendingIsAccepted() { - $bp = new BusinessProcess(); + $bp = new BpConfig(); $host = $bp->createHost('localhost')->setState(99); $service = $bp->createService('localhost', 'ping')->setState(99); $p = $bp->createBp('p') @@ -160,7 +160,7 @@ class AndOperatorTest extends BaseTestCase public function testWhetherWarningIsWorseThanPending() { - $bp = new BusinessProcess(); + $bp = new BpConfig(); $host = $bp->createHost('localhost')->setState(99); $service = $bp->createService('localhost', 'ping')->setState(1); $p = $bp->createBp('p') @@ -175,7 +175,7 @@ class AndOperatorTest extends BaseTestCase public function testWhetherPendingIsWorseThanUpOrOk() { - $bp = new BusinessProcess(); + $bp = new BpConfig(); $host = $bp->createHost('localhost')->setState(99); $service = $bp->createService('localhost', 'ping')->setState(0); $p = $bp->createBp('p') @@ -198,7 +198,7 @@ class AndOperatorTest extends BaseTestCase } /** - * @return BusinessProcess + * @return BpConfig */ protected function getBp() { diff --git a/test/php/library/Businessprocess/Operators/MinOperatorTest.php b/test/php/library/Businessprocess/Operators/MinOperatorTest.php index 43126dd..713c478 100644 --- a/test/php/library/Businessprocess/Operators/MinOperatorTest.php +++ b/test/php/library/Businessprocess/Operators/MinOperatorTest.php @@ -2,7 +2,7 @@ namespace Tests\Icinga\Module\Businessprocess\Operator; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\Test\BaseTestCase; use Icinga\Module\Businessprocess\Storage\LegacyStorage; @@ -18,7 +18,7 @@ class MinOperatorTest extends BaseTestCase $this->getName(); foreach ($expressions as $expression) { $this->assertInstanceOf( - 'Icinga\\Module\\Businessprocess\\Businessprocess', + 'Icinga\\Module\\Businessprocess\\BpConfig', $storage->loadFromString('dummy', $expression) ); } @@ -102,7 +102,7 @@ class MinOperatorTest extends BaseTestCase } /** - * @return BusinessProcess + * @return BpConfig */ protected function getBp() { diff --git a/test/php/library/Businessprocess/Operators/NotOperatorTest.php b/test/php/library/Businessprocess/Operators/NotOperatorTest.php index 9711b1e..dad8042 100644 --- a/test/php/library/Businessprocess/Operators/NotOperatorTest.php +++ b/test/php/library/Businessprocess/Operators/NotOperatorTest.php @@ -2,7 +2,7 @@ namespace Tests\Icinga\Module\Businessprocess\Operator; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\Test\BaseTestCase; use Icinga\Module\Businessprocess\Storage\LegacyStorage; @@ -20,7 +20,7 @@ class NotOperatorTest extends BaseTestCase foreach ($expressions as $expression) { $this->assertInstanceOf( - 'Icinga\\Module\\Businessprocess\\Businessprocess', + 'Icinga\\Module\\Businessprocess\\BpConfig', $storage->loadFromString('dummy', $expression) ); } @@ -138,7 +138,7 @@ class NotOperatorTest extends BaseTestCase } /** - * @return BusinessProcess + * @return BpConfig */ protected function getBp() { diff --git a/test/php/library/Businessprocess/Operators/OrOperatorTest.php b/test/php/library/Businessprocess/Operators/OrOperatorTest.php index ac2f5f3..a9f5b1a 100644 --- a/test/php/library/Businessprocess/Operators/OrOperatorTest.php +++ b/test/php/library/Businessprocess/Operators/OrOperatorTest.php @@ -2,7 +2,7 @@ namespace Tests\Icinga\Module\Businessprocess\Operator; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\Test\BaseTestCase; use Icinga\Module\Businessprocess\Storage\LegacyStorage; @@ -18,7 +18,7 @@ class OrOperatorTest extends BaseTestCase foreach ($expressions as $expression) { $this->assertInstanceOf( - 'Icinga\\Module\\Businessprocess\\Businessprocess', + 'Icinga\\Module\\Businessprocess\\BpConfig', $storage->loadFromString('dummy', $expression) ); } @@ -103,7 +103,7 @@ class OrOperatorTest extends BaseTestCase } /** - * @return BusinessProcess + * @return BpConfig */ protected function getBp() { diff --git a/test/php/library/Businessprocess/ServiceNodeTest.php b/test/php/library/Businessprocess/ServiceNodeTest.php index 6389ff3..152c256 100644 --- a/test/php/library/Businessprocess/ServiceNodeTest.php +++ b/test/php/library/Businessprocess/ServiceNodeTest.php @@ -2,7 +2,7 @@ namespace Tests\Icinga\Module\Businessprocess; -use Icinga\Module\Businessprocess\BusinessProcess; +use Icinga\Module\Businessprocess\BpConfig; use Icinga\Module\Businessprocess\ServiceNode; use Icinga\Module\Businessprocess\Test\BaseTestCase; @@ -46,7 +46,7 @@ class ServiceNodeTest extends BaseTestCase */ protected function pingOnLocalhost() { - $bp = new BusinessProcess(); + $bp = new BpConfig(); return new ServiceNode($bp, (object) array( 'hostname' => 'localhost', 'service' => 'ping <> pong', diff --git a/test/php/library/Businessprocess/Storage/LegacyStorageTest.php b/test/php/library/Businessprocess/Storage/LegacyStorageTest.php index 4c5425a..038261d 100644 --- a/test/php/library/Businessprocess/Storage/LegacyStorageTest.php +++ b/test/php/library/Businessprocess/Storage/LegacyStorageTest.php @@ -7,7 +7,7 @@ use Icinga\Module\Businessprocess\Storage\LegacyStorage; class LegacyStorageTest extends BaseTestCase { - private $processClass = 'Icinga\\Module\\Businessprocess\\BusinessProcess'; + private $processClass = 'Icinga\\Module\\Businessprocess\\BpConfig'; public function testCanBeInstantiatedWithAnEmptyConfigSection() { From f769fd38557939be40855b4df9503d9b5e4efdc3 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 14:33:35 +0100 Subject: [PATCH 249/256] Renderer: rename config property --- library/Businessprocess/Renderer/Renderer.php | 16 ++++++++-------- .../Businessprocess/Renderer/TileRenderer.php | 2 +- .../Businessprocess/Renderer/TreeRenderer.php | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/library/Businessprocess/Renderer/Renderer.php b/library/Businessprocess/Renderer/Renderer.php index 1659c71..c6efd46 100644 --- a/library/Businessprocess/Renderer/Renderer.php +++ b/library/Businessprocess/Renderer/Renderer.php @@ -16,7 +16,7 @@ use Icinga\Module\Businessprocess\Web\Url; abstract class Renderer extends Html { /** @var BpConfig */ - protected $bp; + protected $config; /** @var BpNode */ protected $parent; @@ -39,12 +39,12 @@ abstract class Renderer extends Html /** * Renderer constructor. * - * @param BpConfig $bp + * @param BpConfig $config * @param BpNode|null $parent */ - public function __construct(BpConfig $bp, BpNode $parent = null) + public function __construct(BpConfig $config, BpNode $parent = null) { - $this->bp = $bp; + $this->config = $config; $this->parent = $parent; } @@ -53,7 +53,7 @@ abstract class Renderer extends Html */ public function getBusinessProcess() { - return $this->bp; + return $this->config; } /** @@ -102,7 +102,7 @@ abstract class Renderer extends Html public function getChildNodes() { if ($this->wantsRootNodes()) { - return $this->bp->getRootNodes(); + return $this->config->getRootNodes(); } else { return $this->parent->getChildren(); } @@ -114,7 +114,7 @@ abstract class Renderer extends Html public function countChildNodes() { if ($this->wantsRootNodes()) { - return $this->bp->countChildren(); + return $this->config->countChildren(); } else { return $this->parent->countChildren(); } @@ -323,6 +323,6 @@ abstract class Renderer extends Html public function __destruct() { unset($this->parent); - unset($this->bp); + unset($this->config); } } diff --git a/library/Businessprocess/Renderer/TileRenderer.php b/library/Businessprocess/Renderer/TileRenderer.php index c544d79..9d6218a 100644 --- a/library/Businessprocess/Renderer/TileRenderer.php +++ b/library/Businessprocess/Renderer/TileRenderer.php @@ -14,7 +14,7 @@ class TileRenderer extends Renderer */ public function render() { - $bp = $this->bp; + $bp = $this->config; $nodesDiv = Container::create( array( 'class' => array( diff --git a/library/Businessprocess/Renderer/TreeRenderer.php b/library/Businessprocess/Renderer/TreeRenderer.php index 2b1ef26..6069122 100644 --- a/library/Businessprocess/Renderer/TreeRenderer.php +++ b/library/Businessprocess/Renderer/TreeRenderer.php @@ -18,7 +18,7 @@ class TreeRenderer extends Renderer */ public function render() { - $bp = $this->bp; + $bp = $this->config; $this->add(Container::create( array( 'id' => $bp->getHtmlId(), From fc117217ddececb3138be8a7afd3e098fda1bbed Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 15:56:51 +0100 Subject: [PATCH 250/256] Storage: cosmetics --- .../Businessprocess/Storage/LegacyConfigParser.php | 14 +++++++++++++- .../Storage/LegacyConfigRenderer.php | 2 +- library/Businessprocess/Storage/LegacyStorage.php | 6 ++++++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/library/Businessprocess/Storage/LegacyConfigParser.php b/library/Businessprocess/Storage/LegacyConfigParser.php index c49b627..a93ead2 100644 --- a/library/Businessprocess/Storage/LegacyConfigParser.php +++ b/library/Businessprocess/Storage/LegacyConfigParser.php @@ -5,8 +5,8 @@ namespace Icinga\Module\Businessprocess\Storage; use Icinga\Application\Benchmark; use Icinga\Exception\ConfigurationError; use Icinga\Exception\SystemPermissionException; -use Icinga\Module\Businessprocess\BpNode; use Icinga\Module\Businessprocess\BpConfig; +use Icinga\Module\Businessprocess\BpNode; use Icinga\Module\Businessprocess\Metadata; class LegacyConfigParser @@ -42,6 +42,12 @@ class LegacyConfigParser return $this->config; } + /** + * @param $name + * @param $filename + * + * @return BpConfig + */ public static function parseFile($name, $filename) { Benchmark::measure('Loading business process ' . $name); @@ -51,6 +57,12 @@ class LegacyConfigParser return $parser->getParsedConfig(); } + /** + * @param $name + * @param $string + * + * @return BpConfig + */ public static function parseString($name, $string) { Benchmark::measure('Loading BP config from file: ' . $name); diff --git a/library/Businessprocess/Storage/LegacyConfigRenderer.php b/library/Businessprocess/Storage/LegacyConfigRenderer.php index 4bb7e18..1553221 100644 --- a/library/Businessprocess/Storage/LegacyConfigRenderer.php +++ b/library/Businessprocess/Storage/LegacyConfigRenderer.php @@ -51,7 +51,7 @@ class LegacyConfigRenderer continue; } - $str .= sprintf("# %-11s : %s\n", $key, $value); + $str .= sprintf("# %-15s : %s\n", $key, $value); } $str .= "#\n###################################\n\n"; diff --git a/library/Businessprocess/Storage/LegacyStorage.php b/library/Businessprocess/Storage/LegacyStorage.php index 2c65347..8cbe89b 100644 --- a/library/Businessprocess/Storage/LegacyStorage.php +++ b/library/Businessprocess/Storage/LegacyStorage.php @@ -162,6 +162,12 @@ class LegacyStorage extends Storage return $this->getConfigDir() . '/' . $name . '.conf'; } + /** + * @param $name + * @param $string + * + * @return BpConfig + */ public function loadFromString($name, $string) { return LegacyConfigParser::parseString($name, $string); From 094fbd6c27800babc6fb3c94f68f237add01c7c0 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 15:57:19 +0100 Subject: [PATCH 251/256] BpConfigForm: validate process name --- application/forms/BpConfigForm.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/application/forms/BpConfigForm.php b/application/forms/BpConfigForm.php index 6c0f640..c485a57 100644 --- a/application/forms/BpConfigForm.php +++ b/application/forms/BpConfigForm.php @@ -31,6 +31,21 @@ class BpConfigForm extends QuickForm $this->addElement('text', 'name', array( 'label' => $this->translate('Name'), 'required' => true, + 'validators' => array( + array( + 'validator' => 'StringLength', + 'options' => array( + 'min' => 2, + 'max' => 40 + ) + ), + array( + 'validator' => 'Regex', + 'options' => array( + 'pattern' => '/^[a-zA-Z0-9](?:[a-zA-Z0-9 ._-]*)?[a-zA-Z0-9_]$/' + ) + ) + ), 'description' => $this->translate( 'This is the unique identifier of this process' ), From 56ab13d2b0973ccbaed817eb72448ceafadfbb8f Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 16:54:16 +0100 Subject: [PATCH 252/256] LegacyConfigRenderer: use correct helper... ...when rendering required process --- library/Businessprocess/Storage/LegacyConfigRenderer.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/library/Businessprocess/Storage/LegacyConfigRenderer.php b/library/Businessprocess/Storage/LegacyConfigRenderer.php index 1553221..6c3a7ba 100644 --- a/library/Businessprocess/Storage/LegacyConfigRenderer.php +++ b/library/Businessprocess/Storage/LegacyConfigRenderer.php @@ -95,7 +95,7 @@ class LegacyConfigRenderer return ''; } else { - + $this->renderedNodes[$name] = true; return $this->renderBpNode($node); } } @@ -107,13 +107,11 @@ class LegacyConfigRenderer protected function renderBpNode(BpNode $node) { $name = $node->getName(); - // Doing this before rendering children allows us to store loops - $this->renderedNodes[$name] = true; $cfg = ''; foreach ($node->getChildBpNodes() as $name => $child) { - $cfg .= $this->renderBpNode($child) . "\n"; + $cfg .= $this->requireRenderedBpNode($child) . "\n"; } $cfg .= static::renderSingleBpNode($node); From 643261c9f7b5e750fec1f92f1d37f6c2f9323be1 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 16:55:05 +0100 Subject: [PATCH 253/256] NodeController: apply simulations to business... ...impact rendering --- application/controllers/NodeController.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/application/controllers/NodeController.php b/application/controllers/NodeController.php index d2dc8b6..2c7c444 100644 --- a/application/controllers/NodeController.php +++ b/application/controllers/NodeController.php @@ -4,6 +4,8 @@ namespace Icinga\Module\Businessprocess\Controllers; use Icinga\Module\Businessprocess\Renderer\Breadcrumb; use Icinga\Module\Businessprocess\Renderer\TileRenderer; +use Icinga\Module\Businessprocess\Simulation; +use Icinga\Module\Businessprocess\State\MonitoringState; use Icinga\Module\Businessprocess\Web\Controller; use Icinga\Module\Businessprocess\Web\Url; @@ -11,6 +13,7 @@ class NodeController extends Controller { public function impactAction() { + $this->setAutorefreshInterval(10); $content = $this->content(); $this->controls()->add( $this->singleTab($this->translate('Node Impact')) @@ -26,11 +29,18 @@ class NodeController extends Controller foreach ($config->getRootNodes() as $node) { $node->getState(); } + foreach ($config->getRootNodes() as $node) { + $node->clearState(); + } if (! $config->hasNode($name)) { continue; } + MonitoringState::apply($config); + $simulation = new Simulation($config, $this->session()); + $config->applySimulation($simulation); + foreach ($config->getNode($name)->getPaths() as $path) { $node = array_pop($path); $renderer = new TileRenderer($config, $config->getNode($node)); From c86c2fe1516f1c848becb67b9278d14bb4ca418b Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 17:33:29 +0100 Subject: [PATCH 254/256] DeleteNodeForm: fix business impact link --- application/forms/DeleteNodeForm.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/forms/DeleteNodeForm.php b/application/forms/DeleteNodeForm.php index d91f4d5..b09cd68 100644 --- a/application/forms/DeleteNodeForm.php +++ b/application/forms/DeleteNodeForm.php @@ -39,7 +39,7 @@ class DeleteNodeForm extends QuickForm $biLink = $view->qlink( $node->getAlias(), - 'director/node/impact', + 'businessprocess/node/impact', array('name' => $node->getName()), array('data-base-target' => '_next') ); From c083b117dd994324a63126f1fabbc91dd82dcf46 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 17:36:32 +0100 Subject: [PATCH 255/256] Modification: rename $bp to $config --- .../Businessprocess/Modification/NodeAction.php | 8 ++++---- .../Modification/NodeAddChildrenAction.php | 10 +++++----- .../Modification/NodeCreateAction.php | 6 +++--- .../Modification/NodeModifyAction.php | 10 +++++----- .../Modification/NodeRemoveAction.php | 14 +++++++------- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/library/Businessprocess/Modification/NodeAction.php b/library/Businessprocess/Modification/NodeAction.php index fc7ff00..c93f13a 100644 --- a/library/Businessprocess/Modification/NodeAction.php +++ b/library/Businessprocess/Modification/NodeAction.php @@ -42,18 +42,18 @@ abstract class NodeAction /** * Every NodeAction must be able to apply itself to a BusinessProcess * - * @param BpConfig $bp + * @param BpConfig $config * @return mixed */ - abstract public function applyTo(BpConfig $bp); + abstract public function applyTo(BpConfig $config); /** * Every NodeAction must be able to tell whether it could be applied to a BusinessProcess * - * @param BpConfig $bp + * @param BpConfig $config * @return bool */ - abstract public function appliesTo(BpConfig $bp); + abstract public function appliesTo(BpConfig $config); /** * The name of the node this modification applies to diff --git a/library/Businessprocess/Modification/NodeAddChildrenAction.php b/library/Businessprocess/Modification/NodeAddChildrenAction.php index c6e0444..8ccd08c 100644 --- a/library/Businessprocess/Modification/NodeAddChildrenAction.php +++ b/library/Businessprocess/Modification/NodeAddChildrenAction.php @@ -2,8 +2,8 @@ namespace Icinga\Module\Businessprocess\Modification; -use Icinga\Module\Businessprocess\BpNode; use Icinga\Module\Businessprocess\BpConfig; +use Icinga\Module\Businessprocess\BpNode; class NodeAddChildrenAction extends NodeAction { @@ -14,21 +14,21 @@ class NodeAddChildrenAction extends NodeAction /** * @inheritdoc */ - public function appliesTo(BpConfig $bp) + public function appliesTo(BpConfig $config) { $name = $this->getNodeName(); - if (! $bp->hasNode($name)) { + if (! $config->hasNode($name)) { return false; } - return $bp->getNode($name) instanceof BpNode; + return $config->getNode($name) instanceof BpNode; } /** * @inheritdoc */ - public function applyTo(BpConfig $bp) + public function applyTo(BpConfig $config) { /** @var BpNode $node */ if (! $this->hasNode()) { diff --git a/library/Businessprocess/Modification/NodeCreateAction.php b/library/Businessprocess/Modification/NodeCreateAction.php index ce4bdb1..c3bac80 100644 --- a/library/Businessprocess/Modification/NodeCreateAction.php +++ b/library/Businessprocess/Modification/NodeCreateAction.php @@ -70,15 +70,15 @@ class NodeCreateAction extends NodeAction /** * @inheritdoc */ - public function appliesTo(BpConfig $bp) + public function appliesTo(BpConfig $config) { - return ! $bp->hasNode($this->getNodeName()); + return ! $config->hasNode($this->getNodeName()); } /** * @inheritdoc */ - public function applyTo(BpConfig $bp) + public function applyTo(BpConfig $config) { $name = $this->getNodeName(); diff --git a/library/Businessprocess/Modification/NodeModifyAction.php b/library/Businessprocess/Modification/NodeModifyAction.php index ff15856..627f84b 100644 --- a/library/Businessprocess/Modification/NodeModifyAction.php +++ b/library/Businessprocess/Modification/NodeModifyAction.php @@ -43,15 +43,15 @@ class NodeModifyAction extends NodeAction /** * @inheritdoc */ - public function appliesTo(BpConfig $bp) + public function appliesTo(BpConfig $config) { $name = $this->getNodeName(); - if (! $bp->hasNode($name)) { + if (! $config->hasNode($name)) { return false; } - $node = $bp->getNode($name); + $node = $config->getNode($name); foreach ($this->properties as $key => $val) { $func = 'get' . ucfirst($key); @@ -66,9 +66,9 @@ class NodeModifyAction extends NodeAction /** * @inheritdoc */ - public function applyTo(BpConfig $bp) + public function applyTo(BpConfig $config) { - $node = $bp->getNode($this->getNodeName()); + $node = $config->getNode($this->getNodeName()); foreach ($this->properties as $key => $val) { $func = 'set' . ucfirst($key); diff --git a/library/Businessprocess/Modification/NodeRemoveAction.php b/library/Businessprocess/Modification/NodeRemoveAction.php index 0f9ffe3..29e17a9 100644 --- a/library/Businessprocess/Modification/NodeRemoveAction.php +++ b/library/Businessprocess/Modification/NodeRemoveAction.php @@ -38,29 +38,29 @@ class NodeRemoveAction extends NodeAction /** * @inheritdoc */ - public function appliesTo(BpConfig $bp) + public function appliesTo(BpConfig $config) { $parent = $this->getParentName(); if ($parent === null) { - return $bp->hasNode($this->getNodeName()); + return $config->hasNode($this->getNodeName()); } else { - return $bp->hasNode($this->getNodeName()) && $bp->hasNode($this->getParentName()) ; + return $config->hasNode($this->getNodeName()) && $config->hasNode($this->getParentName()) ; } } /** * @inheritdoc */ - public function applyTo(BpConfig $bp) + public function applyTo(BpConfig $config) { $parent = $this->getParentName(); if ($parent === null) { - $bp->removeNode($this->getNodeName()); + $config->removeNode($this->getNodeName()); } else { - $node = $bp->getNode($this->getNodeName()); + $node = $config->getNode($this->getNodeName()); $node->removeParent($parent); if (! $node->hasParents()) { - $bp->removeNode($this->getNodeName()); + $config->removeNode($this->getNodeName()); } } } From 5f6b35906dbc3c3c76efc767d4ab023a11b7a7c1 Mon Sep 17 00:00:00 2001 From: Thomas Gelf Date: Wed, 11 Jan 2017 17:38:19 +0100 Subject: [PATCH 256/256] AddNode: fix creating nested nodes fixes #13883 --- application/forms/AddNodeForm.php | 3 ++- library/Businessprocess/BpNode.php | 2 +- .../Modification/NodeAddChildrenAction.php | 19 ++++++++++++++----- .../Modification/NodeCreateAction.php | 11 ++++++----- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/application/forms/AddNodeForm.php b/application/forms/AddNodeForm.php index 4413421..e79bf36 100644 --- a/application/forms/AddNodeForm.php +++ b/application/forms/AddNodeForm.php @@ -383,11 +383,12 @@ class AddNodeForm extends QuickForm case 'new-process': $properties = $this->getValues(); unset($properties['name']); + $properties['parentName'] = $this->parent->getName(); $changes->createNode($this->getValue('name'), $properties); break; } - // Trigger session desctruction to make sure it get's stored. + // 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); diff --git a/library/Businessprocess/BpNode.php b/library/Businessprocess/BpNode.php index efa8999..cef6298 100644 --- a/library/Businessprocess/BpNode.php +++ b/library/Businessprocess/BpNode.php @@ -104,7 +104,7 @@ class BpNode extends Node $this->getChildren(); } - $name = (string) $node; + $name = $node->getName(); if (array_key_exists($name, $this->children)) { throw new ConfigurationError( 'Node "%s" has been defined more than once', diff --git a/library/Businessprocess/Modification/NodeAddChildrenAction.php b/library/Businessprocess/Modification/NodeAddChildrenAction.php index 8ccd08c..dfd2ac2 100644 --- a/library/Businessprocess/Modification/NodeAddChildrenAction.php +++ b/library/Businessprocess/Modification/NodeAddChildrenAction.php @@ -36,14 +36,23 @@ class NodeAddChildrenAction extends NodeAction // be a different action return $this; } - $node = $bp->getNode($this->getNodeName()); - $existing = $node->getChildNames(); + + $node = $config->getBpNode($this->getNodeName()); + foreach ($this->children as $name) { - if (! in_array($name, $existing)) { - $existing[] = $name; + if (! $config->hasNode($name)) { + if (strpos($name, ';') !== false) { + list($host, $service) = preg_split('/;/', $name, 2); + + if ($service === 'Hoststatus') { + $config->createHost($host); + } else { + $config->createService($host, $service); + } + } } + $node->addChild($config->getNode($name)); } - $node->setChildNames($existing); return $this; } diff --git a/library/Businessprocess/Modification/NodeCreateAction.php b/library/Businessprocess/Modification/NodeCreateAction.php index c3bac80..69dc77c 100644 --- a/library/Businessprocess/Modification/NodeCreateAction.php +++ b/library/Businessprocess/Modification/NodeCreateAction.php @@ -91,17 +91,18 @@ class NodeCreateAction extends NodeAction } else { $properties['child_names'] = array(); } - $node = new BpNode($bp, (object) $properties); + $node = new BpNode($config, (object) $properties); foreach ($this->getProperties() as $key => $val) { + if ($key === 'parentName') { + $config->getBpNode($val)->addChild($node); + continue; + } $func = 'set' . ucfirst($key); $node->$func($val); } - $bp->addNode($name, $node); - if ($this->hasParent()) { - $node->addParent($bp->getNode($this->getParentName())); - } + $config->addNode($name, $node); return $node; }