diff options
Diffstat (limited to '')
24 files changed, 1998 insertions, 0 deletions
diff --git a/library/Businessprocess/Web/Component/ActionBar.php b/library/Businessprocess/Web/Component/ActionBar.php new file mode 100644 index 0000000..94458dc --- /dev/null +++ b/library/Businessprocess/Web/Component/ActionBar.php @@ -0,0 +1,15 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Component; + +use ipl\Html\BaseHtmlElement; + +class ActionBar extends BaseHtmlElement +{ + protected $contentSeparator = ' '; + + /** @var string */ + protected $tag = 'div'; + + protected $defaultAttributes = array('class' => 'action-bar'); +} diff --git a/library/Businessprocess/Web/Component/BpDashboardTile.php b/library/Businessprocess/Web/Component/BpDashboardTile.php new file mode 100644 index 0000000..9a4a0f6 --- /dev/null +++ b/library/Businessprocess/Web/Component/BpDashboardTile.php @@ -0,0 +1,47 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Component; + +use Icinga\Module\Businessprocess\BpConfig; +use ipl\Html\BaseHtmlElement; +use ipl\Html\Html; +use ipl\Html\Text; +use ipl\Web\Url; +use ipl\Web\Widget\Icon; +use ipl\Web\Widget\Link; + +class BpDashboardTile extends BaseHtmlElement +{ + protected $tag = 'div'; + + protected $defaultAttributes = ['class' => 'dashboard-tile']; + + public function __construct(BpConfig $bp, $title, $description, $icon, $url, $urlParams = null, $attributes = null) + { + $this->add(Html::tag( + 'div', + ['class' => 'bp-link', 'data-base-target' => '_main'], + (new Link(new Icon($icon), Url::fromPath($url, $urlParams ?: []), $attributes)) + ->add(Html::tag('span', ['class' => 'header'], $title)) + ->add($description) + )); + + $tiles = Html::tag('div', ['class' => 'bp-root-tiles']); + + foreach ($bp->getChildren() as $node) { + $state = strtolower($node->getStateName()); + + $tiles->add(Html::tag( + 'a', + [ + 'href' => Url::fromPath($url, $urlParams ?: [])->with(['node' => $node->getName()]), + 'class' => "badge state-{$state}", + 'title' => $node->getAlias() + ], + Text::create(' ')->setEscaped() + )); + } + + $this->add($tiles); + } +} diff --git a/library/Businessprocess/Web/Component/Content.php b/library/Businessprocess/Web/Component/Content.php new file mode 100644 index 0000000..6d14197 --- /dev/null +++ b/library/Businessprocess/Web/Component/Content.php @@ -0,0 +1,14 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Component; + +use ipl\Html\BaseHtmlElement; + +class Content extends BaseHtmlElement +{ + protected $tag = 'div'; + + protected $contentSeparator = "\n"; + + protected $defaultAttributes = array('class' => 'content'); +} diff --git a/library/Businessprocess/Web/Component/Controls.php b/library/Businessprocess/Web/Component/Controls.php new file mode 100644 index 0000000..259cbbb --- /dev/null +++ b/library/Businessprocess/Web/Component/Controls.php @@ -0,0 +1,14 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Component; + +use ipl\Html\BaseHtmlElement; + +class Controls extends BaseHtmlElement +{ + protected $tag = 'div'; + + protected $contentSeparator = "\n"; + + protected $defaultAttributes = array('class' => 'controls'); +} diff --git a/library/Businessprocess/Web/Component/Dashboard.php b/library/Businessprocess/Web/Component/Dashboard.php new file mode 100644 index 0000000..d211772 --- /dev/null +++ b/library/Businessprocess/Web/Component/Dashboard.php @@ -0,0 +1,140 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Component; + +use Exception; +use Icinga\Application\Modules\Module; +use Icinga\Authentication\Auth; +use Icinga\Module\Businessprocess\BpConfig; +use Icinga\Module\Businessprocess\ProvidedHook\Icingadb\IcingadbSupport; +use Icinga\Module\Businessprocess\State\IcingaDbState; +use Icinga\Module\Businessprocess\State\MonitoringState; +use Icinga\Module\Businessprocess\Storage\Storage; +use ipl\Html\BaseHtmlElement; +use ipl\Html\Html; + +class Dashboard extends BaseHtmlElement +{ + /** @var string */ + protected $contentSeparator = "\n"; + + /** @var string */ + protected $tag = 'div'; + + protected $defaultAttributes = array( + 'class' => '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->listProcessNames(); + $this->add( + Html::tag('h1', null, mt('businessprocess', 'Welcome to your Business Process Overview')) + ); + $this->add(Html::tag( + 'p', + null, + mt( + 'businessprocess', + '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( + mt('businessprocess', 'Create'), + mt('businessprocess', 'Create a new Business Process configuration'), + 'plus', + 'businessprocess/process/create', + null, + array('class' => 'addnew') + ) + )->add( + new DashboardAction( + mt('businessprocess', 'Upload'), + mt('businessprocess', 'Upload an existing Business Process configuration'), + 'upload', + 'businessprocess/process/upload', + null, + array('class' => 'addnew') + ) + ); + } elseif (empty($processes)) { + $this->add( + Html::tag('div') + ->add(Html::tag('h1', null, mt('businessprocess', 'Not available'))) + ->add(Html::tag('p', null, mt( + 'businessprocess', + 'No Business Process has been defined for you' + ))) + ); + } + + foreach ($processes as $name) { + $meta = $storage->loadMetadata($name); + $title = $meta->get('Title'); + + if ($title === null) { + $title = $name; + } + + try { + $bp = $storage->loadProcess($name); + } catch (Exception $e) { + $this->add(new BpDashboardTile( + new BpConfig(), + $title, + sprintf(t('File %s has faulty config'), $name . '.conf'), + 'file-circle-xmark', + 'businessprocess/process/show', + ['config' => $name] + )); + + continue; + } + + if (Module::exists('icingadb') && + (! $bp->hasBackendName() && IcingadbSupport::useIcingaDbAsBackend()) + ) { + IcingaDbState::apply($bp); + } else { + MonitoringState::apply($bp); + } + + $this->add(new BpDashboardTile( + $bp, + $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..9bd3240 --- /dev/null +++ b/library/Businessprocess/Web/Component/DashboardAction.php @@ -0,0 +1,27 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Component; + +use Icinga\Web\Url; +use ipl\Html\BaseHtmlElement; +use ipl\Html\Html; +use ipl\Web\Widget\Icon; + +class DashboardAction extends BaseHtmlElement +{ + protected $tag = 'div'; + + protected $defaultAttributes = array('class' => 'action'); + + public function __construct($title, $description, $icon, $url, $urlParams = null, $attributes = null) + { + if (! isset($attributes['href'])) { + $attributes['href'] = Url::fromPath($url, $urlParams ?: []); + } + + $this->add(Html::tag('a', $attributes) + ->add(new Icon($icon)) + ->add(Html::tag('span', ['class' => 'header'], $title)) + ->add($description)); + } +} diff --git a/library/Businessprocess/Web/Component/RenderedProcessActionBar.php b/library/Businessprocess/Web/Component/RenderedProcessActionBar.php new file mode 100644 index 0000000..41fa0f8 --- /dev/null +++ b/library/Businessprocess/Web/Component/RenderedProcessActionBar.php @@ -0,0 +1,161 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Component; + +use Icinga\Authentication\Auth; +use Icinga\Module\Businessprocess\BpConfig; +use Icinga\Module\Businessprocess\Renderer\Renderer; +use Icinga\Module\Businessprocess\Renderer\TreeRenderer; +use Icinga\Web\Url; +use ipl\Html\Html; +use ipl\Web\Widget\Icon; + +class RenderedProcessActionBar extends ActionBar +{ + public function __construct(BpConfig $config, Renderer $renderer, Url $url) + { + $meta = $config->getMetadata(); + + if ($renderer instanceof TreeRenderer) { + $link = Html::tag( + 'a', + [ + 'href' => $url->with('mode', 'tile'), + 'title' => mt('businessprocess', 'Switch to Tile view') + ] + ); + } else { + $link = Html::tag( + 'a', + [ + 'href' => $url->with('mode', 'tree'), + 'title' => mt('businessprocess', 'Switch to Tree view') + ] + ); + } + + $link->add([ + new Icon('grip', ['class' => $renderer instanceof TreeRenderer ? null : 'active']), + new Icon('sitemap', ['class' => $renderer instanceof TreeRenderer ? 'active' : null]) + ]); + + $this->add( + Html::tag('div', ['class' => 'view-toggle']) + ->add(Html::tag('span', null, mt('businessprocess', 'View'))) + ->add($link) + ); + + $this->add(Html::tag( + 'a', + [ + 'data-base-target' => '_main', + 'href' => $url->with('showFullscreen', true), + 'title' => mt('businessprocess', 'Switch to fullscreen mode'), + ], + [ + new Icon('maximize'), + mt('businessprocess', 'Fullscreen') + ] + )); + + $hasChanges = $config->hasSimulations() || $config->hasBeenChanged(); + + if ($renderer->isLocked()) { + if (! $renderer->wantsRootNodes() && $renderer->rendersImportedNode()) { + $span = Html::tag('span', [ + 'class' => 'disabled', + 'title' => mt( + 'businessprocess', + 'Imported processes can only be changed in their original configuration' + ) + ]); + $span->add([new Icon('lock'), mt('businessprocess', 'Editing Locked')]); + $this->add($span); + } else { + $this->add(Html::tag( + 'a', + [ + 'href' => $url->with('unlocked', true), + 'title' => mt('businessprocess', 'Click to unlock editing for this process'), + ], + [ + new Icon('lock'), + mt('businessprocess', 'Unlock Editing') + ] + )); + } + } elseif (! $hasChanges) { + $this->add(Html::tag( + 'a', + [ + 'href' => $url->without('unlocked')->without('action'), + 'title' => mt('businessprocess', 'Click to lock editing for this process'), + ], + [ + new Icon('lock-open'), + mt('businessprocess', 'Lock Editing') + ] + )); + } + + if (($hasChanges || ! $renderer->isLocked()) && $meta->canModify()) { + if ($renderer->wantsRootNodes()) { + $this->add(Html::tag( + 'a', + [ + 'data-base-target' => '_next', + 'href' => Url::fromPath('businessprocess/process/config', $this->currentProcessParams($url)), + 'title' => mt('businessprocess', 'Modify this process'), + ], + [ + new Icon('wrench'), + mt('businessprocess', 'Config') + ] + )); + } else { + $this->add(Html::tag( + 'a', + [ + 'href' => $url->with([ + 'action' => 'edit', + 'editnode' => $url->getParam('node') + ])->getAbsoluteUrl(), + 'title' => mt('businessprocess', 'Modify this process'), + ], + [ + new Icon('wrench'), + mt('businessprocess', 'Config') + ] + )); + } + } + + if (($hasChanges || (! $renderer->isLocked())) && $meta->canModify()) { + $this->add(Html::tag( + 'a', + [ + 'href' => $url->with('action', 'add'), + 'title' => mt('businessprocess', 'Add a new business process node'), + 'class' => 'button-link' + ], + [ + new Icon('plus'), + mt('businessprocess', 'Add Node') + ] + )); + } + } + + protected function currentProcessParams(Url $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/Tabs.php b/library/Businessprocess/Web/Component/Tabs.php new file mode 100644 index 0000000..aaa444e --- /dev/null +++ b/library/Businessprocess/Web/Component/Tabs.php @@ -0,0 +1,9 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Component; + +use ipl\Html\ValidHtml; + +class Tabs extends WtfTabs implements ValidHtml +{ +} diff --git a/library/Businessprocess/Web/Component/WtfTabs.php b/library/Businessprocess/Web/Component/WtfTabs.php new file mode 100644 index 0000000..8f2250f --- /dev/null +++ b/library/Businessprocess/Web/Component/WtfTabs.php @@ -0,0 +1,22 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Component; + +use Icinga\Web\Widget\Tabs; + +/** + * Class WtfTabs + * + * TODO: Please remove this as soon as we drop support for PHP 5.3.x + * This works around https://bugs.php.net/bug.php?id=43200 and fixes + * https://github.com/Icinga/icingaweb2-module-businessprocess/issues/81 + * + * @package Icinga\Module\Businessprocess\Web\Component + */ +class WtfTabs extends Tabs +{ + public function render() + { + return parent::render(); + } +} diff --git a/library/Businessprocess/Web/Controller.php b/library/Businessprocess/Web/Controller.php new file mode 100644 index 0000000..43200cc --- /dev/null +++ b/library/Businessprocess/Web/Controller.php @@ -0,0 +1,262 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web; + +use Icinga\Application\Icinga; +use Icinga\Module\Businessprocess\BpConfig; +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\Notification; +use Icinga\Web\View; +use ipl\Html\Html; +use ipl\Web\Compat\CompatController; + +class Controller extends CompatController +{ + /** @var View */ + public $view; + + /** @var BpConfig */ + protected $bp; + + /** @var Tabs */ + protected $mytabs; + + /** @var Storage */ + private $storage; + + /** @var bool */ + protected $showFullscreen; + + /** @var Url */ + private $url; + + public function init() + { + $m = Icinga::app()->getModuleManager(); + if (! $m->hasLoaded('monitoring') && $m->hasInstalled('monitoring')) { + $m->loadModule('monitoring'); + } + $this->controls(); + $this->content(); + $this->url(); + $this->view->showFullscreen + = $this->showFullscreen + = (bool) $this->_helper->layout()->showFullscreen; + + $this->setViewScript('default'); + } + + /** + * @return Url + */ + protected function url() + { + if ($this->url === null) { + $this->url = Url::fromPath( + $this->getRequest()->getUrl()->getPath() + )->setParams($this->getRequest()->getUrl()->getParams()); + } + + return $this->url; + } + + /** + * @return ActionBar + */ + protected function actions() + { + if ($this->view->actions === null) { + $this->view->actions = new ActionBar(); + } + + return $this->view->actions; + } + + /** + * @return Controls + */ + protected function controls() + { + if ($this->view->controls === null) { + $controls = $this->view->controls = new Controls(); + if ($this->view->compact) { + $controls->getAttributes()->add('class', 'compact'); + } + } + + return $this->view->controls; + } + + /** + * @return Content + */ + protected function content() + { + if ($this->view->content === null) { + $content = $this->view->content = new Content(); + if ($this->view->compact) { + $content->getAttributes()->add('class', 'compact'); + } + } + + return $this->view->content; + } + + /** + * @param $label + * @return Tabs + */ + protected function singleTab($label) + { + return $this->tabs()->add( + 'tab', + array( + 'label' => $label, + 'url' => $this->getRequest()->getUrl() + ) + )->activate('tab'); + } + + /** + * @return Tabs + */ + protected function defaultTab() + { + 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'); + } + + /** + * @return Tabs + */ + protected function tabs() + { + // Todo: do not add to view once all of them render controls() + if ($this->mytabs === null) { + $tabs = new Tabs(); + //$this->controls()->add($tabs); + $this->mytabs = $tabs; + } + + return $this->mytabs; + } + + protected function session() + { + return $this->Window()->getSessionNamespace('businessprocess'); + } + + protected function setViewScript($name) + { + $this->_helper->viewRenderer->setNoController(true); + $this->_helper->viewRenderer->setScriptAction($name); + return $this; + } + + protected function addTitle($title) + { + $args = func_get_args(); + array_shift($args); + $this->view->title = vsprintf($title, $args); + $this->controls()->add(Html::tag('h1', null, $this->view->title)); + return $this; + } + + protected function loadModifiedBpConfig() + { + $bp = $this->loadBpConfig(); + $changes = ProcessChanges::construct($bp, $this->session()); + if ($this->params->get('dismissChanges')) { + Notification::success( + sprintf( + $this->translate('%d pending change(s) have been dropped'), + $changes->count() + ) + ); + $changes->clear(); + $this->redirectNow($this->url()->without('dismissChanges')->without('unlocked')); + } + $bp->applyChanges($changes); + return $bp; + } + + protected function doNotRender() + { + $this->_helper->layout()->disableLayout(); + $this->_helper->viewRenderer->setNoRender(true); + return $this; + } + + protected function loadBpConfig() + { + $name = $this->params->get('config'); + /** @var LegacyStorage $storage */ + $storage = $this->storage(); + + 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]); + } else { + $bp = $storage->loadProcess($name); + } + + // allow URL parameter to override configured state type + if (null !== ($stateType = $this->params->get('state_type'))) { + if ($stateType === 'soft') { + $bp->useSoftStates(); + } + if ($stateType === 'hard') { + $bp->useHardStates(); + } + } + + $this->view->bpconfig = $this->bp = $bp; + $this->view->configName = $bp->getName(); + + return $bp; + } + + public function loadForm($name) + { + return FormLoader::load($name, $this->Module()); + } + + /** + * @return LegacyStorage + */ + protected function storage() + { + if ($this->storage === null) { + $this->storage = LegacyStorage::getInstance(); + } + + return $this->storage; + } +} diff --git a/library/Businessprocess/Web/FakeRequest.php b/library/Businessprocess/Web/FakeRequest.php new file mode 100644 index 0000000..4e54117 --- /dev/null +++ b/library/Businessprocess/Web/FakeRequest.php @@ -0,0 +1,26 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web; + +use Icinga\Exception\ProgrammingError; +use Icinga\Web\Request; + +class FakeRequest extends Request +{ + /** @var string */ + private static $baseUrl; + + public static function setConfiguredBaseUrl($url) + { + self::$baseUrl = $url; + } + + public function getBaseUrl($raw = false) + { + if (self::$baseUrl === null) { + throw new ProgrammingError('Cannot determine base URL on CLI if not configured'); + } else { + return self::$baseUrl; + } + } +} diff --git a/library/Businessprocess/Web/Form/BpConfigBaseForm.php b/library/Businessprocess/Web/Form/BpConfigBaseForm.php new file mode 100644 index 0000000..5ccdf06 --- /dev/null +++ b/library/Businessprocess/Web/Form/BpConfigBaseForm.php @@ -0,0 +1,135 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Form; + +use Icinga\Application\Config; +use Icinga\Application\Icinga; +use Icinga\Authentication\Auth; +use Icinga\Module\Businessprocess\BpConfig; +use Icinga\Module\Businessprocess\Storage\Storage; +use Icinga\Module\Monitoring\Backend\MonitoringBackend; +use Icinga\Web\Session\SessionNamespace; +use ipl\Sql\Connection as IcingaDbConnection; + +abstract class BpConfigBaseForm extends QuickForm +{ + /** @var Storage */ + protected $storage; + + /** @var BpConfig */ + protected $bp; + + /** @var MonitoringBackend|IcingaDbConnection*/ + protected $backend; + + /** @var SessionNamespace */ + protected $session; + + protected function listAvailableBackends() + { + $keys = []; + $moduleManager = Icinga::app()->getModuleManager(); + if ($moduleManager->hasEnabled('monitoring')) { + $keys = array_keys(Config::module('monitoring', 'backends')->toArray()); + $keys = array_combine($keys, $keys); + } + + return $keys; + } + + /** + * Set the storage to use + * + * @param Storage $storage + * + * @return $this + */ + public function setStorage(Storage $storage): self + { + $this->storage = $storage; + + return $this; + } + + /** + * Set the config to use + * + * @param BpConfig $config + * + * @return $this + */ + public function setProcess(BpConfig $config): self + { + $this->bp = $config; + $this->setBackend($config->getBackend()); + + return $this; + } + + /** + * Set the backend to use + * + * @param MonitoringBackend|IcingaDbConnection $backend + * + * @return $this + */ + public function setBackend($backend): self + { + $this->backend = $backend; + + return $this; + } + + /** + * Set the session namespace to use + * + * @param SessionNamespace $session + * + * @return $this + */ + public function setSession(SessionNamespace $session): self + { + $this->session = $session; + + return $this; + } + + protected function prepareMetadata(BpConfig $config) + { + $meta = $config->getMetadata(); + $auth = Auth::getInstance(); + $meta->set('Owner', $auth->getUser()->getUsername()); + + if ($auth->hasPermission('businessprocess/showall')) { + return true; + } + + $prefixes = $auth->getRestrictions('businessprocess/prefix'); + if (! empty($prefixes) && ! $meta->nameIsPrefixedWithOneOf($prefixes)) { + if (count($prefixes) === 1) { + $this->getElement('name')->addError(sprintf( + $this->translate('Please prefix the name with "%s"'), + current($prefixes) + )); + } else { + $this->getElement('name')->addError(sprintf( + $this->translate('Please prefix the name with one of "%s"'), + implode('", "', $prefixes) + )); + } + + return false; + } + + return true; + } + + protected function setPreferredDecorators() + { + parent::setPreferredDecorators(); + + $this->setAttrib('class', $this->getAttrib('class') . ' bp-form'); + + return $this; + } +} diff --git a/library/Businessprocess/Web/Form/CsrfToken.php b/library/Businessprocess/Web/Form/CsrfToken.php new file mode 100644 index 0000000..9eb24ef --- /dev/null +++ b/library/Businessprocess/Web/Form/CsrfToken.php @@ -0,0 +1,53 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Form; + +class CsrfToken +{ + /** + * Check whether the given token is valid + * + * @param string $token Token + * + * @return bool + */ + public static function isValid($token) + { + if (strpos($token, '|') === false) { + return false; + } + + list($seed, $token) = explode('|', $token); + + if (!is_numeric($seed)) { + return false; + } + + return $token === hash('sha256', self::getSessionId() . $seed); + } + + /** + * Create a new token + * + * @return string + */ + public static function generate() + { + $seed = mt_rand(); + $token = hash('sha256', self::getSessionId() . $seed); + + return sprintf('%s|%s', $seed, $token); + } + + /** + * Get current session id + * + * TODO: we should do this through our App or Session object + * + * @return string + */ + protected static function getSessionId() + { + return session_id(); + } +} diff --git a/library/Businessprocess/Web/Form/Element/Checkbox.php b/library/Businessprocess/Web/Form/Element/Checkbox.php new file mode 100644 index 0000000..7975b82 --- /dev/null +++ b/library/Businessprocess/Web/Form/Element/Checkbox.php @@ -0,0 +1,8 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Form\Element; + +class Checkbox extends \Icinga\Web\Form\Element\Checkbox +{ + +} 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 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Form\Element; + +use Zend_Form_Element_Xhtml; + +class FormElement extends Zend_Form_Element_Xhtml +{ +} diff --git a/library/Businessprocess/Web/Form/Element/IplStateOverrides.php b/library/Businessprocess/Web/Form/Element/IplStateOverrides.php new file mode 100644 index 0000000..5b9ea16 --- /dev/null +++ b/library/Businessprocess/Web/Form/Element/IplStateOverrides.php @@ -0,0 +1,75 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Form\Element; + +use ipl\Html\Attributes; +use ipl\Html\FormElement\FieldsetElement; + +class IplStateOverrides extends FieldsetElement +{ + /** @var array */ + protected $options = []; + + /** + * Set the options show + * + * @param array $options + * + * @return $this + */ + public function setOptions(array $options): self + { + $this->options = $options; + + return $this; + } + + /** + * Get the options to show + * + * @return array + */ + public function getOptions(): array + { + return $this->options; + } + + public function getValues() + { + $cleanedValue = parent::getValues(); + + if (! empty($cleanedValue)) { + foreach ($cleanedValue as $from => $to) { + if ((int) $from === (int) $to) { + unset($cleanedValue[$from]); + } + } + } + + return $cleanedValue; + } + + protected function assemble() + { + $states = $this->getOptions(); + foreach ($states as $state => $label) { + if ($state === 0) { + continue; + } + + $this->addElement('select', $state, [ + 'label' => $label, + 'value' => $state, + 'options' => [$state => $this->translate('Keep actual state')] + $states + ]); + } + } + + protected function registerAttributeCallbacks(Attributes $attributes) + { + parent::registerAttributeCallbacks($attributes); + + $this->getAttributes() + ->registerAttributeCallback('options', null, [$this, 'setOptions']); + } +} diff --git a/library/Businessprocess/Web/Form/Element/SimpleNote.php b/library/Businessprocess/Web/Form/Element/SimpleNote.php new file mode 100644 index 0000000..9f757f2 --- /dev/null +++ b/library/Businessprocess/Web/Form/Element/SimpleNote.php @@ -0,0 +1,22 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Form\Element; + +class SimpleNote extends FormElement +{ + public $helper = 'formSimpleNote'; + + /** + * Always ignore this element + * @codingStandardsIgnoreStart + * + * @var boolean + */ + protected $_ignore = true; + // @codingStandardsIgnoreEnd + + public function isValid($value, $context = null) + { + return true; + } +} diff --git a/library/Businessprocess/Web/Form/FormLoader.php b/library/Businessprocess/Web/Form/FormLoader.php new file mode 100644 index 0000000..0cc5389 --- /dev/null +++ b/library/Businessprocess/Web/Form/FormLoader.php @@ -0,0 +1,39 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Form; + +use Icinga\Application\Icinga; +use Icinga\Application\Modules\Module; +use Icinga\Exception\ProgrammingError; + +class FormLoader +{ + public static function load($name, Module $module = null) + { + if ($module === null) { + $basedir = Icinga::app()->getApplicationDir('forms'); + $ns = '\\Icinga\\Web\\Forms\\'; + } else { + $basedir = $module->getFormDir(); + $ns = '\\Icinga\\Module\\' . ucfirst($module->getName()) . '\\Forms\\'; + } + + $file = null; + if (preg_match('~^[a-z0-9/]+$~i', $name)) { + $parts = preg_split('~/~', $name); + $class = ucfirst(array_pop($parts)) . 'Form'; + $file = sprintf('%s/%s/%s.php', rtrim($basedir, '/'), implode('/', $parts), $class); + if (file_exists($file)) { + require_once($file); + $class = $ns . $class; + $options = array(); + if ($module !== null) { + $options['icingaModule'] = $module; + } + + return new $class($options); + } + } + throw new ProgrammingError(sprintf('Cannot load %s (%s), no such form', $name, $file)); + } +} diff --git a/library/Businessprocess/Web/Form/QuickBaseForm.php b/library/Businessprocess/Web/Form/QuickBaseForm.php new file mode 100644 index 0000000..36d134f --- /dev/null +++ b/library/Businessprocess/Web/Form/QuickBaseForm.php @@ -0,0 +1,166 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Form; + +use Icinga\Application\Icinga; +use Icinga\Application\Modules\Module; +use ipl\Html\ValidHtml; +use Zend_Form; + +abstract class QuickBaseForm extends Zend_Form implements ValidHtml +{ + /** + * The Icinga module this form belongs to. Usually only set if the + * form is initialized through the FormLoader + * + * @var ?Module + */ + protected $icingaModule; + + protected $icingaModuleName; + + private $hintCount = 0; + + public function __construct($options = null) + { + $this->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('<div class="hint">' . $html . '</div>', $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)) { + $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 new file mode 100644 index 0000000..cb4d287 --- /dev/null +++ b/library/Businessprocess/Web/Form/QuickForm.php @@ -0,0 +1,514 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Form; + +use Icinga\Application\Icinga; +use Icinga\Application\Web; +use Icinga\Exception\ProgrammingError; +use Icinga\Web\Notification; +use Icinga\Web\Request; +use Icinga\Web\Response; +use Icinga\Web\Url; +use Exception; + +/** + * QuickForm wants to be a base class for simple forms + */ +abstract class QuickForm extends QuickBaseForm +{ + const ID = '__FORM_NAME'; + + const CSRF = '__FORM_CSRF'; + + /** + * The name of this form + */ + protected $formName; + + /** + * Whether the form has been sent + */ + protected $hasBeenSent; + + /** + * Whether the form has been sent + */ + protected $hasBeenSubmitted; + + /** + * The submit caption, element - still tbd + */ + // protected $submit; + + /** + * Our request + */ + protected $request; + + /** + * @var ?Url + */ + protected $successUrl; + + protected $successMessage; + + protected $submitLabel; + + protected $submitButtonName; + + protected $deleteButtonName; + + protected $fakeSubmitButtonName; + + /** + * Whether form elements have already been created + */ + protected $didSetup = false; + + protected $isApiRequest = false; + + public function __construct($options = null) + { + parent::__construct($options); + + $this->setMethod('post'); + $this->getActionFromRequest() + ->createIdElement() + ->regenerateCsrfToken() + ->setPreferredDecorators(); + } + + protected function getActionFromRequest() + { + $this->setAction(Url::fromRequest()); + return $this; + } + + protected function setPreferredDecorators() + { + $this->setAttrib('class', 'autofocus icinga-controls'); + $this->setDecorators( + array( + 'Description', + array('FormErrors', array('onlyCustomFormErrors' => true)), + 'FormElements', + 'Form' + ) + ); + + return $this; + } + + protected function addSubmitButtonIfSet() + { + 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 + ); + $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) + { + $request = $this->getRequest(); + if ($request->isPost() && $this->hasBeenSent()) { + return $request->getPost($name); + } else { + return $default; + } + } + + public function getSubmitLabel() + { + if ($this->submitLabel === null) { + return $this->translate('Submit'); + } + + return $this->submitLabel; + } + + public function setSubmitLabel($label) + { + $this->submitLabel = $label; + return $this; + } + + public function setApiRequest($isApiRequest = true) + { + $this->isApiRequest = $isApiRequest; + return $this; + } + + public function isApiRequest() + { + return $this->isApiRequest; + } + + public function regenerateCsrfToken() + { + if (! $element = $this->getElement(self::CSRF)) { + $this->addHidden(self::CSRF, CsrfToken::generate()); + $element = $this->getElement(self::CSRF); + } + $element->setIgnore(true); + + return $this; + } + + public function removeCsrfToken() + { + $this->removeElement(self::CSRF); + return $this; + } + + 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; + } + + protected function beforeSetup() + { + } + + public function setup() + { + } + + protected function onSetup() + { + } + + /** + * @param $action string|Url + * @return $this + */ + public function setAction($action) + { + if ($action instanceof Url) { + $action = $action->getAbsoluteUrl('&'); + } + + return parent::setAction($action); + } + + public function hasBeenSubmitted() + { + if ($this->hasBeenSubmitted === null) { + $req = $this->getRequest(); + if ($req->isPost()) { + 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; + } + } + + 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()) + { + } + + public function prepareElements() + { + if (! $this->didSetup) { + $this->beforeSetup(); + $this->setup(); + $this->addSubmitButtonIfSet(); + $this->onSetup(); + $this->didSetup = true; + } + + return $this; + } + + public function handleRequest(Request $request = null) + { + if ($request === null) { + $request = $this->getRequest(); + } else { + $this->setRequest($request); + } + + $this->prepareElements(); + + if ($this->hasBeenSent()) { + $post = $request->getPost(); + if ($this->hasBeenSubmitted()) { + $this->beforeValidation($post); + if ($this->isValid($post)) { + try { + $this->onSuccess(); + } catch (Exception $e) { + $this->addException($e); + $this->onFailure(); + } + } else { + $this->onFailure(); + } + } else { + $this->setDefaults($post); + } + } else { + // Well... + } + + return $this; + } + + public function addException(Exception $e, $elementName = null) + { + $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 { + $this->addError($msg); + } + } + + public function onSuccess() + { + $this->redirectOnSuccess(); + } + + public function setSuccessMessage($message) + { + $this->successMessage = $message; + return $this; + } + + public function getSuccessMessage($message = null) + { + if ($message !== null) { + return $message; + } + if ($this->successMessage === null) { + return t('Form has successfully been sent'); + } + return $this->successMessage; + } + + 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); + } + + public function onFailure() + { + } + + public function notifySuccess($message = null) + { + if ($message === null) { + $message = t('Form has successfully been sent'); + } + Notification::success($message); + return $this; + } + + public function notifyError($message) + { + Notification::error($message); + return $this; + } + + protected function redirectAndExit($url) + { + /** @var Web $app */ + $app = Icinga::app(); + /** @var Response $response */ + $response = $app->getFrontController()->getResponse(); + $response->redirectAndExit($url); + } + + protected function setHttpResponseCode($code) + { + /** @var Web $app */ + $app = Icinga::app(); + $app->getFrontController()->getResponse()->setHttpResponseCode($code); + return $this; + } + + protected function onRequest() + { + } + + public function setRequest(Request $request) + { + if ($this->request !== null) { + throw new ProgrammingError('Unable to set request twice'); + } + + $this->request = $request; + $this->prepareElements(); + $this->onRequest(); + return $this; + } + + /** + * @return Request + */ + public function getRequest() + { + if ($this->request === null) { + /** @var Web $app */ + $app = Icinga::app(); + /** @var Request $request */ + $request = $app->getFrontController()->getRequest(); + $this->setRequest($request); + } + return $this->request; + } + + public function hasBeenSent() + { + if ($this->hasBeenSent === null) { + if ($this->request === null) { + /** @var Web $app */ + $app = Icinga::app(); + $req = $app->getFrontController()->getRequest(); + } else { + $req = $this->request; + } + + /** @var Request $req */ + if ($req->isPost()) { + $post = $req->getPost(); + $this->hasBeenSent = array_key_exists(self::ID, $post) && + $post[self::ID] === $this->getName(); + } else { + $this->hasBeenSent = false; + } + } + + return $this->hasBeenSent; + } + + protected function detectName() + { + if ($this->formName !== null) { + $this->setName($this->formName); + } else { + $this->setName(get_class($this)); + } + } +} diff --git a/library/Businessprocess/Web/Form/Validator/HostServiceTermValidator.php b/library/Businessprocess/Web/Form/Validator/HostServiceTermValidator.php new file mode 100644 index 0000000..58249f7 --- /dev/null +++ b/library/Businessprocess/Web/Form/Validator/HostServiceTermValidator.php @@ -0,0 +1,96 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Form\Validator; + +use Icinga\Module\Businessprocess\BpConfig; +use Icinga\Module\Businessprocess\BpNode; +use Icinga\Module\Businessprocess\ServiceNode; +use Icinga\Module\Businessprocess\State\IcingaDbState; +use Icinga\Module\Businessprocess\State\MonitoringState; +use Icinga\Module\Monitoring\Backend\MonitoringBackend; +use ipl\I18n\Translation; +use ipl\Validator\BaseValidator; +use ipl\Web\FormElement\TermInput\Term; +use LogicException; + +class HostServiceTermValidator extends BaseValidator +{ + use Translation; + + /** @var ?BpNode */ + protected $parent; + + /** + * Set the affected process + * + * @param BpNode $parent + * + * @return $this + */ + public function setParent(BpNode $parent): self + { + $this->parent = $parent; + + return $this; + } + + public function isValid($terms) + { + if ($this->parent === null) { + throw new LogicException('Missing parent process. Cannot validate terms.'); + } + + if (! is_array($terms)) { + $terms = [$terms]; + } + + $isValid = true; + $testConfig = new BpConfig(); + + foreach ($terms as $term) { + /** @var Term $term */ + [$hostName, $serviceName] = BpConfig::splitNodeName($term->getSearchValue()); + if ($serviceName !== null && $serviceName !== 'Hoststatus') { + $node = $testConfig->createService($hostName, $serviceName); + } else { + $node = $testConfig->createHost($hostName); + if ($serviceName === null) { + $term->setSearchValue(BpConfig::joinNodeName($hostName, 'Hoststatus')); + } + } + + if ($this->parent->hasChild($term->getSearchValue())) { + $term->setMessage($this->translate('Already defined in this process')); + $isValid = false; + } else { + $testConfig->getNode('__unbound__') + ->addChild($node); + } + } + + if ($this->parent->getBpConfig()->getBackend() instanceof MonitoringBackend) { + MonitoringState::apply($testConfig); + } else { + IcingaDbState::apply($testConfig); + } + + foreach ($terms as $term) { + /** @var Term $term */ + $node = $testConfig->getNode($term->getSearchValue()); + if ($node->isMissing()) { + if ($node instanceof ServiceNode) { + $term->setMessage($this->translate('Service not found')); + } else { + $term->setMessage($this->translate('Host not found')); + } + + $isValid = false; + } else { + $term->setLabel($node->getAlias()); + $term->setClass($node->getObjectClassName()); + } + } + + return $isValid; + } +} diff --git a/library/Businessprocess/Web/Navigation/Renderer/ProcessProblemsBadge.php b/library/Businessprocess/Web/Navigation/Renderer/ProcessProblemsBadge.php new file mode 100644 index 0000000..575dc5e --- /dev/null +++ b/library/Businessprocess/Web/Navigation/Renderer/ProcessProblemsBadge.php @@ -0,0 +1,59 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Navigation\Renderer; + +use Icinga\Module\Businessprocess\Storage\LegacyStorage; +use Icinga\Web\Navigation\Renderer\BadgeNavigationItemRenderer; + +class ProcessProblemsBadge extends BadgeNavigationItemRenderer +{ + /** + * Cached count + * + * @var int + */ + protected $count; + + /** @var string */ + private $bpConfigName; + + public function getCount() + { + $count = 0; + if ($this->count === null) { + $storage = LegacyStorage::getInstance(); + $bp = $storage->loadProcess($this->getBpConfigName()); + foreach ($bp->getRootNodes() as $rootNode) { + if (! $rootNode->isEmpty() && + $rootNode->getState() !== $rootNode::ICINGA_PENDING + && $rootNode->hasProblems()) { + $count++; + } + } + + $this->count = $count; + $this->setState(self::STATE_CRITICAL); + } + + if ($count) { + $this->setTitle(sprintf( + tp('One unhandled root node critical', '%d unhandled root nodes critical', $count), + $count + )); + } + + return $this->count; + } + + public function setBpConfigName($bpConfigName) + { + $this->bpConfigName = $bpConfigName; + + return $this; + } + + public function getBpConfigName() + { + return $this->bpConfigName; + } +} diff --git a/library/Businessprocess/Web/Navigation/Renderer/ProcessesProblemsBadge.php b/library/Businessprocess/Web/Navigation/Renderer/ProcessesProblemsBadge.php new file mode 100644 index 0000000..dd419a2 --- /dev/null +++ b/library/Businessprocess/Web/Navigation/Renderer/ProcessesProblemsBadge.php @@ -0,0 +1,53 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web\Navigation\Renderer; + +use Icinga\Application\Modules\Module; +use Icinga\Module\Businessprocess\ProvidedHook\Icingadb\IcingadbSupport; +use Icinga\Module\Businessprocess\State\IcingaDbState; +use Icinga\Module\Businessprocess\State\MonitoringState; +use Icinga\Module\Businessprocess\Storage\LegacyStorage; +use Icinga\Web\Navigation\Renderer\BadgeNavigationItemRenderer; + +class ProcessesProblemsBadge extends BadgeNavigationItemRenderer +{ + /** + * Cached count + * + * @var int + */ + protected $count; + + public function getCount() + { + if ($this->count === null) { + $storage = LegacyStorage::getInstance(); + $count = 0; + + foreach ($storage->listProcessNames() as $processName) { + $bp = $storage->loadProcess($processName); + if (Module::exists('icingadb') && + (! $bp->hasBackendName() && IcingadbSupport::useIcingaDbAsBackend()) + ) { + IcingaDbState::apply($bp); + } else { + MonitoringState::apply($bp); + } + + foreach ($bp->getRootNodes() as $rootNode) { + if (! $rootNode->isEmpty() && + $rootNode->getState() !== $rootNode::ICINGA_PENDING + && $rootNode->hasProblems()) { + $count++; + break; + } + } + } + + $this->count = $count; + $this->setState(self::STATE_CRITICAL); + } + + return $this->count; + } +} diff --git a/library/Businessprocess/Web/Url.php b/library/Businessprocess/Web/Url.php new file mode 100644 index 0000000..92b1e85 --- /dev/null +++ b/library/Businessprocess/Web/Url.php @@ -0,0 +1,32 @@ +<?php + +namespace Icinga\Module\Businessprocess\Web; + +use Icinga\Application\Icinga; +use Icinga\Application\Web; +use Icinga\Web\Request; +use Icinga\Web\Url as WebUrl; + +/** + * Class Url + * + * The main purpose of this class is to get unit tests running on CLI + * + * @package Icinga\Module\Businessprocess\Web + */ +class Url extends WebUrl +{ + /** + * @return FakeRequest|Request + */ + protected static function getRequest() + { + $app = Icinga::app(); + if ($app->isCli()) { + return new FakeRequest(); + } + + /** @var Web $app */ + return $app->getRequest(); + } +} |