summaryrefslogtreecommitdiffstats
path: root/library/Businessprocess/Renderer/TreeRenderer.php
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 13:15:40 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 13:15:40 +0000
commitb7fd908d538ed19fe41f03c0a3f93351d8da64e9 (patch)
tree46e14f318948cd4f5d7e874f83e7dfcc5d42fc64 /library/Businessprocess/Renderer/TreeRenderer.php
parentInitial commit. (diff)
downloadicingaweb2-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.php380
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')
+ );
+ }
+}