Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 078e27f

Browse filesBrowse files
lyrixxfabpot
authored andcommitted
[Workflow] Added initial set of files
1 parent 17d59a7 commit 078e27f
Copy full SHA for 078e27f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Dismiss banner
Expand file treeCollapse file tree

43 files changed

+2032
-280
lines changed

‎composer.json

Copy file name to clipboardExpand all lines: composer.json
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
"symfony/validator": "self.version",
7373
"symfony/var-dumper": "self.version",
7474
"symfony/web-profiler-bundle": "self.version",
75+
"symfony/workflow": "self.version",
7576
"symfony/yaml": "self.version"
7677
},
7778
"require-dev": {
+52Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Twig\Extension;
13+
14+
use Symfony\Component\Workflow\Registry;
15+
16+
/**
17+
* WorkflowExtension.
18+
*
19+
* @author Grégoire Pineau <lyrixx@lyrixx.info>
20+
*/
21+
class WorkflowExtension extends \Twig_Extension
22+
{
23+
private $workflowRegistry;
24+
25+
public function __construct(Registry $workflowRegistry)
26+
{
27+
$this->workflowRegistry = $workflowRegistry;
28+
}
29+
30+
public function getFunctions()
31+
{
32+
return array(
33+
new \Twig_SimpleFunction('workflow_can', array($this, 'canTransition')),
34+
new \Twig_SimpleFunction('workflow_transitions', array($this, 'getEnabledTransitions')),
35+
);
36+
}
37+
38+
public function canTransition($object, $transition, $name = null)
39+
{
40+
return $this->workflowRegistry->get($object, $name)->can($object, $transition);
41+
}
42+
43+
public function getEnabledTransitions($object, $name = null)
44+
{
45+
return $this->workflowRegistry->get($object, $name)->getEnabledTransitions($object);
46+
}
47+
48+
public function getName()
49+
{
50+
return 'workflow';
51+
}
52+
}
+78Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bundle\FrameworkBundle\Command;
13+
14+
use Symfony\Component\Console\Input\InputArgument;
15+
use Symfony\Component\Console\Input\InputInterface;
16+
use Symfony\Component\Console\Output\OutputInterface;
17+
use Symfony\Component\Workflow\Dumper\GraphvizDumper;
18+
use Symfony\Component\Workflow\Marking;
19+
20+
/**
21+
* @author Grégoire Pineau <lyrixx@lyrixx.info>
22+
*/
23+
class WorkflowDumpCommand extends ContainerAwareCommand
24+
{
25+
public function isEnabled()
26+
{
27+
return $this->getContainer()->has('workflow.registry');
28+
}
29+
30+
/**
31+
* {@inheritdoc}
32+
*/
33+
protected function configure()
34+
{
35+
$this
36+
->setName('workflow:dump')
37+
->setDefinition(array(
38+
new InputArgument('name', InputArgument::REQUIRED, 'A workflow name'),
39+
new InputArgument('marking', InputArgument::IS_ARRAY, 'A marking (a list of places)'),
40+
))
41+
->setDescription('Dump a workflow')
42+
->setHelp(<<<'EOF'
43+
The <info>%command.name%</info> command dumps the graphical representation of a
44+
workflow in DOT format
45+
46+
%command.full_name% <workflow name> | dot -Tpng > workflow.png
47+
48+
EOF
49+
)
50+
;
51+
}
52+
53+
/**
54+
* {@inheritdoc}
55+
*/
56+
protected function execute(InputInterface $input, OutputInterface $output)
57+
{
58+
$workflow = $this->getContainer()->get('workflow.'.$input->getArgument('name'));
59+
$definition = $this->getProperty($workflow, 'definition');
60+
61+
$dumper = new GraphvizDumper();
62+
63+
$marking = new Marking();
64+
foreach ($input->getArgument('marking') as $place) {
65+
$marking->mark($place);
66+
}
67+
68+
$output->writeln($dumper->dump($definition, $marking));
69+
}
70+
71+
private function getProperty($object, $property)
72+
{
73+
$reflectionProperty = new \ReflectionProperty(get_class($object), $property);
74+
$reflectionProperty->setAccessible(true);
75+
76+
return $reflectionProperty->getValue($object);
77+
}
78+
}

‎src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/Configuration.php
+94Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ public function getConfigTreeBuilder()
103103
$this->addSsiSection($rootNode);
104104
$this->addFragmentsSection($rootNode);
105105
$this->addProfilerSection($rootNode);
106+
$this->addWorkflowSection($rootNode);
106107
$this->addRouterSection($rootNode);
107108
$this->addSessionSection($rootNode);
108109
$this->addRequestSection($rootNode);
@@ -226,6 +227,99 @@ private function addProfilerSection(ArrayNodeDefinition $rootNode)
226227
;
227228
}
228229

230+
private function addWorkflowSection(ArrayNodeDefinition $rootNode)
231+
{
232+
$rootNode
233+
->children()
234+
->arrayNode('workflows')
235+
->useAttributeAsKey('name')
236+
->prototype('array')
237+
->children()
238+
->arrayNode('marking_store')
239+
->isRequired()
240+
->children()
241+
->enumNode('type')
242+
->values(array('property_accessor', 'scalar'))
243+
->end()
244+
->arrayNode('arguments')
245+
->beforeNormalization()
246+
->ifString()
247+
->then(function ($v) { return array($v); })
248+
->end()
249+
->prototype('scalar')
250+
->end()
251+
->end()
252+
->scalarNode('service')
253+
->cannotBeEmpty()
254+
->end()
255+
->end()
256+
->validate()
257+
->always(function ($v) {
258+
if (isset($v['type']) && isset($v['service'])) {
259+
throw new \InvalidArgumentException('"type" and "service" could not be used together.');
260+
}
261+
262+
return $v;
263+
})
264+
->end()
265+
->end()
266+
->arrayNode('supports')
267+
->isRequired()
268+
->beforeNormalization()
269+
->ifString()
270+
->then(function ($v) { return array($v); })
271+
->end()
272+
->prototype('scalar')
273+
->cannotBeEmpty()
274+
->validate()
275+
->ifTrue(function ($v) { return !class_exists($v); })
276+
->thenInvalid('The supported class %s does not exist.')
277+
->end()
278+
->end()
279+
->end()
280+
->arrayNode('places')
281+
->isRequired()
282+
->requiresAtLeastOneElement()
283+
->prototype('scalar')
284+
->cannotBeEmpty()
285+
->end()
286+
->end()
287+
->arrayNode('transitions')
288+
->useAttributeAsKey('name')
289+
->isRequired()
290+
->requiresAtLeastOneElement()
291+
->prototype('array')
292+
->children()
293+
->arrayNode('from')
294+
->beforeNormalization()
295+
->ifString()
296+
->then(function ($v) { return array($v); })
297+
->end()
298+
->requiresAtLeastOneElement()
299+
->prototype('scalar')
300+
->cannotBeEmpty()
301+
->end()
302+
->end()
303+
->arrayNode('to')
304+
->beforeNormalization()
305+
->ifString()
306+
->then(function ($v) { return array($v); })
307+
->end()
308+
->requiresAtLeastOneElement()
309+
->prototype('scalar')
310+
->cannotBeEmpty()
311+
->end()
312+
->end()
313+
->end()
314+
->end()
315+
->end()
316+
->end()
317+
->end()
318+
->end()
319+
->end()
320+
;
321+
}
322+
229323
private function addRouterSection(ArrayNodeDefinition $rootNode)
230324
{
231325
$rootNode

‎src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php
+51Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@
3131
use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
3232
use Symfony\Component\Serializer\Normalizer\JsonSerializableNormalizer;
3333
use Symfony\Component\Validator\Validation;
34+
use Symfony\Component\Workflow;
3435

3536
/**
3637
* FrameworkExtension.
3738
*
3839
* @author Fabien Potencier <fabien@symfony.com>
3940
* @author Jeremy Mikola <jmikola@gmail.com>
4041
* @author Kévin Dunglas <dunglas@gmail.com>
42+
* @author Grégoire Pineau <lyrixx@lyrixx.info>
4143
*/
4244
class FrameworkExtension extends Extension
4345
{
@@ -129,6 +131,7 @@ public function load(array $configs, ContainerBuilder $container)
129131
$this->registerTranslatorConfiguration($config['translator'], $container);
130132
$this->registerProfilerConfiguration($config['profiler'], $container, $loader);
131133
$this->registerCacheConfiguration($config['cache'], $container);
134+
$this->registerWorkflowConfiguration($config['workflows'], $container, $loader);
132135

133136
if ($this->isConfigEnabled($container, $config['router'])) {
134137
$this->registerRouterConfiguration($config['router'], $container, $loader);
@@ -346,6 +349,54 @@ private function registerProfilerConfiguration(array $config, ContainerBuilder $
346349
}
347350
}
348351

352+
/**
353+
* Loads the workflow configuration.
354+
*
355+
* @param array $workflows A workflow configuration array
356+
* @param ContainerBuilder $container A ContainerBuilder instance
357+
* @param XmlFileLoader $loader An XmlFileLoader instance
358+
*/
359+
private function registerWorkflowConfiguration(array $workflows, ContainerBuilder $container, XmlFileLoader $loader)
360+
{
361+
if (!$workflows) {
362+
return;
363+
}
364+
365+
$loader->load('workflow.xml');
366+
367+
$registryDefinition = $container->getDefinition('workflow.registry');
368+
369+
foreach ($workflows as $name => $workflow) {
370+
$definitionDefinition = new Definition(Workflow\Definition::class);
371+
$definitionDefinition->addMethodCall('addPlaces', array($workflow['places']));
372+
foreach ($workflow['transitions'] as $transitionName => $transition) {
373+
$definitionDefinition->addMethodCall('addTransition', array(new Definition(Workflow\Transition::class, array($transitionName, $transition['from'], $transition['to']))));
374+
}
375+
376+
if (isset($workflow['marking_store']['type'])) {
377+
$markingStoreDefinition = new DefinitionDecorator('workflow.marking_store.'.$workflow['marking_store']['type']);
378+
foreach ($workflow['marking_store']['arguments'] as $argument) {
379+
$markingStoreDefinition->addArgument($argument);
380+
}
381+
} else {
382+
$markingStoreDefinition = new Reference($workflow['marking_store']['service']);
383+
}
384+
385+
$workflowDefinition = new DefinitionDecorator('workflow.abstract');
386+
$workflowDefinition->replaceArgument(0, $definitionDefinition);
387+
$workflowDefinition->replaceArgument(1, $markingStoreDefinition);
388+
$workflowDefinition->replaceArgument(3, $name);
389+
390+
$workflowId = 'workflow.'.$name;
391+
392+
$container->setDefinition($workflowId, $workflowDefinition);
393+
394+
foreach ($workflow['supports'] as $supportedClass) {
395+
$registryDefinition->addMethodCall('add', array(new Reference($workflowId), $supportedClass));
396+
}
397+
}
398+
}
399+
349400
/**
350401
* Loads the router configuration.
351402
*

‎src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd

Copy file name to clipboardExpand all lines: src/Symfony/Bundle/FrameworkBundle/Resources/config/schema/symfony-1.0.xsd
+39Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
<xsd:element name="serializer" type="serializer" minOccurs="0" maxOccurs="1" />
2727
<xsd:element name="property-info" type="property_info" minOccurs="0" maxOccurs="1" />
2828
<xsd:element name="cache" type="cache" minOccurs="0" maxOccurs="1" />
29+
<xsd:element name="workflows" type="workflows" minOccurs="0" maxOccurs="1" />
2930
</xsd:all>
3031

3132
<xsd:attribute name="http-method-override" type="xsd:boolean" />
@@ -224,4 +225,42 @@
224225
<xsd:attribute name="provider" type="xsd:string" />
225226
<xsd:attribute name="clearer" type="xsd:string" />
226227
</xsd:complexType>
228+
229+
<xsd:complexType name="workflows">
230+
<xsd:choice minOccurs="0" maxOccurs="unbounded">
231+
<xsd:element name="workflow" type="workflow" />
232+
</xsd:choice>
233+
</xsd:complexType>
234+
235+
<xsd:complexType name="workflow">
236+
<xsd:sequence>
237+
<xsd:element name="marking-store" type="marking_store" />
238+
<xsd:element name="supports" type="xsd:string" minOccurs="1" maxOccurs="unbounded" />
239+
<xsd:element name="places" type="xsd:string" minOccurs="1" maxOccurs="unbounded" />
240+
<xsd:element name="transitions" type="transitions" />
241+
</xsd:sequence>
242+
<xsd:attribute name="name" type="xsd:string" />
243+
</xsd:complexType>
244+
245+
<xsd:complexType name="marking_store">
246+
<xsd:sequence>
247+
<xsd:element name="type" type="xsd:string" minOccurs="0" maxOccurs="1" />
248+
<xsd:element name="arguments" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
249+
<xsd:element name="service" type="xsd:string" minOccurs="0" maxOccurs="1" />
250+
</xsd:sequence>
251+
</xsd:complexType>
252+
253+
<xsd:complexType name="transitions">
254+
<xsd:sequence>
255+
<xsd:element name="transition" type="transition" />
256+
</xsd:sequence>
257+
</xsd:complexType>
258+
259+
<xsd:complexType name="transition">
260+
<xsd:sequence>
261+
<xsd:element name="from" type="xsd:string" minOccurs="1" maxOccurs="unbounded" />
262+
<xsd:element name="to" type="xsd:string" minOccurs="1" maxOccurs="unbounded" />
263+
</xsd:sequence>
264+
<xsd:attribute name="name" type="xsd:string" />
265+
</xsd:complexType>
227266
</xsd:schema>

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.