Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\Core\Menu;
|
Chris@0
|
4
|
Chris@0
|
5 use Drupal\Core\Access\AccessManagerInterface;
|
Chris@0
|
6 use Drupal\Core\Cache\CacheableMetadata;
|
Chris@0
|
7 use Drupal\Core\Cache\CacheBackendInterface;
|
Chris@17
|
8 use Drupal\Core\Controller\ControllerResolverInterface;
|
Chris@0
|
9 use Drupal\Core\Extension\ModuleHandlerInterface;
|
Chris@0
|
10 use Drupal\Core\Language\LanguageManagerInterface;
|
Chris@0
|
11 use Drupal\Core\Plugin\DefaultPluginManager;
|
Chris@0
|
12 use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
|
Chris@0
|
13 use Drupal\Core\Plugin\Discovery\YamlDiscovery;
|
Chris@0
|
14 use Drupal\Core\Plugin\Factory\ContainerFactory;
|
Chris@0
|
15 use Drupal\Core\Routing\RouteMatchInterface;
|
Chris@0
|
16 use Drupal\Core\Routing\RouteProviderInterface;
|
Chris@0
|
17 use Drupal\Core\Url;
|
Chris@0
|
18 use Symfony\Component\HttpFoundation\RequestStack;
|
Chris@17
|
19 use Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface;
|
Chris@0
|
20 use Drupal\Core\Session\AccountInterface;
|
Chris@0
|
21
|
Chris@0
|
22 /**
|
Chris@0
|
23 * Provides the default local action manager using YML as primary definition.
|
Chris@0
|
24 */
|
Chris@0
|
25 class LocalActionManager extends DefaultPluginManager implements LocalActionManagerInterface {
|
Chris@0
|
26
|
Chris@0
|
27 /**
|
Chris@0
|
28 * Provides some default values for all local action plugins.
|
Chris@0
|
29 *
|
Chris@0
|
30 * @var array
|
Chris@0
|
31 */
|
Chris@0
|
32 protected $defaults = [
|
Chris@0
|
33 // The plugin id. Set by the plugin system based on the top-level YAML key.
|
Chris@0
|
34 'id' => NULL,
|
Chris@0
|
35 // The static title for the local action.
|
Chris@0
|
36 'title' => '',
|
Chris@0
|
37 // The weight of the local action.
|
Chris@0
|
38 'weight' => NULL,
|
Chris@0
|
39 // (Required) the route name used to generate a link.
|
Chris@0
|
40 'route_name' => NULL,
|
Chris@0
|
41 // Default route parameters for generating links.
|
Chris@0
|
42 'route_parameters' => [],
|
Chris@0
|
43 // Associative array of link options.
|
Chris@0
|
44 'options' => [],
|
Chris@0
|
45 // The route names where this local action appears.
|
Chris@0
|
46 'appears_on' => [],
|
Chris@0
|
47 // Default class for local action implementations.
|
Chris@0
|
48 'class' => 'Drupal\Core\Menu\LocalActionDefault',
|
Chris@0
|
49 ];
|
Chris@0
|
50
|
Chris@0
|
51 /**
|
Chris@17
|
52 * An argument resolver object.
|
Chris@17
|
53 *
|
Chris@17
|
54 * @var \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface
|
Chris@17
|
55 */
|
Chris@17
|
56 protected $argumentResolver;
|
Chris@17
|
57
|
Chris@17
|
58 /**
|
Chris@0
|
59 * A controller resolver object.
|
Chris@0
|
60 *
|
Chris@0
|
61 * @var \Symfony\Component\HttpKernel\Controller\ControllerResolverInterface
|
Chris@17
|
62 *
|
Chris@17
|
63 * @deprecated
|
Chris@17
|
64 * Using the 'controller_resolver' service as the first argument is
|
Chris@17
|
65 * deprecated, use the 'http_kernel.controller.argument_resolver' instead.
|
Chris@17
|
66 * If your subclass requires the 'controller_resolver' service add it as an
|
Chris@17
|
67 * additional argument.
|
Chris@17
|
68 *
|
Chris@17
|
69 * @see https://www.drupal.org/node/2959408
|
Chris@0
|
70 */
|
Chris@0
|
71 protected $controllerResolver;
|
Chris@0
|
72
|
Chris@0
|
73 /**
|
Chris@0
|
74 * The request stack.
|
Chris@0
|
75 *
|
Chris@0
|
76 * @var \Symfony\Component\HttpFoundation\RequestStack
|
Chris@0
|
77 */
|
Chris@0
|
78 protected $requestStack;
|
Chris@0
|
79
|
Chris@0
|
80 /**
|
Chris@0
|
81 * The current route match.
|
Chris@0
|
82 *
|
Chris@0
|
83 * @var \Drupal\Core\Routing\RouteMatchInterface
|
Chris@0
|
84 */
|
Chris@0
|
85 protected $routeMatch;
|
Chris@0
|
86
|
Chris@0
|
87 /**
|
Chris@0
|
88 * The route provider to load routes by name.
|
Chris@0
|
89 *
|
Chris@0
|
90 * @var \Drupal\Core\Routing\RouteProviderInterface
|
Chris@0
|
91 */
|
Chris@0
|
92 protected $routeProvider;
|
Chris@0
|
93
|
Chris@0
|
94 /**
|
Chris@0
|
95 * The access manager.
|
Chris@0
|
96 *
|
Chris@0
|
97 * @var \Drupal\Core\Access\AccessManagerInterface
|
Chris@0
|
98 */
|
Chris@0
|
99 protected $accessManager;
|
Chris@0
|
100
|
Chris@0
|
101 /**
|
Chris@0
|
102 * The current user.
|
Chris@0
|
103 *
|
Chris@0
|
104 * @var \Drupal\Core\Session\AccountInterface
|
Chris@0
|
105 */
|
Chris@0
|
106 protected $account;
|
Chris@0
|
107
|
Chris@0
|
108 /**
|
Chris@0
|
109 * The plugin instances.
|
Chris@0
|
110 *
|
Chris@0
|
111 * @var \Drupal\Core\Menu\LocalActionInterface[]
|
Chris@0
|
112 */
|
Chris@0
|
113 protected $instances = [];
|
Chris@0
|
114
|
Chris@0
|
115 /**
|
Chris@0
|
116 * Constructs a LocalActionManager object.
|
Chris@0
|
117 *
|
Chris@17
|
118 * @param \Symfony\Component\HttpKernel\Controller\ArgumentResolverInterface $argument_resolver
|
Chris@17
|
119 * An object to use in resolving route arguments.
|
Chris@0
|
120 * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
|
Chris@0
|
121 * The request stack.
|
Chris@0
|
122 * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
|
Chris@0
|
123 * The current route match.
|
Chris@0
|
124 * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
|
Chris@0
|
125 * The route provider.
|
Chris@0
|
126 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
|
Chris@0
|
127 * The module handler.
|
Chris@0
|
128 * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
|
Chris@0
|
129 * Cache backend instance to use.
|
Chris@0
|
130 * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
|
Chris@0
|
131 * The language manager.
|
Chris@0
|
132 * @param \Drupal\Core\Access\AccessManagerInterface $access_manager
|
Chris@0
|
133 * The access manager.
|
Chris@0
|
134 * @param \Drupal\Core\Session\AccountInterface $account
|
Chris@0
|
135 * The current user.
|
Chris@0
|
136 */
|
Chris@17
|
137 public function __construct(ArgumentResolverInterface $argument_resolver, RequestStack $request_stack, RouteMatchInterface $route_match, RouteProviderInterface $route_provider, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager, AccessManagerInterface $access_manager, AccountInterface $account) {
|
Chris@0
|
138 // Skip calling the parent constructor, since that assumes annotation-based
|
Chris@0
|
139 // discovery.
|
Chris@0
|
140 $this->factory = new ContainerFactory($this, 'Drupal\Core\Menu\LocalActionInterface');
|
Chris@17
|
141 $this->argumentResolver = $argument_resolver;
|
Chris@17
|
142 if ($argument_resolver instanceof ControllerResolverInterface) {
|
Chris@17
|
143 @trigger_error("Using the 'controller_resolver' service as the first argument is deprecated, use the 'http_kernel.controller.argument_resolver' instead. If your subclass requires the 'controller_resolver' service add it as an additional argument. See https://www.drupal.org/node/2959408.", E_USER_DEPRECATED);
|
Chris@17
|
144 $this->controllerResolver = $argument_resolver;
|
Chris@17
|
145 }
|
Chris@0
|
146 $this->requestStack = $request_stack;
|
Chris@0
|
147 $this->routeMatch = $route_match;
|
Chris@0
|
148 $this->routeProvider = $route_provider;
|
Chris@0
|
149 $this->accessManager = $access_manager;
|
Chris@0
|
150 $this->moduleHandler = $module_handler;
|
Chris@0
|
151 $this->account = $account;
|
Chris@0
|
152 $this->alterInfo('menu_local_actions');
|
Chris@0
|
153 $this->setCacheBackend($cache_backend, 'local_action_plugins:' . $language_manager->getCurrentLanguage()->getId(), ['local_action']);
|
Chris@0
|
154 }
|
Chris@0
|
155
|
Chris@0
|
156 /**
|
Chris@0
|
157 * {@inheritdoc}
|
Chris@0
|
158 */
|
Chris@0
|
159 protected function getDiscovery() {
|
Chris@0
|
160 if (!isset($this->discovery)) {
|
Chris@0
|
161 $yaml_discovery = new YamlDiscovery('links.action', $this->moduleHandler->getModuleDirectories());
|
Chris@0
|
162 $yaml_discovery->addTranslatableProperty('title', 'title_context');
|
Chris@0
|
163 $this->discovery = new ContainerDerivativeDiscoveryDecorator($yaml_discovery);
|
Chris@0
|
164 }
|
Chris@0
|
165 return $this->discovery;
|
Chris@0
|
166 }
|
Chris@0
|
167
|
Chris@0
|
168 /**
|
Chris@0
|
169 * {@inheritdoc}
|
Chris@0
|
170 */
|
Chris@0
|
171 public function getTitle(LocalActionInterface $local_action) {
|
Chris@0
|
172 $controller = [$local_action, 'getTitle'];
|
Chris@17
|
173 $arguments = $this->argumentResolver->getArguments($this->requestStack->getCurrentRequest(), $controller);
|
Chris@0
|
174 return call_user_func_array($controller, $arguments);
|
Chris@0
|
175 }
|
Chris@0
|
176
|
Chris@0
|
177 /**
|
Chris@0
|
178 * {@inheritdoc}
|
Chris@0
|
179 */
|
Chris@0
|
180 public function getActionsForRoute($route_appears) {
|
Chris@0
|
181 if (!isset($this->instances[$route_appears])) {
|
Chris@0
|
182 $route_names = [];
|
Chris@0
|
183 $this->instances[$route_appears] = [];
|
Chris@0
|
184 // @todo - optimize this lookup by compiling or caching.
|
Chris@0
|
185 foreach ($this->getDefinitions() as $plugin_id => $action_info) {
|
Chris@0
|
186 if (in_array($route_appears, $action_info['appears_on'])) {
|
Chris@0
|
187 $plugin = $this->createInstance($plugin_id);
|
Chris@0
|
188 $route_names[] = $plugin->getRouteName();
|
Chris@0
|
189 $this->instances[$route_appears][$plugin_id] = $plugin;
|
Chris@0
|
190 }
|
Chris@0
|
191 }
|
Chris@0
|
192 // Pre-fetch all the action route objects. This reduces the number of SQL
|
Chris@0
|
193 // queries that would otherwise be triggered by the access manager.
|
Chris@0
|
194 if (!empty($route_names)) {
|
Chris@0
|
195 $this->routeProvider->getRoutesByNames($route_names);
|
Chris@0
|
196 }
|
Chris@0
|
197 }
|
Chris@0
|
198 $links = [];
|
Chris@17
|
199 $cacheability = new CacheableMetadata();
|
Chris@17
|
200 $cacheability->addCacheContexts(['route']);
|
Chris@0
|
201 /** @var $plugin \Drupal\Core\Menu\LocalActionInterface */
|
Chris@0
|
202 foreach ($this->instances[$route_appears] as $plugin_id => $plugin) {
|
Chris@0
|
203 $route_name = $plugin->getRouteName();
|
Chris@0
|
204 $route_parameters = $plugin->getRouteParameters($this->routeMatch);
|
Chris@0
|
205 $access = $this->accessManager->checkNamedRoute($route_name, $route_parameters, $this->account, TRUE);
|
Chris@0
|
206 $links[$plugin_id] = [
|
Chris@0
|
207 '#theme' => 'menu_local_action',
|
Chris@0
|
208 '#link' => [
|
Chris@0
|
209 'title' => $this->getTitle($plugin),
|
Chris@0
|
210 'url' => Url::fromRoute($route_name, $route_parameters),
|
Chris@0
|
211 'localized_options' => $plugin->getOptions($this->routeMatch),
|
Chris@0
|
212 ],
|
Chris@0
|
213 '#access' => $access,
|
Chris@0
|
214 '#weight' => $plugin->getWeight(),
|
Chris@0
|
215 ];
|
Chris@0
|
216 $cacheability->addCacheableDependency($access)->addCacheableDependency($plugin);
|
Chris@0
|
217 }
|
Chris@17
|
218 $cacheability->applyTo($links);
|
Chris@0
|
219
|
Chris@0
|
220 return $links;
|
Chris@0
|
221 }
|
Chris@0
|
222
|
Chris@0
|
223 }
|