Skip to content
This repository was archived by the owner on Sep 16, 2021. It is now read-only.

Commit fde5bfd

Browse files
Ben Glassmandbu
authored andcommitted
Add CreateMenuItemFromNodeEvent and dispatch in createFromNode.
1 parent 0e74d9a commit fde5bfd

File tree

6 files changed

+201
-60
lines changed

6 files changed

+201
-60
lines changed

ContentAwareFactory.php

Lines changed: 31 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,21 @@
99
* file that was distributed with this source code.
1010
*/
1111

12-
1312
namespace Symfony\Cmf\Bundle\MenuBundle;
1413

1514
use Knp\Menu\Silex\RouterAwareFactory;
1615
use Knp\Menu\ItemInterface;
1716
use Knp\Menu\NodeInterface;
1817
use Knp\Menu\MenuItem;
1918

19+
use Psr\Log\LoggerInterface;
20+
2021
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
2122
use Symfony\Component\Routing\Exception\RouteNotFoundException;
22-
use Symfony\Component\Security\Core\SecurityContextInterface;
23-
use Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishWorkflowChecker;
24-
25-
use Psr\Log\LoggerInterface;
2623

2724
use Symfony\Cmf\Bundle\MenuBundle\Voter\VoterInterface;
25+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
26+
use Symfony\Cmf\Bundle\MenuBundle\Event\CreateMenuItemFromNodeEvent;
2827

2928
/**
3029
* This factory builds menu items from the menu nodes and builds urls based on
@@ -60,18 +59,6 @@ class ContentAwareFactory extends RouterAwareFactory
6059
*/
6160
private $logger;
6261

63-
/**
64-
* @var SecurityContextInterface
65-
*/
66-
private $securityContext;
67-
68-
/**
69-
* The permission to check for when doing the publish workflow check.
70-
*
71-
* @var string
72-
*/
73-
private $publishWorkflowPermission = PublishWorkflowChecker::VIEW_ATTRIBUTE;
74-
7562
/**
7663
* Whether to return null or a MenuItem without any URL if no URL can be
7764
* found for a MenuNode.
@@ -84,22 +71,20 @@ class ContentAwareFactory extends RouterAwareFactory
8471
* @param UrlGeneratorInterface $generator for the parent class
8572
* @param UrlGeneratorInterface $contentRouter to generate routes when
8673
* content is set
87-
* @param SecurityContextInterface $securityContext the publish workflow
88-
* checker to check if menu items are published.
8974
* @param LoggerInterface $logger
9075
*/
9176
public function __construct(
9277
UrlGeneratorInterface $generator,
9378
UrlGeneratorInterface $contentRouter,
94-
SecurityContextInterface $securityContext,
95-
LoggerInterface $logger
79+
LoggerInterface $logger,
80+
EventDispatcherInterface $dispatcher
9681
)
9782
{
9883
parent::__construct($generator);
9984
$this->contentRouter = $contentRouter;
100-
$this->securityContext = $securityContext;
10185
$this->logger = $logger;
10286
$this->linkTypes = array('route', 'uri', 'content');
87+
$this->dispatcher = $dispatcher;
10388
}
10489

10590
/**
@@ -124,17 +109,6 @@ public function setAllowEmptyItems($allowEmptyItems)
124109
$this->allowEmptyItems = $allowEmptyItems;
125110
}
126111

127-
/**
128-
* What attribute to use in the publish workflow check. This typically
129-
* is VIEW or VIEW_ANONYMOUS.
130-
*
131-
* @param string $attribute
132-
*/
133-
public function setPublishWorkflowPermission($attribute)
134-
{
135-
$this->publishWorkflowPermission = $attribute;
136-
}
137-
138112
/**
139113
* Add a voter to decide on current item.
140114
*
@@ -169,17 +143,36 @@ private function getVoters()
169143
*/
170144
public function createFromNode(NodeInterface $node)
171145
{
172-
$item = $this->createItem($node->getName(), $node->getOptions());
146+
$event = new CreateMenuItemFromNodeEvent($node, $this);
147+
$this->dispatcher->dispatch('cmf_menu.create_menu_item_from_node', $event);
148+
149+
if ($event->getSkipNode()) {
150+
return null;
151+
}
152+
153+
$item = $event->getItem() ?: $this->createItem($node->getName(), $node->getOptions());
173154

174155
if (empty($item)) {
175156
return null;
176157
}
177158

178-
foreach ($node->getChildren() as $childNode) {
179-
if (!$this->securityContext->isGranted($this->publishWorkflowPermission, $childNode)) {
180-
continue;
181-
}
159+
if ($event->getSkipChildren()) {
160+
return $item;
161+
}
182162

163+
return $this->addChildrenFromNode($node, $item);
164+
}
165+
166+
/**
167+
* Add children to a menu item from a node
168+
*
169+
* @param NodeInterface $node
170+
* @param ItemInterface $item
171+
* @return ItemInterface
172+
*/
173+
public function addChildrenFromNode(NodeInterface $node, ItemInterface $item)
174+
{
175+
foreach ($node->getChildren() as $childNode) {
183176
if ($childNode instanceof NodeInterface) {
184177
$child = $this->createFromNode($childNode);
185178
if (!empty($child)) {
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony CMF package.
5+
*
6+
* (c) 2011-2013 Symfony CMF
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\Cmf\Bundle\MenuBundle\Event;
13+
14+
use Knp\Menu\MenuItem;
15+
use Knp\Menu\NodeInterface;
16+
17+
use Symfony\Component\EventDispatcher\Event;
18+
19+
use Symfony\Cmf\Bundle\MenuBundle\ContentAwareFactory;
20+
21+
class CreateMenuItemFromNodeEvent extends Event
22+
{
23+
/**
24+
* @var NodeInterface
25+
*/
26+
protected $node;
27+
28+
/**
29+
* @var ContentAwareFactory
30+
*/
31+
protected $factory;
32+
33+
/**
34+
* @var MenuItem
35+
*/
36+
protected $item;
37+
38+
/**
39+
* Whether or not to skip processing of this node
40+
*
41+
* @var boolean
42+
*/
43+
protected $skipNode = false;
44+
45+
/**
46+
* Whether or not to skip processing of child nodes
47+
*
48+
* @var boolean
49+
*/
50+
protected $skipChildren = false;
51+
52+
public function __construct(
53+
NodeInterface $node,
54+
ContentAwareFactory $factory
55+
) {
56+
$this->node = $node;
57+
$this->factory = $factory;
58+
}
59+
60+
public function getItem()
61+
{
62+
return $this->item;
63+
}
64+
65+
public function setItem(MenuItem $item = null)
66+
{
67+
$this->item = $item;
68+
}
69+
70+
public function getFactory()
71+
{
72+
return $this->factory;
73+
}
74+
75+
public function getNode()
76+
{
77+
return $this->node;
78+
}
79+
80+
public function setSkipNode($skipNode)
81+
{
82+
$this->skipNode = (bool) $skipNode;
83+
}
84+
85+
public function getSkipNode()
86+
{
87+
return $this->skipNode;
88+
}
89+
90+
public function setSkipChildren($skipChildren)
91+
{
92+
$this->skipChildren = (bool) $skipChildren;
93+
}
94+
95+
public function getSkipChildren()
96+
{
97+
return $this->skipChildren;
98+
}
99+
100+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
namespace Symfony\Cmf\Bundle\MenuBundle\PublishWorkflow;
4+
5+
use Symfony\Cmf\Bundle\MenuBundle\Event\CreateMenuItemFromNodeEvent;
6+
use Symfony\Cmf\Bundle\CoreBundle\PublishWorkflow\PublishWorkflowChecker;
7+
use Symfony\Component\Security\Core\SecurityContextInterface;
8+
use Symfony\Cmf\Bundle\MenuBundle\Voter\VoterInterface;
9+
use Symfony\Cmf\Bundle\MenuBundle\Model\Menu;
10+
11+
class CreateMenuItemFromNodeListener
12+
{
13+
/**
14+
* @var SecurityContextInterface
15+
*/
16+
private $securityContext;
17+
18+
/**
19+
* The permission to check for when doing the publish workflow check.
20+
*
21+
* @var string
22+
*/
23+
private $publishWorkflowPermission = PublishWorkflowChecker::VIEW_ATTRIBUTE;
24+
25+
public function __construct(SecurityContextInterface $securityContext)
26+
{
27+
$this->securityContext = $securityContext;
28+
}
29+
30+
public function onCreateMenuItemFromNode(CreateMenuItemFromNodeEvent $event)
31+
{
32+
$node = $event->getNode();
33+
$item = $event->getItem();
34+
$factory = $event->getFactory();
35+
36+
if ($node instanceof Menu) {
37+
return;
38+
}
39+
40+
if (!$this->securityContext->isGranted($this->publishWorkflowPermission, $node)) {
41+
$event->setSkipNode(true);
42+
}
43+
44+
}
45+
}
46+

Resources/config/menu.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
<service id="cmf_menu.factory" class="%cmf_menu.factory.class%">
1414
<argument type="service" id="router"/>
1515
<argument/> <!-- content url generator -->
16-
<argument type="service" id="cmf_core.publish_workflow.checker"/>
1716
<argument type="service" id="logger"/>
17+
<argument type="service" id="event_dispatcher"/>
1818
<call method="setAllowEmptyItems">
1919
<argument>%cmf_menu.allow_empty_items%</argument>
2020
</call>

Resources/config/publish-workflow.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@
1818
<tag name="cmf_published_voter" priority="30"/>
1919
</service>
2020

21+
<service id="cmf_menu.publish_workflow.create_menu_item_from_node_listener"
22+
class="Symfony\Cmf\Bundle\MenuBundle\PublishWorkflow\CreateMenuItemFromNodeListener">
23+
<argument type="service" id="cmf_core.publish_workflow.checker"/>
24+
<tag name="kernel.event_listener" event="cmf_menu.create_menu_item_from_node" method="onCreateMenuItemFromNode" />
25+
</service>
26+
27+
2128
</services>
2229

2330
</container>

Tests/Unit/ContentAwareFactoryTest.php

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ class ContentAwareFactoryTest extends \PHPUnit_Framework_Testcase
1717
{
1818
private $urlGenerator;
1919
private $contentUrlGenerator;
20-
private $securityContext;
2120
private $logger;
21+
private $dispatcher;
2222

2323
private $node1;
2424
private $node2;
@@ -33,18 +33,18 @@ public function setUp()
3333
$this->contentUrlGenerator = $this->getMock(
3434
'Symfony\Component\Routing\Generator\UrlGeneratorInterface'
3535
);
36-
$this->securityContext = $this->getMock(
37-
'Symfony\Component\Security\Core\SecurityContextInterface'
38-
);
3936
$this->logger = $this->getMock(
4037
'Psr\Log\LoggerInterface'
4138
);
39+
$this->dispatcher = $this->getMock(
40+
'Symfony\Component\EventDispatcher\EventDispatcherInterface'
41+
);
4242

4343
$this->factory = new ContentAwareFactory(
4444
$this->urlGenerator,
4545
$this->contentUrlGenerator,
46-
$this->securityContext,
4746
$this->logger,
47+
$this->dispatcher,
4848
false // refactore this empty items option
4949
);
5050

@@ -79,9 +79,9 @@ public function testCreateFromNode($options)
7979
->method('generate')
8080
->will($this->returnValue('foobar'));
8181

82-
$this->node1->expects($this->once())
82+
$this->node1->expects($this->any())
8383
->method('getOptions')->will($this->returnValue(array()));
84-
$this->node3->expects($this->once())
84+
$this->node3->expects($this->any())
8585
->method('getOptions')->will($this->returnValue(array()));
8686

8787
$this->node1->expects($this->once())
@@ -95,28 +95,23 @@ public function testCreateFromNode($options)
9595
->method('getChildren')
9696
->will($this->returnValue(array()));
9797

98-
$mock = $this->securityContext->expects($this->at(0))
99-
->method('isGranted')
100-
->with('VIEW', $this->node2)
101-
;
102-
10398
if ($options['node2_is_published']) {
104-
$mock->will($this->returnValue(true));
105-
$this->node2->expects($this->once())
99+
$this->node2->expects($this->any())
106100
->method('getOptions')->will($this->returnValue(array()));
107101
$this->node2->expects($this->once())
108102
->method('getChildren')
109103
->will($this->returnValue(array()));
110104
} else {
111-
$mock->will($this->returnValue(false));
105+
$node2 = $this->node2;
106+
$this->dispatcher->expects($this->any())
107+
->method('dispatch')
108+
->will($this->returnCallback(function ($name, $event) use ($options, $node2) {
109+
if ($event->getNode() === $node2) {
110+
$event->setSkipNode(true);
111+
}
112+
}));
112113
}
113114

114-
$this->securityContext->expects($this->at(1))
115-
->method('isGranted')
116-
->with('VIEW', $this->node3)
117-
->will($this->returnValue(true))
118-
;
119-
120115
$res = $this->factory->createFromNode($this->node1);
121116
$this->assertInstanceOf('Knp\Menu\MenuItem', $res);
122117
}

0 commit comments

Comments
 (0)