Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 603f5cf

Browse files
committed
feature #29538 [Workflow] Add colors to workflow dumps (alexislefebvre)
This PR was squashed before being merged into the 4.3-dev branch (closes #29538). Discussion ---------- [Workflow] Add colors to workflow dumps Fixes #28874 | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #28874, replaces #28933 | License | MIT | Doc PR | TODO, requires symfony/symfony-docs#9476 Fetch data with the `MetadataStore` from #26092 in order to add colors to the dumps. Example of configuration: ```yaml transitions: submit: from: start to: travis metadata: title: transition submit title dump_style: label: 'My custom label' arrow_color: '#0088FF' label_color: 'Red' ``` This code was developed as a bundle, examples can be found on its repository: https://github.com/alexislefebvre/SymfonyWorkflowStyleBundle Commits ------- 60ad109 [Workflow] Add colors to workflow dumps
2 parents 2ff8c19 + 60ad109 commit 603f5cf

13 files changed

+288
-88
lines changed

src/Symfony/Component/Workflow/CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,19 @@ CHANGELOG
66

77
* Trigger `entered` event for subject entering in the Workflow for the first time
88
* Added a context to `Workflow::apply()`. The `MethodMarkingStore` could be used to leverage this feature.
9+
* Add style to transitions by declaring metadata:
10+
```
11+
$places = range('a', 'b');
12+
$transition = new Transition('t1', 'a', 'b');
13+
$transitions[] = $transition;
14+
$transitionsMetadata = new \SplObjectStorage();
15+
$transitionsMetadata[$transition] = [
16+
'color' => 'Red',
17+
'arrow_color' => '#00ff00',
18+
];
19+
$inMemoryMetadataStore = new InMemoryMetadataStore([], [], $transitionsMetadata);
20+
return new Definition($places, $transitions, null, $inMemoryMetadataStore);
21+
```
922

1023
4.1.0
1124
-----

src/Symfony/Component/Workflow/Dumper/GraphvizDumper.php

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ public function dump(Definition $definition, Marking $marking = null, array $opt
6363
*/
6464
protected function findPlaces(Definition $definition, Marking $marking = null)
6565
{
66+
$workflowMetadata = $definition->getMetadataStore();
67+
6668
$places = [];
6769

6870
foreach ($definition->getPlaces() as $place) {
@@ -74,6 +76,15 @@ protected function findPlaces(Definition $definition, Marking $marking = null)
7476
$attributes['color'] = '#FF0000';
7577
$attributes['shape'] = 'doublecircle';
7678
}
79+
$backgroundColor = $workflowMetadata->getMetadata('bg_color', $place);
80+
if (null !== $backgroundColor) {
81+
$attributes['style'] = 'filled';
82+
$attributes['fillcolor'] = $backgroundColor;
83+
}
84+
$label = $workflowMetadata->getMetadata('label', $place);
85+
if (null !== $label) {
86+
$attributes['name'] = $label;
87+
}
7788
$places[$place] = [
7889
'attributes' => $attributes,
7990
];
@@ -87,12 +98,23 @@ protected function findPlaces(Definition $definition, Marking $marking = null)
8798
*/
8899
protected function findTransitions(Definition $definition)
89100
{
101+
$workflowMetadata = $definition->getMetadataStore();
102+
90103
$transitions = [];
91104

92105
foreach ($definition->getTransitions() as $transition) {
106+
$attributes = ['shape' => 'box', 'regular' => true];
107+
108+
$backgroundColor = $workflowMetadata->getMetadata('bg_color', $transition);
109+
if (null !== $backgroundColor) {
110+
$attributes['style'] = 'filled';
111+
$attributes['fillcolor'] = $backgroundColor;
112+
}
113+
$name = $workflowMetadata->getMetadata('label', $transition) ?? $transition->getName();
114+
93115
$transitions[] = [
94-
'attributes' => ['shape' => 'box', 'regular' => true],
95-
'name' => $transition->getName(),
116+
'attributes' => $attributes,
117+
'name' => $name,
96118
];
97119
}
98120

@@ -107,7 +129,14 @@ protected function addPlaces(array $places)
107129
$code = '';
108130

109131
foreach ($places as $id => $place) {
110-
$code .= sprintf(" place_%s [label=\"%s\", shape=circle%s];\n", $this->dotize($id), $this->escape($id), $this->addAttributes($place['attributes']));
132+
if (isset($place['attributes']['name'])) {
133+
$placeName = $place['attributes']['name'];
134+
unset($place['attributes']['name']);
135+
} else {
136+
$placeName = $id;
137+
}
138+
139+
$code .= sprintf(" place_%s [label=\"%s\", shape=circle%s];\n", $this->dotize($id), $this->escape($placeName), $this->addAttributes($place['attributes']));
111140
}
112141

113142
return $code;
@@ -121,7 +150,7 @@ protected function addTransitions(array $transitions)
121150
$code = '';
122151

123152
foreach ($transitions as $place) {
124-
$code .= sprintf(" transition_%s [label=\"%s\", shape=box%s];\n", $this->dotize($place['name']), $this->escape($place['name']), $this->addAttributes($place['attributes']));
153+
$code .= sprintf(" transition_%s [label=\"%s\",%s];\n", $this->dotize($place['name']), $this->escape($place['name']), $this->addAttributes($place['attributes']));
125154
}
126155

127156
return $code;
@@ -132,19 +161,23 @@ protected function addTransitions(array $transitions)
132161
*/
133162
protected function findEdges(Definition $definition)
134163
{
164+
$workflowMetadata = $definition->getMetadataStore();
165+
135166
$dotEdges = [];
136167

137168
foreach ($definition->getTransitions() as $transition) {
169+
$transitionName = $workflowMetadata->getMetadata('label', $transition) ?? $transition->getName();
170+
138171
foreach ($transition->getFroms() as $from) {
139172
$dotEdges[] = [
140173
'from' => $from,
141-
'to' => $transition->getName(),
174+
'to' => $transitionName,
142175
'direction' => 'from',
143176
];
144177
}
145178
foreach ($transition->getTos() as $to) {
146179
$dotEdges[] = [
147-
'from' => $transition->getName(),
180+
'from' => $transitionName,
148181
'to' => $to,
149182
'direction' => 'to',
150183
];
@@ -209,15 +242,15 @@ protected function escape($value): string
209242
return \is_bool($value) ? ($value ? '1' : '0') : \addslashes($value);
210243
}
211244

212-
private function addAttributes(array $attributes): string
245+
protected function addAttributes(array $attributes): string
213246
{
214247
$code = [];
215248

216249
foreach ($attributes as $k => $v) {
217250
$code[] = sprintf('%s="%s"', $k, $this->escape($v));
218251
}
219252

220-
return $code ? ', '.implode(', ', $code) : '';
253+
return $code ? ' '.implode(' ', $code) : '';
221254
}
222255

223256
private function addOptions(array $options): string

src/Symfony/Component/Workflow/Dumper/PlantUmlDumper.php

Lines changed: 99 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
use InvalidArgumentException;
1515
use Symfony\Component\Workflow\Definition;
1616
use Symfony\Component\Workflow\Marking;
17+
use Symfony\Component\Workflow\Metadata\MetadataStoreInterface;
18+
use Symfony\Component\Workflow\Transition;
1719

1820
/**
1921
* PlantUmlDumper dumps a workflow as a PlantUML file.
@@ -63,13 +65,13 @@ public function __construct(string $transitionType = null)
6365
public function dump(Definition $definition, Marking $marking = null, array $options = []): string
6466
{
6567
$options = array_replace_recursive(self::DEFAULT_OPTIONS, $options);
66-
$code = $this->initialize($options);
68+
69+
$workflowMetadata = $definition->getMetadataStore();
70+
71+
$code = $this->initialize($options, $definition);
72+
6773
foreach ($definition->getPlaces() as $place) {
68-
$placeEscaped = $this->escape($place);
69-
$code[] =
70-
"state $placeEscaped".
71-
($definition->getInitialPlace() === $place ? ' '.self::INITIAL : '').
72-
($marking && $marking->has($place) ? ' '.self::MARKED : '');
74+
$code[] = $this->getState($place, $definition, $marking);
7375
}
7476
if ($this->isWorkflowTransitionType()) {
7577
foreach ($definition->getTransitions() as $transition) {
@@ -83,18 +85,34 @@ public function dump(Definition $definition, Marking $marking = null, array $opt
8385
$fromEscaped = $this->escape($from);
8486
foreach ($transition->getTos() as $to) {
8587
$toEscaped = $this->escape($to);
88+
89+
$transitionEscapedWithStyle = $this->getTransitionEscapedWithStyle($workflowMetadata, $transition, $transitionEscaped);
90+
91+
$arrowColor = $workflowMetadata->getMetadata('arrow_color', $transition);
92+
93+
$transitionColor = '';
94+
if (null !== $arrowColor) {
95+
$transitionColor = $this->getTransitionColor($arrowColor) ?? '';
96+
}
97+
8698
if ($this->isWorkflowTransitionType()) {
99+
$transitionLabel = '';
100+
// Add label only if it has a style
101+
if ($transitionEscapedWithStyle != $transitionEscaped) {
102+
$transitionLabel = ": $transitionEscapedWithStyle";
103+
}
104+
87105
$lines = [
88-
"$fromEscaped --> $transitionEscaped",
89-
"$transitionEscaped --> $toEscaped",
106+
"$fromEscaped -${transitionColor}-> ${transitionEscaped}${transitionLabel}",
107+
"$transitionEscaped -${transitionColor}-> ${toEscaped}${transitionLabel}",
90108
];
91109
foreach ($lines as $line) {
92110
if (!\in_array($line, $code)) {
93111
$code[] = $line;
94112
}
95113
}
96114
} else {
97-
$code[] = "$fromEscaped --> $toEscaped: $transitionEscaped";
115+
$code[] = "$fromEscaped -${transitionColor}-> $toEscaped: $transitionEscapedWithStyle";
98116
}
99117
}
100118
}
@@ -126,15 +144,28 @@ private function getLines(array $code): string
126144
return implode(PHP_EOL, $code);
127145
}
128146

129-
private function initialize(array $options): array
147+
private function initialize(array $options, Definition $definition): array
130148
{
149+
$workflowMetadata = $definition->getMetadataStore();
150+
131151
$code = [];
132152
if (isset($options['title'])) {
133153
$code[] = "title {$options['title']}";
134154
}
135155
if (isset($options['name'])) {
136156
$code[] = "title {$options['name']}";
137157
}
158+
159+
// Add style from nodes
160+
foreach ($definition->getPlaces() as $place) {
161+
$backgroundColor = $workflowMetadata->getMetadata('bg_color', $place);
162+
if (null !== $backgroundColor) {
163+
$key = 'BackgroundColor<<'.$this->getColorId($backgroundColor).'>>';
164+
165+
$options['skinparams']['state'][$key] = $backgroundColor;
166+
}
167+
}
168+
138169
if (isset($options['skinparams']) && \is_array($options['skinparams'])) {
139170
foreach ($options['skinparams'] as $skinparamKey => $skinparamValue) {
140171
if (!$this->isWorkflowTransitionType() && 'agent' === $skinparamKey) {
@@ -160,4 +191,62 @@ private function escape(string $string): string
160191
// It's not possible to escape property double quote, so let's remove it
161192
return '"'.str_replace('"', '', $string).'"';
162193
}
194+
195+
private function getState(string $place, Definition $definition, Marking $marking = null): string
196+
{
197+
$workflowMetadata = $definition->getMetadataStore();
198+
199+
$placeEscaped = $this->escape($place);
200+
201+
$output = "state $placeEscaped".
202+
($definition->getInitialPlace() === $place ? ' '.self::INITIAL : '').
203+
($marking && $marking->has($place) ? ' '.self::MARKED : '');
204+
205+
$backgroundColor = $workflowMetadata->getMetadata('bg_color', $place);
206+
if (null !== $backgroundColor) {
207+
$output .= ' <<'.$this->getColorId($backgroundColor).'>>';
208+
}
209+
210+
$description = $workflowMetadata->getMetadata('description', $place);
211+
if (null !== $description) {
212+
$output .= ' as '.$place.
213+
PHP_EOL.
214+
$place.' : '.$description;
215+
}
216+
217+
return $output;
218+
}
219+
220+
private function getTransitionEscapedWithStyle(MetadataStoreInterface $workflowMetadata, Transition $transition, string $to): string
221+
{
222+
$to = $workflowMetadata->getMetadata('label', $transition) ?? $to;
223+
224+
$color = $workflowMetadata->getMetadata('color', $transition) ?? null;
225+
226+
if (null !== $color) {
227+
$to = sprintf(
228+
'<font color=%1$s>%2$s</font>',
229+
$color,
230+
$to
231+
);
232+
}
233+
234+
return $this->escape($to);
235+
}
236+
237+
private function getTransitionColor(string $color): string
238+
{
239+
// PUML format requires that color in transition have to be prefixed with “#”.
240+
if ('#' !== substr($color, 0, 1)) {
241+
$color = '#'.$color;
242+
}
243+
244+
return sprintf('[%s]', $color);
245+
}
246+
247+
private function getColorId(string $color): string
248+
{
249+
// Remove “#“ from start of the color name so it can be used as an identifier.
250+
return ltrim($color, '#');
251+
}
163252
}

src/Symfony/Component/Workflow/Dumper/StateMachineGraphvizDumper.php

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,32 @@ public function dump(Definition $definition, Marking $marking = null, array $opt
4646
*/
4747
protected function findEdges(Definition $definition)
4848
{
49+
$workflowMetadata = $definition->getMetadataStore();
50+
4951
$edges = [];
5052

5153
foreach ($definition->getTransitions() as $transition) {
54+
$attributes = [];
55+
56+
$transitionName = $workflowMetadata->getMetadata('label', $transition) ?? $transition->getName();
57+
58+
$labelColor = $workflowMetadata->getMetadata('color', $transition);
59+
if (null !== $labelColor) {
60+
$attributes['fontcolor'] = $labelColor;
61+
}
62+
$arrowColor = $workflowMetadata->getMetadata('arrow_color', $transition);
63+
if (null !== $arrowColor) {
64+
$attributes['color'] = $arrowColor;
65+
}
66+
5267
foreach ($transition->getFroms() as $from) {
5368
foreach ($transition->getTos() as $to) {
54-
$edges[$from][] = [
55-
'name' => $transition->getName(),
69+
$edge = [
70+
'name' => $transitionName,
5671
'to' => $to,
72+
'attributes' => $attributes,
5773
];
74+
$edges[$from][] = $edge;
5875
}
5976
}
6077
}
@@ -71,7 +88,14 @@ protected function addEdges(array $edges)
7188

7289
foreach ($edges as $id => $edges) {
7390
foreach ($edges as $edge) {
74-
$code .= sprintf(" place_%s -> place_%s [label=\"%s\" style=\"%s\"];\n", $this->dotize($id), $this->dotize($edge['to']), $this->escape($edge['name']), 'solid');
91+
$code .= sprintf(
92+
" place_%s -> place_%s [label=\"%s\" style=\"%s\"%s];\n",
93+
$this->dotize($id),
94+
$this->dotize($edge['to']),
95+
$this->escape($edge['name']),
96+
'solid',
97+
$this->addAttributes($edge['attributes'])
98+
);
7599
}
76100
}
77101

0 commit comments

Comments
 (0)