diff options
Diffstat (limited to 'application/controllers/ProcessController.php')
-rw-r--r-- | application/controllers/ProcessController.php | 612 |
1 files changed, 612 insertions, 0 deletions
diff --git a/application/controllers/ProcessController.php b/application/controllers/ProcessController.php new file mode 100644 index 0000000..475826f --- /dev/null +++ b/application/controllers/ProcessController.php @@ -0,0 +1,612 @@ +<?php + +namespace Icinga\Module\Businessprocess\Controllers; + +use Icinga\Application\Modules\Module; +use Icinga\Date\DateFormatter; +use Icinga\Module\Businessprocess\BpConfig; +use Icinga\Module\Businessprocess\BpNode; +use Icinga\Module\Businessprocess\Node; +use Icinga\Module\Businessprocess\ProvidedHook\Icingadb\IcingadbSupport; +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\State\IcingaDbState; +use Icinga\Module\Businessprocess\State\MonitoringState; +use Icinga\Module\Businessprocess\Storage\ConfigDiff; +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; +use Icinga\Module\Businessprocess\Web\Controller; +use Icinga\Util\Json; +use Icinga\Web\Notification; +use Icinga\Web\Url; +use Icinga\Web\Widget\Tabextension\DashboardAction; +use Icinga\Web\Widget\Tabextension\OutputFormat; +use ipl\Html\Html; +use ipl\Html\HtmlString; + +class ProcessController extends Controller +{ + /** @var Renderer */ + protected $renderer; + + /** + * Create a new Business Process Configuration + */ + public function createAction() + { + $this->assertPermission('businessprocess/create'); + + $title = $this->translate('Create a new Business Process'); + $this->setTitle($title); + $this->controls() + ->add($this->tabsForCreate()->activate('create')) + ->add(Html::tag('h1', null, $title)); + + $this->content()->add( + $this->loadForm('bpConfig') + ->setStorage($this->storage()) + ->setSuccessUrl('businessprocess/process/show') + ->handleRequest() + ); + } + + /** + * Upload an existing Business Process Configuration + */ + public function uploadAction() + { + $this->assertPermission('businessprocess/create'); + + $title = $this->translate('Upload a Business Process Config file'); + $this->setTitle($title); + $this->controls() + ->add($this->tabsForCreate()->activate('upload')) + ->add(Html::tag('h1', null, $title)); + + $this->content()->add( + $this->loadForm('BpUpload') + ->setStorage($this->storage()) + ->setSuccessUrl('businessprocess/process/show') + ->handleRequest() + ); + } + + /** + * Show a business process + */ + public function showAction() + { + $bp = $this->loadModifiedBpConfig(); + $node = $this->getNode($bp); + + if (Module::exists('icingadb') && + (! $bp->hasBackendName() && IcingadbSupport::useIcingaDbAsBackend()) + ) { + IcingaDbState::apply($bp); + } else { + MonitoringState::apply($bp); + } + + $this->handleSimulations($bp); + + $this->setTitle($this->translate('Business Process "%s"'), $bp->getTitle()); + + $renderer = $this->prepareRenderer($bp, $node); + + if (! $this->showFullscreen && ($node === null || ! $renderer->rendersImportedNode())) { + if ($this->params->get('unlocked')) { + $renderer->unlock(); + } + + if ($bp->isEmpty() && $renderer->isLocked()) { + $this->redirectNow($this->url()->with('unlocked', true)); + } + } + + $this->handleFormatRequest($bp, $node); + + $this->prepareControls($bp, $renderer); + + $this->tabs()->extend(new OutputFormat()); + + $missing = $bp->getMissingChildren(); + if (! empty($missing)) { + if (($count = count($missing)) > 10) { + $missing = array_slice($missing, 0, 10); + $missing[] = '...'; + } + $bp->addError('There are %d missing nodes: %s', $count, implode(', ', $missing)); + } + $this->content()->add($this->showHints($bp)); + $this->content()->add($this->showWarnings($bp)); + $this->content()->add($this->showErrors($bp)); + $this->content()->add($renderer); + $this->loadActionForm($bp, $node); + $this->setDynamicAutorefresh(); + } + + protected function prepareControls($bp, $renderer) + { + $controls = $this->controls(); + + if ($this->showFullscreen) { + $controls->getAttributes()->add('class', 'want-fullscreen'); + $controls->add(Html::tag( + 'a', + [ + 'href' => $this->url()->without('showFullscreen')->without('view'), + 'title' => $this->translate('Leave full screen and switch back to normal mode'), + 'style' => 'float: right' + ], + Html::tag('i', ['class' => 'icon icon-resize-small']) + )); + } + + if (! ($this->showFullscreen || $this->view->compact)) { + $controls->add($this->getProcessTabs($bp, $renderer)); + $controls->getAttributes()->add('class', 'separated'); + } + + $controls->add(Breadcrumb::create(clone $renderer)); + if (! $this->showFullscreen && ! $this->view->compact) { + $controls->add( + new RenderedProcessActionBar($bp, $renderer, $this->Auth(), $this->url()) + ); + } + } + + protected function getNode(BpConfig $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') === 'tree') { + $renderer = new TreeRenderer($bp, $node); + } else { + $renderer = new TileRenderer($bp, $node); + } + $renderer->setUrl($this->url()) + ->setPath($this->params->getValues('path')); + + $this->renderer = $renderer; + } + + return $this->renderer; + } + + protected function getProcessTabs(BpConfig $bp, Renderer $renderer) + { + $tabs = $this->singleTab($bp->getTitle()); + if ($renderer->isLocked()) { + $tabs->extend(new DashboardAction()); + } + + return $tabs; + } + + protected function handleSimulations(BpConfig $bp) + { + $simulation = Simulation::fromSession($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(BpConfig $bp, Node $node = null) + { + $action = $this->params->get('action'); + $form = null; + if ($this->showFullscreen) { + return; + } + + $canEdit = $bp->getMetadata()->canModify(); + + if ($action === 'add' && $canEdit) { + $form = $this->loadForm('AddNode') + ->setSuccessUrl(Url::fromRequest()->without('action')) + ->setStorage($this->storage()) + ->setProcess($bp) + ->setParentNode($node) + ->setSession($this->session()) + ->handleRequest(); + } elseif ($action === 'editmonitored' && $canEdit) { + $form = $this->loadForm('EditNode') + ->setSuccessUrl(Url::fromRequest()->without('action')) + ->setProcess($bp) + ->setNode($bp->getNode($this->params->get('editmonitorednode'))) + ->setParentNode($node) + ->setSession($this->session()) + ->handleRequest(); + } elseif ($action === 'delete' && $canEdit) { + $form = $this->loadForm('DeleteNode') + ->setSuccessUrl(Url::fromRequest()->without('action')) + ->setProcess($bp) + ->setNode($bp->getNode($this->params->get('deletenode'))) + ->setParentNode($node) + ->setSession($this->session()) + ->handleRequest(); + } elseif ($action === 'edit' && $canEdit) { + $form = $this->loadForm('Process') + ->setSuccessUrl(Url::fromRequest()->without('action')) + ->setProcess($bp) + ->setNode($bp->getNode($this->params->get('editnode'))) + ->setSession($this->session()) + ->handleRequest(); + } elseif ($action === 'simulation') { + $form = $this->loadForm('simulation') + ->setSuccessUrl(Url::fromRequest()->without('action')) + ->setNode($bp->getNode($this->params->get('simulationnode'))) + ->setSimulation(Simulation::fromSession($this->session())) + ->handleRequest(); + } elseif ($action === 'move') { + $form = $this->loadForm('MoveNode') + ->setProcess($bp) + ->setParentNode($node) + ->setSession($this->session()) + ->setNode($bp->getNode($this->params->get('movenode'))) + ->handleRequest(); + } + + if ($form) { + $this->content()->prepend(HtmlString::create((string) $form)); + } + } + + protected function setDynamicAutorefresh() + { + 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(BpConfig $bp) + { + if ($bp->hasWarnings()) { + $ul = Html::tag('ul', array('class' => 'warning')); + foreach ($bp->getWarnings() as $warning) { + $ul->add(Html::tag('li')->setContent($warning)); + } + + return $ul; + } else { + return null; + } + } + + protected function showErrors(BpConfig $bp) + { + if ($bp->hasWarnings()) { + $ul = Html::tag('ul', array('class' => 'error')); + foreach ($bp->getErrors() as $msg) { + $ul->add(Html::tag('li')->setContent($msg)); + } + + return $ul; + } else { + return null; + } + } + + protected function showHints(BpConfig $bp) + { + $ul = Html::tag('ul', ['class' => 'error']); + foreach ($bp->getErrors() as $error) { + $ul->add(Html::tag('li')->setContent($error)); + } + if ($bp->hasChanges()) { + $li = Html::tag('li')->setSeparator(' '); + $li->add(sprintf( + $this->translate('This process has %d pending change(s).'), + $bp->countChanges() + ))->add(Html::tag( + 'a', + [ + 'href' => Url::fromPath('businessprocess/process/config') + ->setParams($this->getRequest()->getUrl()->getParams()) + ], + $this->translate('Store') + ))->add(Html::tag( + 'a', + ['href' => $this->url()->with('dismissChanges', true)], + $this->translate('Dismiss') + )); + $ul->add($li); + } + + if ($bp->hasSimulations()) { + $li = Html::tag('li')->setSeparator(' '); + $li->add(sprintf( + $this->translate('This process shows %d simulated state(s).'), + $bp->countSimulations() + ))->add(Html::tag( + 'a', + ['href' => $this->url()->with('dismissSimulations', true)], + $this->translate('Dismiss') + )); + $ul->add($li); + } + + if (! $ul->isEmpty()) { + return $ul; + } else { + return null; + } + } + + /** + * Show the source code for a process + */ + public function sourceAction() + { + $this->assertPermission('businessprocess/modify'); + + $bp = $this->loadModifiedBpConfig(); + $this->view->showDiff = $showDiff = (bool) $this->params->get('showDiff', false); + + $this->view->source = LegacyConfigRenderer::renderConfig($bp); + if ($this->view->showDiff) { + $this->view->diff = ConfigDiff::create( + $this->storage()->getSource($this->view->configName), + $this->view->source + ); + $title = sprintf( + $this->translate('%s: Source Code Differences'), + $bp->getTitle() + ); + } else { + $title = sprintf( + $this->translate('%s: Source Code'), + $bp->getTitle() + ); + } + + $this->setTitle($title); + $this->controls() + ->add($this->tabsForConfig($bp)->activate('source')) + ->add(Html::tag('h1', null, $title)) + ->add($this->createConfigActionBar($bp, $showDiff)); + + $this->setViewScript('process/source'); + } + + /** + * Download a process configuration file + */ + public function downloadAction() + { + $this->assertPermission('businessprocess/modify'); + + $config = $this->loadModifiedBpConfig(); + $response = $this->getResponse(); + $response->setHeader( + 'Content-Disposition', + sprintf( + 'attachment; filename="%s.conf";', + $config->getName() + ) + ); + $response->setHeader('Content-Type', 'text/plain'); + + echo LegacyConfigRenderer::renderConfig($config); + $this->doNotRender(); + } + + /** + * Modify a business process configuration + */ + public function configAction() + { + $this->assertPermission('businessprocess/modify'); + + $bp = $this->loadModifiedBpConfig(); + + $title = sprintf( + $this->translate('%s: Configuration'), + $bp->getTitle() + ); + $this->setTitle($title); + $this->controls() + ->add($this->tabsForConfig($bp)->activate('config')) + ->add(Html::tag('h1', null, $title)) + ->add($this->createConfigActionBar($bp)); + + $url = Url::fromPath('businessprocess/process/show') + ->setParams($this->getRequest()->getUrl()->getParams()); + $this->content()->add( + $this->loadForm('bpConfig') + ->setProcessConfig($bp) + ->setStorage($this->storage()) + ->setSuccessUrl($url) + ->handleRequest() + ); + } + + protected function createConfigActionBar(BpConfig $config, $showDiff = false) + { + $actionBar = new ActionBar(); + + if ($showDiff) { + $params = array('config' => $config->getName()); + $actionBar->add(Html::tag( + 'a', + [ + 'href' => Url::fromPath('businessprocess/process/source', $params), + 'class' => 'icon-doc-text', + 'title' => $this->translate('Show source code') + ], + $this->translate('Source') + )); + } else { + $params = array( + 'config' => $config->getName(), + 'showDiff' => true + ); + + $actionBar->add(Html::tag( + 'a', + [ + 'href' => Url::fromPath('businessprocess/process/source', $params), + 'class' => 'icon-flapping', + 'title' => $this->translate('Highlight changes') + ], + $this->translate('Diff') + )); + } + + $actionBar->add(Html::tag( + 'a', + [ + 'href' => Url::fromPath('businessprocess/process/download', ['config' => $config->getName()]), + 'class' => 'icon-download', + 'target' => '_blank', + 'title' => $this->translate('Download process configuration') + ], + $this->translate('Download') + )); + + return $actionBar; + } + + protected function tabsForShow() + { + return $this->tabs()->add('show', array( + 'label' => $this->translate('Business Process'), + 'url' => $this->url() + )); + } + + /** + * @return Tabs + */ + protected function tabsForCreate() + { + return $this->tabs()->add('create', array( + 'label' => $this->translate('Create'), + 'url' => 'businessprocess/process/create' + ))->add('upload', array( + 'label' => $this->translate('Upload'), + 'url' => 'businessprocess/process/upload' + )); + } + + protected function tabsForConfig(BpConfig $config) + { + $params = array( + 'config' => $config->getName() + ); + + $tabs = $this->tabs()->add('config', array( + 'label' => $this->translate('Process Configuration'), + 'url' =>Url::fromPath('businessprocess/process/config', $params) + )); + + if ($this->params->get('showDiff')) { + $params['showDiff'] = true; + } + + $tabs->add('source', array( + 'label' => $this->translate('Source'), + 'url' =>Url::fromPath('businessprocess/process/source', $params) + )); + + return $tabs; + } + + protected function handleFormatRequest(BpConfig $bp, BpNode $node = null) + { + $desiredContentType = $this->getRequest()->getHeader('Accept'); + if ($desiredContentType === 'application/json') { + $desiredFormat = 'json'; + } elseif ($desiredContentType === 'text/csv') { + $desiredFormat = 'csv'; + } else { + $desiredFormat = strtolower($this->params->get('format', 'html')); + } + + switch ($desiredFormat) { + case 'json': + $response = $this->getResponse(); + $response + ->setHeader('Content-Type', 'application/json') + ->setHeader('Cache-Control', 'no-store') + ->setHeader( + 'Content-Disposition', + 'inline; filename=' . $this->getRequest()->getActionName() . '.json' + ) + ->appendBody(Json::sanitize($node !== null ? $node->toArray() : $bp->toArray())) + ->sendResponse(); + exit; + case 'csv': + $csv = fopen('php://temp', 'w'); + + fputcsv($csv, ['Path', 'Name', 'State', 'Since', 'In_Downtime']); + + foreach ($node !== null ? $node->toArray(null, true) : $bp->toArray(true) as $node) { + $data = [$node['path'], $node['name']]; + + if (isset($node['state'])) { + $data[] = $node['state']; + } + + if (isset($node['since'])) { + $data[] = DateFormatter::formatDateTime($node['since']); + } + + if (isset($node['in_downtime'])) { + $data[] = $node['in_downtime']; + } + + fputcsv($csv, $data); + } + + $response = $this->getResponse(); + $response + ->setHeader('Content-Type', 'text/csv') + ->setHeader('Cache-Control', 'no-store') + ->setHeader( + 'Content-Disposition', + 'attachment; filename=' . $this->getRequest()->getActionName() . '.csv' + ) + ->sendHeaders(); + + rewind($csv); + + fpassthru($csv); + + exit; + } + } +} |