diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 13:15:40 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 13:15:40 +0000 |
commit | b7fd908d538ed19fe41f03c0a3f93351d8da64e9 (patch) | |
tree | 46e14f318948cd4f5d7e874f83e7dfcc5d42fc64 /library/Businessprocess/Renderer/TreeRenderer.php | |
parent | Initial commit. (diff) | |
download | icingaweb2-module-businessprocess-b7fd908d538ed19fe41f03c0a3f93351d8da64e9.tar.xz icingaweb2-module-businessprocess-b7fd908d538ed19fe41f03c0a3f93351d8da64e9.zip |
Adding upstream version 2.5.0.upstream/2.5.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | library/Businessprocess/Renderer/TreeRenderer.php | 380 |
1 files changed, 380 insertions, 0 deletions
diff --git a/library/Businessprocess/Renderer/TreeRenderer.php b/library/Businessprocess/Renderer/TreeRenderer.php new file mode 100644 index 0000000..097d148 --- /dev/null +++ b/library/Businessprocess/Renderer/TreeRenderer.php @@ -0,0 +1,380 @@ +<?php + +namespace Icinga\Module\Businessprocess\Renderer; + +use Icinga\Application\Version; +use Icinga\Date\DateFormatter; +use Icinga\Module\Businessprocess\BpConfig; +use Icinga\Module\Businessprocess\BpNode; +use Icinga\Module\Businessprocess\ImportedNode; +use Icinga\Module\Businessprocess\Node; +use Icinga\Module\Businessprocess\Web\Form\CsrfToken; +use ipl\Html\Attributes; +use ipl\Html\BaseHtmlElement; +use ipl\Html\Html; +use ipl\Html\HtmlElement; +use ipl\Web\Widget\Icon; +use ipl\Web\Widget\StateBall; + +class TreeRenderer extends Renderer +{ + const NEW_COLLAPSIBLE_IMPLEMENTATION_SINCE = '2.11.2'; + + public function assemble() + { + $bp = $this->config; + $htmlId = $bp->getHtmlId(); + $tree = Html::tag( + 'ul', + [ + 'id' => $htmlId, + 'class' => ['bp', 'sortable', $this->wantsRootNodes() ? '' : 'process'], + 'data-sortable-disabled' => $this->isLocked() || $this->appliesCustomSorting() + ? 'true' + : 'false', + 'data-sortable-data-id-attr' => 'id', + 'data-sortable-direction' => 'vertical', + 'data-sortable-group' => json_encode([ + 'name' => $this->wantsRootNodes() ? 'root' : $htmlId, + 'put' => 'function:rowPutAllowed' + ]), + 'data-sortable-invert-swap' => 'true', + 'data-csrf-token' => CsrfToken::generate() + ], + $this->renderBp($bp) + ); + if ($this->wantsRootNodes()) { + $tree->getAttributes()->add( + 'data-action-url', + $this->getUrl()->with(['config' => $bp->getName()])->getAbsoluteUrl() + ); + + if (version_compare(Version::VERSION, self::NEW_COLLAPSIBLE_IMPLEMENTATION_SINCE, '<')) { + $tree->getAttributes()->add('data-is-root-config', true); + } + } else { + $nodeName = $this->parent instanceof ImportedNode + ? $this->parent->getNodeName() + : $this->parent->getName(); + $tree->getAttributes() + ->add('data-node-name', $nodeName) + ->add('data-action-url', $this->getUrl() + ->with([ + 'config' => $this->parent->getBpConfig()->getName(), + 'node' => $nodeName + ]) + ->getAbsoluteUrl()); + } + + $this->addHtml($tree); + } + + /** + * @param BpConfig $bp + * @return array + */ + public function renderBp(BpConfig $bp) + { + $html = []; + if ($this->wantsRootNodes()) { + $nodes = $bp->getRootNodes(); + } else { + $nodes = $this->parent->getChildren(); + } + + foreach ($this->sort($nodes) as $name => $node) { + if ($node instanceof BpNode) { + $html[] = $this->renderNode($bp, $node); + } else { + $html[] = $this->renderChild($bp, $this->parent, $node); + } + } + + return $html; + } + + 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 + * @param array $path + * @param BpNode $parent + * @return BaseHtmlElement[] + */ + public function getNodeIcons(Node $node, array $path = null, BpNode $parent = null) + { + $icons = []; + if (empty($path) && $node instanceof BpNode) { + $icons[] = new Icon('sitemap'); + } else { + $icons[] = $node->getIcon(); + } + $state = strtolower($node->getStateName($parent !== null ? $parent->getChildState($node) : null)); + if ($node->isHandled()) { + $state = $state . ' handled'; + } + $icons[] = (new StateBall($state, StateBall::SIZE_MEDIUM))->addAttributes([ + 'title' => sprintf( + '%s %s', + $state, + DateFormatter::timeSince($node->getLastStateChange()) + ) + ]); + + if ($node->isAcknowledged()) { + $icons[] = new Icon('check'); + } elseif ($node->isInDowntime()) { + $icons[] = new Icon('plug'); + } + + return $icons; + } + + public function getOverriddenState($fakeState, Node $node) + { + $overriddenState = Html::tag('div', ['class' => 'overridden-state']); + $overriddenState->add( + (new StateBall(strtolower($node->getStateName()), StateBall::SIZE_MEDIUM)) + ->addAttributes([ + 'title' => sprintf( + '%s', + $node->getStateName() + ) + ]) + ); + + $overriddenState->add(new Icon('arrow-right')); + $overriddenState->add( + (new StateBall(strtolower($node->getStateName($fakeState)), StateBall::SIZE_MEDIUM)) + ->addAttributes([ + 'title' => sprintf( + '%s', + $node->getStateName($fakeState) + ), + 'class' => 'last' + ]) + ); + + return $overriddenState; + } + + /** + * @param BpConfig $bp + * @param Node $node + * @param array $path + * + * @return string + */ + public function renderNode(BpConfig $bp, Node $node, $path = array()) + { + $htmlId = $this->getId($node, $path); + $li = Html::tag( + 'li', + [ + 'id' => $htmlId, + 'class' => ['bp', 'movable', $node->getObjectClassName()], + 'data-node-name' => $node instanceof ImportedNode + ? $node->getNodeName() + : $node->getName() + ] + ); + $attributes = $li->getAttributes(); + $attributes->add('class', $this->getStateClassNames($node)); + if ($node->isHandled()) { + $attributes->add('class', 'handled'); + } + if ($node instanceof BpNode) { + $attributes->add('class', 'operator'); + } else { + $attributes->add('class', 'node'); + } + + $details = new HtmlElement('details', Attributes::create(['open' => true])); + $summary = new HtmlElement('summary'); + if (version_compare(Version::VERSION, self::NEW_COLLAPSIBLE_IMPLEMENTATION_SINCE, '>=')) { + $details->getAttributes()->add('class', 'collapsible'); + $summary->getAttributes()->add('class', 'collapsible-control'); // Helps JS, improves performance a bit + } + + $summary->addHtml( + new Icon('caret-down', ['class' => 'collapse-icon']), + new Icon('caret-right', ['class' => 'expand-icon']) + ); + + $summary->add($this->getNodeIcons($node, $path)); + + $summary->add(Html::tag('span', null, $node->getAlias())); + + if ($node instanceof BpNode) { + $summary->add(Html::tag('span', ['class' => 'op'], $node->operatorHtml())); + } + + if ($node instanceof BpNode && $node->hasInfoUrl()) { + $summary->add($this->createInfoAction($node)); + } + + $differentConfig = $node->getBpConfig()->getName() !== $this->getBusinessProcess()->getName(); + if (! $this->isLocked() && !$differentConfig) { + $summary->add($this->getActionIcons($bp, $node)); + } elseif ($differentConfig) { + $summary->add($this->actionIcon( + 'share', + $node->getBpConfig()->isFaulty() + ? $this->getBaseUrl()->setParam('config', $node->getBpConfig()->getName()) + : $this->getSourceUrl($node)->addParams(['mode' => 'tree'])->getAbsoluteUrl(), + mt('businessprocess', 'Show this process as part of its original configuration') + )->addAttributes(['data-base-target' => '_next'])); + } + + $ul = Html::tag('ul', [ + 'class' => ['bp', 'sortable'], + 'data-sortable-disabled' => ($this->isLocked() || $differentConfig || $this->appliesCustomSorting()) + ? 'true' + : 'false', + 'data-sortable-invert-swap' => 'true', + 'data-sortable-data-id-attr' => 'id', + 'data-sortable-draggable' => '.movable', + 'data-sortable-direction' => 'vertical', + 'data-sortable-group' => json_encode([ + 'name' => $htmlId, // Unique, so that the function below is the only deciding factor + 'put' => 'function:rowPutAllowed' + ]), + 'data-csrf-token' => CsrfToken::generate(), + 'data-action-url' => $this->getUrl() + ->with([ + 'config' => $node->getBpConfig()->getName(), + 'node' => $node instanceof ImportedNode + ? $node->getNodeName() + : $node->getName() + ]) + ->getAbsoluteUrl() + ]); + + $path[] = $differentConfig ? $node->getIdentifier() : $node->getName(); + foreach ($this->sort($node->getChildren()) as $name => $child) { + if ($child instanceof BpNode) { + $ul->add($this->renderNode($bp, $child, $path)); + } else { + $ul->add($this->renderChild($bp, $node, $child, $path)); + } + } + + $details->addHtml($summary); + $details->addHtml($ul); + $li->addHtml($details); + + return $li; + } + + protected function renderChild($bp, BpNode $parent, Node $node, $path = null) + { + $li = Html::tag('li', [ + 'class' => 'movable', + 'id' => $this->getId($node, $path ?: []), + 'data-node-name' => $node->getName() + ]); + + $li->add($this->getNodeIcons($node, $path, $parent)); + + $link = $node->getLink(); + $link->getAttributes()->set('data-base-target', '_next'); + $li->add($link); + + if (($overriddenState = $parent->getChildState($node)) !== $node->getState()) { + $li->add($this->getOverriddenState($overriddenState, $node)); + } + + if (! $this->isLocked() && $node->getBpConfig()->getName() === $this->getBusinessProcess()->getName()) { + $li->add($this->getActionIcons($bp, $node)); + } + + return $li; + } + + protected function getActionIcons(BpConfig $bp, Node $node) + { + if ($node instanceof BpNode) { + if ($bp->getMetadata()->canModify()) { + return [$this->createEditAction($bp, $node), $this->renderAddNewNode($node)]; + } else { + return ''; + } + } else { + return $this->createSimulationAction($bp, $node); + } + } + + protected function createEditAction(BpConfig $bp, BpNode $node) + { + return $this->actionIcon( + 'edit', + $this->getUrl()->with(array( + 'action' => 'edit', + 'editnode' => $node->getName() + )), + mt('businessprocess', 'Modify this node') + ); + } + + protected function createSimulationAction(BpConfig $bp, Node $node) + { + return $this->actionIcon( + 'wand-magic-sparkles', + $this->getUrl()->with(array( + //'config' => $bp->getName(), + 'action' => 'simulation', + 'simulationnode' => $node->getName() + )), + mt('businessprocess', 'Simulate a specific state') + ); + } + + protected function createInfoAction(BpNode $node) + { + $url = $node->getInfoUrl(); + return $this->actionIcon( + 'question', + $url, + sprintf('%s: %s', mt('businessprocess', 'More information'), $url) + )->addAttributes(['target' => '_blank']); + } + + protected function actionIcon($icon, $url, $title) + { + return Html::tag( + 'a', + [ + 'href' => $url, + 'title' => $title, + 'class' => 'action-link' + ], + new Icon($icon) + ); + } + + protected function renderAddNewNode($parent) + { + return $this->actionIcon( + 'plus', + $this->getUrl() + ->with('action', 'add') + ->with('node', $parent->getName()), + mt('businessprocess', 'Add a new business process node') + ); + } +} |