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