diff --git a/application/clicommands/ProcessCommand.php b/application/clicommands/ProcessCommand.php
index af3cc29..0d00a37 100644
--- a/application/clicommands/ProcessCommand.php
+++ b/application/clicommands/ProcessCommand.php
@@ -167,21 +167,22 @@ class ProcessCommand extends Command
}
}
- protected function renderProblemTree($tree, $useColors = false, $depth = 0)
+ protected function renderProblemTree($tree, $useColors = false, $depth = 0, BpNode $parent = null)
{
$output = '';
foreach ($tree as $name => $subtree) {
/** @var Node $node */
$node = $subtree['node'];
+ $state = $parent !== null ? $parent->getChildState($node) : $node->getState();
if ($node instanceof HostNode) {
- $colors = $this->hostColors[$node->getState()];
+ $colors = $this->hostColors[$state];
} else {
- $colors = $this->serviceColors[$node->getState()];
+ $colors = $this->serviceColors[$state];
}
- $state = sprintf('[%s]', $node->getStateName());
+ $state = sprintf('[%s]', $node->getStateName($state));
if ($useColors) {
$state = $this->screen->colorize($state, $colors[0], $colors[1]);
}
@@ -193,7 +194,10 @@ class ProcessCommand extends Command
$state,
$node->getAlias()
);
- $output .= $this->renderProblemTree($subtree['children'], $useColors, $depth + 1);
+
+ if ($node instanceof BpNode) {
+ $output .= $this->renderProblemTree($subtree['children'], $useColors, $depth + 1, $node);
+ }
}
return $output;
diff --git a/application/forms/AddNodeForm.php b/application/forms/AddNodeForm.php
index 591f37a..df9fe66 100644
--- a/application/forms/AddNodeForm.php
+++ b/application/forms/AddNodeForm.php
@@ -200,6 +200,11 @@ class AddNodeForm extends QuickForm
),
'validators' => [[new NoDuplicateChildrenValidator($this, $this->bp, $this->parent), true]]
]);
+
+ $this->addHostOverrideCheckbox();
+ if ($this->getSentValue('host_override') === '1') {
+ $this->addHostOverrideElement();
+ }
}
protected function selectService()
@@ -207,6 +212,11 @@ class AddNodeForm extends QuickForm
$this->addHostElement();
if ($host = $this->getSentValue('host')) {
$this->addServicesElement($host);
+ $this->addServiceOverrideCheckbox();
+
+ if ($this->getSentValue('service_override') === '1') {
+ $this->addServiceOverrideElement();
+ }
} else {
$this->setSubmitLabel($this->translate('Next'));
}
@@ -253,6 +263,44 @@ class AddNodeForm extends QuickForm
]);
}
+ protected function addHostOverrideCheckbox()
+ {
+ $this->addElement('checkbox', 'host_override', [
+ 'ignore' => true,
+ 'class' => 'autosubmit',
+ 'label' => $this->translate('Override Host State'),
+ 'description' => $this->translate('Enable host state overrides')
+ ]);
+ }
+
+ protected function addHostOverrideElement()
+ {
+ $this->addElement('stateOverrides', 'stateOverrides', [
+ 'required' => true,
+ 'label' => $this->translate('State Overrides'),
+ 'states' => $this->enumHostStateList()
+ ]);
+ }
+
+ protected function addServiceOverrideCheckbox()
+ {
+ $this->addElement('checkbox', 'service_override', [
+ 'ignore' => true,
+ 'class' => 'autosubmit',
+ 'label' => $this->translate('Override Service State'),
+ 'description' => $this->translate('Enable service state overrides')
+ ]);
+ }
+
+ protected function addServiceOverrideElement()
+ {
+ $this->addElement('stateOverrides', 'stateOverrides', [
+ 'required' => true,
+ 'label' => $this->translate('State Overrides'),
+ 'states' => $this->enumServiceStateList()
+ ]);
+ }
+
protected function selectProcess()
{
if ($this->hasParentNode()) {
@@ -372,6 +420,17 @@ class AddNodeForm extends QuickForm
return $res;
}
+ protected function enumHostStateList()
+ {
+ $hostStateList = [
+ 0 => $this->translate('UP'),
+ 1 => $this->translate('DOWN'),
+ 99 => $this->translate('PENDING')
+ ];
+
+ return $hostStateList;
+ }
+
protected function enumServiceList($host)
{
$names = $this->backend
@@ -391,6 +450,19 @@ class AddNodeForm extends QuickForm
return $services;
}
+ protected function enumServiceStateList()
+ {
+ $serviceStateList = [
+ 0 => $this->translate('OK'),
+ 1 => $this->translate('WARNING'),
+ 2 => $this->translate('CRITICAL'),
+ 3 => $this->translate('UNKNOWN'),
+ 99 => $this->translate('PENDING'),
+ ];
+
+ return $serviceStateList;
+ }
+
protected function hasProcesses()
{
return count($this->enumProcesses()) > 0;
@@ -466,6 +538,19 @@ class AddNodeForm extends QuickForm
switch ($this->getValue('node_type')) {
case 'host':
case 'service':
+ $stateOverrides = $this->getValue('stateOverrides');
+ if (! empty($stateOverrides)) {
+ $childOverrides = [];
+ foreach ($this->getValue('children') as $service) {
+ $childOverrides[$service] = $stateOverrides;
+ }
+
+ $changes->modifyNode($this->parent, [
+ 'stateOverrides' => array_merge($this->parent->getStateOverrides(), $childOverrides)
+ ]);
+ }
+
+ // Fallthrough
case 'process':
if ($this->hasParentNode()) {
$changes->addChildrenToNode($this->getValue('children'), $this->parent);
diff --git a/application/forms/EditNodeForm.php b/application/forms/EditNodeForm.php
index 429a8f2..588c6de 100644
--- a/application/forms/EditNodeForm.php
+++ b/application/forms/EditNodeForm.php
@@ -182,6 +182,14 @@ class EditNodeForm extends QuickForm
'description' => $this->translate('The host for this business process node'),
'validators' => [[new NoDuplicateChildrenValidator($this, $this->bp, $this->parent), true]]
));
+
+ $this->addHostOverrideCheckbox();
+ $hostOverrideSent = $this->getSentValue('host_override');
+ if ($hostOverrideSent === '1'
+ || ($hostOverrideSent === null && $this->getElement('host_override')->isChecked())
+ ) {
+ $this->addHostOverrideElement();
+ }
}
protected function selectService()
@@ -190,8 +198,16 @@ class EditNodeForm extends QuickForm
if ($this->getSentValue('hosts') === null) {
$this->addServicesElement($this->host);
+ $this->addServiceOverrideCheckbox();
+ if ($this->getElement('service_override')->isChecked() || $this->getSentValue('service_override') === '1') {
+ $this->addServiceOverrideElement();
+ }
} elseif ($host = $this->getSentValue('hosts')) {
$this->addServicesElement($host);
+ $this->addServiceOverrideCheckbox();
+ if ($this->getSentValue('service_override') === '1') {
+ $this->addServiceOverrideElement();
+ }
} else {
$this->setSubmitLabel($this->translate('Next'));
}
@@ -210,6 +226,27 @@ class EditNodeForm extends QuickForm
$this->getElement('hosts')->setValue($this->host);
}
+ protected function addHostOverrideCheckbox()
+ {
+ $this->addElement('checkbox', 'host_override', [
+ 'ignore' => true,
+ 'class' => 'autosubmit',
+ 'value' => ! empty($this->parent->getStateOverrides($this->node->getName())),
+ 'label' => $this->translate('Override Host State'),
+ 'description' => $this->translate('Enable host state overrides')
+ ]);
+ }
+
+ protected function addHostOverrideElement()
+ {
+ $this->addElement('stateOverrides', 'stateOverrides', [
+ 'required' => true,
+ 'states' => $this->enumHostStateList(),
+ 'value' => $this->parent->getStateOverrides($this->node->getName()),
+ 'label' => $this->translate('State Overrides')
+ ]);
+ }
+
protected function addServicesElement($host)
{
$this->addElement('select', 'children', array(
@@ -222,6 +259,27 @@ class EditNodeForm extends QuickForm
));
}
+ protected function addServiceOverrideCheckbox()
+ {
+ $this->addElement('checkbox', 'service_override', [
+ 'ignore' => true,
+ 'class' => 'autosubmit',
+ 'value' => ! empty($this->parent->getStateOverrides($this->node->getName())),
+ 'label' => $this->translate('Override Service State'),
+ 'description' => $this->translate('Enable service state overrides')
+ ]);
+ }
+
+ protected function addServiceOverrideElement()
+ {
+ $this->addElement('stateOverrides', 'stateOverrides', [
+ 'required' => true,
+ 'states' => $this->enumServiceStateList(),
+ 'value' => $this->parent->getStateOverrides($this->node->getName()),
+ 'label' => $this->translate('State Overrides')
+ ]);
+ }
+
protected function selectProcess()
{
$this->addElement('multiselect', 'children', array(
@@ -322,6 +380,17 @@ class EditNodeForm extends QuickForm
return $res;
}
+ protected function enumHostStateList()
+ {
+ $hostStateList = [
+ 0 => $this->translate('UP'),
+ 1 => $this->translate('DOWN'),
+ 99 => $this->translate('PENDING')
+ ];
+
+ return $hostStateList;
+ }
+
protected function enumServiceList($host)
{
$names = $this->backend
@@ -341,6 +410,19 @@ class EditNodeForm extends QuickForm
return $services;
}
+ protected function enumServiceStateList()
+ {
+ $serviceStateList = [
+ 0 => $this->translate('OK'),
+ 1 => $this->translate('WARNING'),
+ 2 => $this->translate('CRITICAL'),
+ 3 => $this->translate('UNKNOWN'),
+ 99 => $this->translate('PENDING'),
+ ];
+
+ return $serviceStateList;
+ }
+
protected function hasProcesses()
{
return count($this->enumProcesses()) > 0;
@@ -407,6 +489,19 @@ class EditNodeForm extends QuickForm
switch ($this->getValue('node_type')) {
case 'host':
case 'service':
+ $stateOverrides = $this->getValue('stateOverrides') ?: [];
+ if (! empty($stateOverrides)) {
+ $stateOverrides = array_merge(
+ $this->parent->getStateOverrides(),
+ [$this->getValue('children') => $stateOverrides]
+ );
+ } else {
+ $stateOverrides = $this->parent->getStateOverrides();
+ unset($stateOverrides[$this->getValue('children')]);
+ }
+
+ $changes->modifyNode($this->parent, ['stateOverrides' => $stateOverrides]);
+ // Fallthrough
case 'process':
$changes->addChildrenToNode($this->getValue('children'), $this->parent);
break;
diff --git a/application/views/helpers/FormStateOverrides.php b/application/views/helpers/FormStateOverrides.php
new file mode 100644
index 0000000..74ed2f4
--- /dev/null
+++ b/application/views/helpers/FormStateOverrides.php
@@ -0,0 +1,40 @@
+ $label) {
+ if ($state === 0) {
+ continue;
+ }
+
+ $chosen = $state;
+ if (isset($value[$state])) {
+ $chosen = $value[$state];
+ }
+
+ $options = [$state => t('Keep actual state')] + $states;
+
+ $html .= '';
+ }
+
+ return $html;
+ }
+}
diff --git a/doc/07-State-Overrides.md b/doc/07-State-Overrides.md
new file mode 100644
index 0000000..dfdbaeb
--- /dev/null
+++ b/doc/07-State-Overrides.md
@@ -0,0 +1,45 @@
+# State Overrides
+
+Business processes utilize their children's states to calculate their own state.
+While you can influence this with [operators](09-Operators.md), it's also possible
+to override individual states. (This applies to host and service nodes.)
+
+## Configuring Overrides
+
+State overrides get configured per node. When adding or editing a node, you can
+define which state should be overridden with another one.
+
+Below `WARNING` is chosen as a replacement for `CRITICAL`.
+
+
+
+## Identifying Overrides
+
+In tile view overridden states are indicated by an additional state ball in the
+lower left of a tile. This is then the actual state the object is in.
+
+
+
+In tree view overridden states are indicated on the very right of a row. There
+the actual state is shown and which one it is replaced with.
+
+
+
+## File Format Extensions
+
+The configuration file format has slightly been changed to accommodate state
+overrides. Though, previous configurations are perfectly upwards compatible.
+
+### New Extra Line
+
+For process nodes a new extra line is used to store state overrides.
+
+```
+state_overrides dev_database_servers!mysql;mysql|2-1
+```
+
+The full syntax for this is as follows:
+
+```
+state_overrides !|n-n[!|n-n[,n-n]]
+```
diff --git a/doc/screenshot/07_state_overrides/0701_override_config.png b/doc/screenshot/07_state_overrides/0701_override_config.png
new file mode 100644
index 0000000..49ca2ad
Binary files /dev/null and b/doc/screenshot/07_state_overrides/0701_override_config.png differ
diff --git a/doc/screenshot/07_state_overrides/0702_overridden_tile.png b/doc/screenshot/07_state_overrides/0702_overridden_tile.png
new file mode 100644
index 0000000..db2012e
Binary files /dev/null and b/doc/screenshot/07_state_overrides/0702_overridden_tile.png differ
diff --git a/doc/screenshot/07_state_overrides/0703_overridden_tree.png b/doc/screenshot/07_state_overrides/0703_overridden_tree.png
new file mode 100644
index 0000000..ccf4fde
Binary files /dev/null and b/doc/screenshot/07_state_overrides/0703_overridden_tree.png differ
diff --git a/library/Businessprocess/BpNode.php b/library/Businessprocess/BpNode.php
index 9aeaab8..58a7c11 100644
--- a/library/Businessprocess/BpNode.php
+++ b/library/Businessprocess/BpNode.php
@@ -26,6 +26,7 @@ class BpNode extends Node
protected $missing = null;
protected $empty = null;
protected $missingChildren;
+ protected $stateOverrides = [];
protected static $emptyStateSummary = array(
'OK' => 0,
@@ -71,7 +72,7 @@ class BpNode extends Node
} elseif ($child->isMissing()) {
$this->counters['MISSING']++;
} else {
- $state = $child->getStateName();
+ $state = $child->getStateName($this->getChildState($child));
$this->counters[$state]++;
}
}
@@ -127,9 +128,13 @@ class BpNode extends Node
$problems = array();
foreach ($this->getChildren() as $child) {
- if ($child->isProblem()
- || ($child instanceof BpNode && $child->hasProblems())
- ) {
+ if (isset($this->stateOverrides[$child->getName()])) {
+ $problem = $this->getChildState($child) > 0;
+ } else {
+ $problem = $child->isProblem() || ($child instanceof BpNode && $child->hasProblems());
+ }
+
+ if ($problem) {
$problems[] = $child;
}
}
@@ -187,7 +192,8 @@ class BpNode extends Node
if ($nodeState !== 0) {
foreach ($this->getChildren() as $child) {
- $childState = $rootCause ? $child->getSortingState() : $child->getState();
+ $childState = $this->getChildState($child);
+ $childState = $rootCause ? $child->getSortingState($childState) : $childState;
if (($rootCause ? $this->getSortingState() : $nodeState) === $childState) {
$name = $child->getName();
$tree[$name] = [
@@ -327,6 +333,31 @@ class BpNode extends Node
return $this->info_command;
}
+ public function setStateOverrides(array $overrides, $name = null)
+ {
+ if ($name === null) {
+ $this->stateOverrides = $overrides;
+ } else {
+ $this->stateOverrides[$name] = $overrides;
+ }
+
+ return $this;
+ }
+
+ public function getStateOverrides($name = null)
+ {
+ $overrides = null;
+ if ($name !== null) {
+ if (isset($this->stateOverrides[$name])) {
+ $overrides = $this->stateOverrides[$name];
+ }
+ } else {
+ $overrides = $this->stateOverrides;
+ }
+
+ return $overrides;
+ }
+
public function getAlias()
{
return $this->alias ? preg_replace('~_~', ' ', $this->alias) : $this->name;
@@ -354,6 +385,27 @@ class BpNode extends Node
return $this->state;
}
+ /**
+ * Get the given child's state, possibly adjusted by override rules
+ *
+ * @param Node|string $child
+ * @return int
+ */
+ public function getChildState($child)
+ {
+ if (! $child instanceof Node) {
+ $child = $this->getChildByName($child);
+ }
+
+ $childName = $child->getName();
+ $childState = $child->getState();
+ if (! isset($this->stateOverrides[$childName][$childState])) {
+ return $childState;
+ }
+
+ return $this->stateOverrides[$childName][$childState];
+ }
+
public function getHtmlId()
{
return 'businessprocess-' . preg_replace('/[\r\n\t\s]/', '_', $this->getName());
@@ -391,7 +443,7 @@ class BpNode extends Node
$child->setMissing();
}
- $sort_states[] = $child->getSortingState();
+ $sort_states[] = $child->getSortingState($this->getChildState($child));
$lastStateChange = max($lastStateChange, $child->getLastStateChange());
$bp->endLoopDetection($this->name);
}
diff --git a/library/Businessprocess/Node.php b/library/Businessprocess/Node.php
index 7849a7c..f22339b 100644
--- a/library/Businessprocess/Node.php
+++ b/library/Businessprocess/Node.php
@@ -249,9 +249,11 @@ abstract class Node
return $this->state;
}
- public function getSortingState()
+ public function getSortingState($state = null)
{
- $state = $this->getState();
+ if ($state === null) {
+ $state = $this->getState();
+ }
if (self::$ackIsOk && $this->isAcknowledged()) {
$state = self::ICINGA_OK;
diff --git a/library/Businessprocess/Renderer/Renderer.php b/library/Businessprocess/Renderer/Renderer.php
index 4c9a8de..35e3225 100644
--- a/library/Businessprocess/Renderer/Renderer.php
+++ b/library/Businessprocess/Renderer/Renderer.php
@@ -180,9 +180,9 @@ abstract class Renderer extends HtmlDocument
if ($node->isEmpty() && ! $node instanceof MonitoredNode) {
$classes = array('empty');
} else {
- $classes = array(
- strtolower($node->getStateName())
- );
+ $classes = [strtolower($node->getStateName(
+ $this->parent !== null ? $this->parent->getChildState($node) : null
+ ))];
}
if ($node->hasMissingChildren()) {
$classes[] = 'missing-children';
diff --git a/library/Businessprocess/Renderer/TileRenderer/NodeTile.php b/library/Businessprocess/Renderer/TileRenderer/NodeTile.php
index 8c16ed5..ef26693 100644
--- a/library/Businessprocess/Renderer/TileRenderer/NodeTile.php
+++ b/library/Businessprocess/Renderer/TileRenderer/NodeTile.php
@@ -113,6 +113,18 @@ class NodeTile extends BaseHtmlElement
));
}
+ if ($this->renderer->rendersSubNode()
+ && $this->renderer->getParentNode()->getChildState($node) !== $node->getState()
+ ) {
+ $this->add((new StateBall(strtolower($node->getStateName())))->addAttributes([
+ 'class' => 'overridden-state',
+ 'title' => sprintf(
+ '%s',
+ $node->getStateName()
+ )
+ ]));
+ }
+
if ($node instanceof BpNode && !$renderer->isBreadcrumb()) {
$this->add(Html::tag(
'p',
diff --git a/library/Businessprocess/Renderer/TreeRenderer.php b/library/Businessprocess/Renderer/TreeRenderer.php
index 9322c26..efe8d77 100644
--- a/library/Businessprocess/Renderer/TreeRenderer.php
+++ b/library/Businessprocess/Renderer/TreeRenderer.php
@@ -79,7 +79,7 @@ class TreeRenderer extends Renderer
if ($node instanceof BpNode) {
$html[] = $this->renderNode($bp, $node);
} else {
- $html[] = $this->renderChild($bp, $node);
+ $html[] = $this->renderChild($bp, $this->parent, $node);
}
}
@@ -106,9 +106,10 @@ class TreeRenderer extends Renderer
/**
* @param Node $node
* @param array $path
+ * @param BpNode $parent
* @return BaseHtmlElement[]
*/
- public function getNodeIcons(Node $node, array $path = null)
+ public function getNodeIcons(Node $node, array $path = null, BpNode $parent = null)
{
$icons = [];
if (empty($path) && $node instanceof BpNode) {
@@ -116,7 +117,7 @@ class TreeRenderer extends Renderer
} else {
$icons[] = $node->getIcon();
}
- $state = strtolower($node->getStateName());
+ $state = strtolower($node->getStateName($parent !== null ? $parent->getChildState($node) : null));
if ($node->isHandled()) {
$state = $state . '-handled';
}
@@ -136,6 +137,27 @@ class TreeRenderer extends Renderer
return $icons;
}
+ public function getOverriddenState($fakeState, Node $node)
+ {
+ $overriddenState = Html::tag('div', ['class' => 'overridden-state']);
+ $overriddenState->add((new StateBall(strtolower($node->getStateName())))->addAttributes([
+ 'title' => sprintf(
+ '%s',
+ $node->getStateName()
+ )
+ ]));
+ $overriddenState->add(Html::tag('i', ['class' => 'icon icon-right-small']));
+ $overriddenState->add((new StateBall(strtolower($node->getStateName($fakeState))))->addAttributes([
+ 'title' => sprintf(
+ '%s',
+ $node->getStateName($fakeState)
+ ),
+ 'class' => 'last'
+ ]));
+
+ return $overriddenState;
+ }
+
/**
* @param BpConfig $bp
* @param Node $node
@@ -222,14 +244,14 @@ class TreeRenderer extends Renderer
if ($child instanceof BpNode) {
$ul->add($this->renderNode($bp, $child, $path));
} else {
- $ul->add($this->renderChild($bp, $child, $path));
+ $ul->add($this->renderChild($bp, $node, $child, $path));
}
}
return $li;
}
- protected function renderChild($bp, Node $node, $path = null)
+ protected function renderChild($bp, BpNode $parent, Node $node, $path = null)
{
$li = Html::tag('li', [
'class' => 'movable',
@@ -237,12 +259,16 @@ class TreeRenderer extends Renderer
'data-node-name' => $node->getName()
]);
- $li->add($this->getNodeIcons($node, $path));
+ $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));
}
diff --git a/library/Businessprocess/Storage/LegacyConfigParser.php b/library/Businessprocess/Storage/LegacyConfigParser.php
index d4325cf..4142731 100644
--- a/library/Businessprocess/Storage/LegacyConfigParser.php
+++ b/library/Businessprocess/Storage/LegacyConfigParser.php
@@ -233,6 +233,24 @@ class LegacyConfigParser
$bp->getBpNode($name)->setInfoUrl($url);
}
+ protected function parseStateOverrides(&$line, BpConfig $bp)
+ {
+ // state_overrides !|n-n[,n-n]!|n-n[,n-n]
+ $segments = preg_split('~\s*!\s*~', substr($line, 16));
+ $node = $bp->getNode(array_shift($segments));
+ foreach ($segments as $overrideDef) {
+ list($childName, $overrides) = preg_split('~\s*\|\s*~', $overrideDef, 2);
+
+ $stateOverrides = [];
+ foreach (preg_split('~\s*,\s*~', $overrides) as $override) {
+ list($from, $to) = preg_split('~\s*-\s*~', $override, 2);
+ $stateOverrides[(int) $from] = (int) $to;
+ }
+
+ $node->setStateOverrides($stateOverrides, $childName);
+ }
+ }
+
protected function parseExtraLine(&$line, $typeLength, BpConfig $bp)
{
$type = substr($line, 0, $typeLength);
@@ -251,6 +269,9 @@ class LegacyConfigParser
case 'info_url':
$this->parseInfoUrl($line, $bp);
break;
+ case 'state_overrides':
+ $this->parseStateOverrides($line, $bp);
+ break;
case 'template':
// compat, ignoring for now
break;
@@ -282,9 +303,9 @@ class LegacyConfigParser
return;
}
- // Space found in the first 14 cols? Might be a line with extra information
+ // Space found in the first 16 cols? Might be a line with extra information
$pos = strpos($line, ' ');
- if ($pos !== false && $pos < 14) {
+ if ($pos !== false && $pos < 16) {
if ($this->parseExtraLine($line, $pos, $bp)) {
return;
}
diff --git a/library/Businessprocess/Storage/LegacyConfigRenderer.php b/library/Businessprocess/Storage/LegacyConfigRenderer.php
index 77a1d13..ebe9589 100644
--- a/library/Businessprocess/Storage/LegacyConfigRenderer.php
+++ b/library/Businessprocess/Storage/LegacyConfigRenderer.php
@@ -158,6 +158,7 @@ class LegacyConfigRenderer
public static function renderSingleBpNode(BpNode $node)
{
return static::renderExpression($node)
+ . static::renderStateOverrides($node)
. static::renderDisplay($node)
. static::renderInfoUrl($node);
}
@@ -214,6 +215,27 @@ class LegacyConfigRenderer
}
}
+ public static function renderStateOverrides(BpNode $node)
+ {
+ $stateOverrides = '';
+ foreach ($node->getStateOverrides() as $childName => $overrideRules) {
+ $overrides = [];
+ foreach ($overrideRules as $from => $to) {
+ $overrides[] = sprintf('%d-%d', $from, $to);
+ }
+
+ if (! empty($overrides)) {
+ $stateOverrides .= '!' . $childName . '|' . join(',', $overrides);
+ }
+ }
+
+ if (! $stateOverrides) {
+ return '';
+ }
+
+ return 'state_overrides ' . $node->getName() . $stateOverrides . "\n";
+ }
+
/**
* @param BpNode $node
* @return string
diff --git a/library/Businessprocess/Web/Form/Element/StateOverrides.php b/library/Businessprocess/Web/Form/Element/StateOverrides.php
new file mode 100644
index 0000000..c2216c0
--- /dev/null
+++ b/library/Businessprocess/Web/Form/Element/StateOverrides.php
@@ -0,0 +1,55 @@
+states = $states;
+
+ return $this;
+ }
+
+ /**
+ * Get the overridable states
+ *
+ * @return array
+ */
+ public function getStates()
+ {
+ return $this->states;
+ }
+
+ public function init()
+ {
+ $this->setIsArray(true);
+ }
+
+ public function setValue($value)
+ {
+ $cleanedValue = [];
+
+ if (! empty($value)) {
+ foreach ($value as $from => $to) {
+ if ((int) $from !== (int) $to) {
+ $cleanedValue[$from] = $to;
+ }
+ }
+ }
+
+ return parent::setValue($cleanedValue);
+ }
+}
diff --git a/public/css/module.less b/public/css/module.less
index 40d71f7..3db49bf 100644
--- a/public/css/module.less
+++ b/public/css/module.less
@@ -214,13 +214,18 @@ ul.bp {
margin-right: .5em;
}
- > a.action-link {
+ > :not(.overridden-state) + a.action-link {
margin-left: auto; // Let the first action link move everything to the right
& + a.action-link {
margin-left: 0; // But really only the first one
}
}
+
+ .overridden-state {
+ margin-left: auto;
+ margin-right: 1em;
+ }
}
// collapse handling
@@ -416,6 +421,15 @@ td > a > .badges {
font-size: 0.5em;
}
+ .overridden-state {
+ font-size: .75em;
+ position: absolute;
+ left: 0;
+ bottom: 0;
+ margin: .5em;
+ border: 1px solid white;
+ }
+
> a {
display: block;
text-decoration: none;
@@ -949,6 +963,29 @@ form dt label {
}
}
+#stateOverrides-element {
+ display: inline-table;
+ table-layout: fixed;
+ border-spacing: .5em;
+ padding: 0;
+
+ label {
+ display: table-row;
+
+ span, select {
+ display: table-cell;
+ }
+
+ span {
+ width: 10em;
+ }
+
+ select {
+ width: 26em;
+ }
+ }
+}
+
form fieldset {
min-width: 36em;
}