LocalActionManager.php 7.2 KB
Newer Older
1 2 3 4 5 6 7 8 9
<?php

/**
 * @file
 * Contains \Drupal\Core\Menu\LocalActionManager.
 */

namespace Drupal\Core\Menu;

10
use Drupal\Core\Access\AccessManager;
11
use Drupal\Core\Cache\CacheBackendInterface;
12
use Drupal\Core\Extension\ModuleHandlerInterface;
13
use Drupal\Core\Language\LanguageManager;
14
use Drupal\Core\Language\LanguageManagerInterface;
15 16
use Drupal\Core\Menu\LocalActionInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
17 18 19 20 21
use Drupal\Component\Plugin\Discovery\ProcessDecorator;
use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
use Drupal\Core\Plugin\Discovery\YamlDiscovery;
use Drupal\Core\Plugin\Factory\ContainerFactory;
use Drupal\Core\Routing\RouteProviderInterface;
22
use Symfony\Component\HttpFoundation\RequestStack;
23
use Symfony\Component\HttpKernel\Controller\ControllerResolverInterface;
24
use Drupal\Core\Session\AccountInterface;
25 26 27 28 29 30 31 32 33 34 35

/**
 * Manages discovery and instantiation of menu local action plugins.
 *
 * Menu local actions are links that lead to actions like "add new". The plugin
 * format allows them (if needed) to dynamically generate a title or the path
 * they link to. The annotation on the plugin provides the default title,
 * and the list of routes where the action should be rendered.
 */
class LocalActionManager extends DefaultPluginManager {

36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
  /**
   * Provides some default values for all local action plugins.
   *
   * @var array
   */
  protected $defaults = array(
    // The plugin id. Set by the plugin system based on the top-level YAML key.
    'id' => NULL,
    // The static title for the local action.
    'title' => '',
    // The weight of the local action.
    'weight' => NULL,
    // (Required) the route name used to generate a link.
    'route_name' => NULL,
    // Default route parameters for generating links.
    'route_parameters' => array(),
    // Associative array of link options.
    'options' => array(),
    // The route names where this local action appears.
    'appears_on' => array(),
    // Default class for local action implementations.
    'class' => 'Drupal\Core\Menu\LocalActionDefault',
  );

60 61 62 63 64 65 66 67
  /**
   * A controller resolver object.
   *
   * @var \Symfony\Component\HttpKernel\Controller\ControllerResolverInterface
   */
  protected $controllerResolver;

  /**
68
   * The request stack.
69
   *
70
   * @var \Symfony\Component\HttpFoundation\RequestStack
71
   */
72
  protected $requestStack;
73 74

  /**
75 76 77 78 79 80 81 82 83 84 85 86 87
   * The route provider to load routes by name.
   *
   * @var \Drupal\Core\Routing\RouteProviderInterface
   */
  protected $routeProvider;

  /**
   * The access manager.
   *
   * @var \Drupal\Core\Access\AccessManager
   */
  protected $accessManager;

88 89 90 91 92 93 94
  /**
   * The current user.
   *
   * @var \Drupal\Core\Session\AccountInterface
   */
  protected $account;

95
  /**
96 97
   * The plugin instances.
   *
98
   * @var \Drupal\Core\Menu\LocalActionInterface[]
99 100 101 102 103 104 105 106
   */
  protected $instances = array();

  /**
   * Constructs a LocalActionManager object.
   *
   * @param \Symfony\Component\HttpKernel\Controller\ControllerResolverInterface $controller_resolver
   *   An object to use in introspecting route methods.
107 108
   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
   *   The request stack.
109 110
   * @param \Drupal\Core\Routing\RouteProviderInterface $route_provider
   *   The route provider.
111 112
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
   *   The module handler.
113 114
   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
   *   Cache backend instance to use.
115
   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
116
   *   The language manager.
117 118
   * @param \Drupal\Core\Access\AccessManager $access_manager
   *   The access manager.
119 120
   * @param \Drupal\Core\Session\AccountInterface $account
   *   The current user.
121
   */
122
  public function __construct(ControllerResolverInterface $controller_resolver, RequestStack $request_stack, RouteProviderInterface $route_provider, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager, AccessManager $access_manager, AccountInterface $account) {
123 124
    // Skip calling the parent constructor, since that assumes annotation-based
    // discovery.
125
    $this->discovery = new YamlDiscovery('links.action', $module_handler->getModuleDirectories());
126 127
    $this->discovery = new ContainerDerivativeDiscoveryDecorator($this->discovery);
    $this->factory = new ContainerFactory($this);
128 129
    $this->controllerResolver = $controller_resolver;
    $this->requestStack = $request_stack;
130 131
    $this->routeProvider = $route_provider;
    $this->accessManager = $access_manager;
132
    $this->moduleHandler = $module_handler;
133
    $this->account = $account;
134
    $this->alterInfo('menu_local_actions');
135
    $this->setCacheBackend($cache_backend, 'local_action_plugins:' . $language_manager->getCurrentLanguage()->getId(), array('local_action' => TRUE));
136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
  }

  /**
   * Gets the title for a local action.
   *
   * @param \Drupal\Core\Menu\LocalActionInterface $local_action
   *   An object to get the title from.
   *
   * @return string
   *   The title (already localized).
   *
   * @throws \BadMethodCallException
   *   If the plugin does not implement the getTitle() method.
   */
  public function getTitle(LocalActionInterface $local_action) {
    $controller = array($local_action, 'getTitle');
152
    $arguments = $this->controllerResolver->getArguments($this->requestStack->getCurrentRequest(), $controller);
153 154 155 156 157 158
    return call_user_func_array($controller, $arguments);
  }

  /**
   * Finds all local actions that appear on a named route.
   *
159 160
   * @param string $route_appears
   *   The route name for which to find local actions.
161
   *
162 163
   * @return array
   *   An array of link render arrays.
164
   */
165 166 167 168
  public function getActionsForRoute($route_appears) {
    if (!isset($this->instances[$route_appears])) {
      $route_names = array();
      $this->instances[$route_appears] = array();
169 170
      // @todo - optimize this lookup by compiling or caching.
      foreach ($this->getDefinitions() as $plugin_id => $action_info) {
171
        if (in_array($route_appears, $action_info['appears_on'])) {
172
          $plugin = $this->createInstance($plugin_id);
173 174
          $route_names[] = $plugin->getRouteName();
          $this->instances[$route_appears][$plugin_id] = $plugin;
175 176
        }
      }
177 178 179 180 181 182 183
      // Pre-fetch all the action route objects. This reduces the number of SQL
      // queries that would otherwise be triggered by the access manager.
      if (!empty($route_names)) {
        $this->routeProvider->getRoutesByNames($route_names);
      }
    }
    $links = array();
184
    $request = $this->requestStack->getCurrentRequest();
185
    foreach ($this->instances[$route_appears] as $plugin_id => $plugin) {
186
      $route_name = $plugin->getRouteName();
187
      $route_parameters = $plugin->getRouteParameters($request);
188
      $links[$plugin_id] = array(
189 190 191 192 193
        '#theme' => 'menu_local_action',
        '#link' => array(
          'title' => $this->getTitle($plugin),
          'route_name' => $route_name,
          'route_parameters' => $route_parameters,
194
          'localized_options' => $plugin->getOptions($request),
195
        ),
196
        '#access' => $this->accessManager->checkNamedRoute($route_name, $route_parameters, $this->account),
197 198
        '#weight' => $plugin->getWeight(),
      );
199
    }
200
    return $links;
201 202 203
  }

}