ContextualLinkManager.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. <?php
  2. namespace Drupal\Core\Menu;
  3. use Drupal\Component\Plugin\Exception\PluginException;
  4. use Drupal\Core\Access\AccessManagerInterface;
  5. use Drupal\Core\Cache\CacheBackendInterface;
  6. use Drupal\Core\Controller\ControllerResolverInterface;
  7. use Drupal\Core\Extension\ModuleHandlerInterface;
  8. use Drupal\Core\Language\LanguageManagerInterface;
  9. use Drupal\Core\Plugin\DefaultPluginManager;
  10. use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
  11. use Drupal\Core\Plugin\Discovery\YamlDiscovery;
  12. use Drupal\Core\Plugin\Factory\ContainerFactory;
  13. use Drupal\Core\Session\AccountInterface;
  14. use Symfony\Component\HttpFoundation\RequestStack;
  15. /**
  16. * Defines a contextual link plugin manager to deal with contextual links.
  17. *
  18. * @see \Drupal\Core\Menu\ContextualLinkInterface
  19. */
  20. class ContextualLinkManager extends DefaultPluginManager implements ContextualLinkManagerInterface {
  21. /**
  22. * Provides default values for a contextual link definition.
  23. *
  24. * @var array
  25. */
  26. protected $defaults = [
  27. // (required) The name of the route to link to.
  28. 'route_name' => '',
  29. // (required) The contextual links group.
  30. 'group' => '',
  31. // The static title text for the link.
  32. 'title' => '',
  33. // The default link options.
  34. 'options' => [],
  35. // The weight of the link.
  36. 'weight' => NULL,
  37. // Default class for contextual link implementations.
  38. 'class' => '\Drupal\Core\Menu\ContextualLinkDefault',
  39. // The plugin id. Set by the plugin system based on the top-level YAML key.
  40. 'id' => '',
  41. ];
  42. /**
  43. * A controller resolver object.
  44. *
  45. * @var \Symfony\Component\HttpKernel\Controller\ControllerResolverInterface
  46. */
  47. protected $controllerResolver;
  48. /**
  49. * The access manager.
  50. *
  51. * @var \Drupal\Core\Access\AccessManagerInterface
  52. */
  53. protected $accessManager;
  54. /**
  55. * The current user.
  56. *
  57. * @var \Drupal\Core\Session\AccountInterface
  58. */
  59. protected $account;
  60. /**
  61. * The request stack.
  62. *
  63. * @var \Symfony\Component\HttpFoundation\RequestStack
  64. */
  65. protected $requestStack;
  66. /**
  67. * A static cache of all the contextual link plugins by group name.
  68. *
  69. * @var array
  70. */
  71. protected $pluginsByGroup;
  72. /**
  73. * Constructs a new ContextualLinkManager instance.
  74. *
  75. * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver
  76. * The controller resolver.
  77. * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
  78. * The module handler.
  79. * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
  80. * The cache backend.
  81. * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
  82. * The language manager.
  83. * @param \Drupal\Core\Access\AccessManagerInterface $access_manager
  84. * The access manager.
  85. * @param \Drupal\Core\Session\AccountInterface $account
  86. * The current user.
  87. * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
  88. * The request stack.
  89. */
  90. public function __construct(ControllerResolverInterface $controller_resolver, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager, AccessManagerInterface $access_manager, AccountInterface $account, RequestStack $request_stack) {
  91. $this->factory = new ContainerFactory($this, '\Drupal\Core\Menu\ContextualLinkInterface');
  92. $this->controllerResolver = $controller_resolver;
  93. $this->accessManager = $access_manager;
  94. $this->account = $account;
  95. $this->moduleHandler = $module_handler;
  96. $this->requestStack = $request_stack;
  97. $this->alterInfo('contextual_links_plugins');
  98. $this->setCacheBackend($cache_backend, 'contextual_links_plugins:' . $language_manager->getCurrentLanguage()->getId(), ['contextual_links_plugins']);
  99. }
  100. /**
  101. * {@inheritdoc}
  102. */
  103. protected function getDiscovery() {
  104. if (!isset($this->discovery)) {
  105. $yaml_discovery = new YamlDiscovery('links.contextual', $this->moduleHandler->getModuleDirectories());
  106. $yaml_discovery->addTranslatableProperty('title', 'title_context');
  107. $this->discovery = new ContainerDerivativeDiscoveryDecorator($yaml_discovery);
  108. }
  109. return $this->discovery;
  110. }
  111. /**
  112. * {@inheritdoc}
  113. */
  114. public function processDefinition(&$definition, $plugin_id) {
  115. parent::processDefinition($definition, $plugin_id);
  116. // If there is no route name, this is a broken definition.
  117. if (empty($definition['route_name'])) {
  118. throw new PluginException(sprintf('Contextual link plugin (%s) definition must include "route_name".', $plugin_id));
  119. }
  120. // If there is no group name, this is a broken definition.
  121. if (empty($definition['group'])) {
  122. throw new PluginException(sprintf('Contextual link plugin (%s) definition must include "group".', $plugin_id));
  123. }
  124. }
  125. /**
  126. * {@inheritdoc}
  127. */
  128. public function getContextualLinkPluginsByGroup($group_name) {
  129. if (isset($this->pluginsByGroup[$group_name])) {
  130. $contextual_links = $this->pluginsByGroup[$group_name];
  131. }
  132. elseif ($cache = $this->cacheBackend->get($this->cacheKey . ':' . $group_name)) {
  133. $contextual_links = $cache->data;
  134. $this->pluginsByGroup[$group_name] = $contextual_links;
  135. }
  136. else {
  137. $contextual_links = [];
  138. foreach ($this->getDefinitions() as $plugin_id => $plugin_definition) {
  139. if ($plugin_definition['group'] == $group_name) {
  140. $contextual_links[$plugin_id] = $plugin_definition;
  141. }
  142. }
  143. $this->cacheBackend->set($this->cacheKey . ':' . $group_name, $contextual_links);
  144. $this->pluginsByGroup[$group_name] = $contextual_links;
  145. }
  146. return $contextual_links;
  147. }
  148. /**
  149. * {@inheritdoc}
  150. */
  151. public function getContextualLinksArrayByGroup($group_name, array $route_parameters, array $metadata = []) {
  152. $links = [];
  153. $request = $this->requestStack->getCurrentRequest();
  154. foreach ($this->getContextualLinkPluginsByGroup($group_name) as $plugin_id => $plugin_definition) {
  155. /** @var $plugin \Drupal\Core\Menu\ContextualLinkInterface */
  156. $plugin = $this->createInstance($plugin_id);
  157. $route_name = $plugin->getRouteName();
  158. // Check access.
  159. if (!$this->accessManager->checkNamedRoute($route_name, $route_parameters, $this->account)) {
  160. continue;
  161. }
  162. $links[$plugin_id] = [
  163. 'route_name' => $route_name,
  164. 'route_parameters' => $route_parameters,
  165. 'title' => $plugin->getTitle($request),
  166. 'weight' => $plugin->getWeight(),
  167. 'localized_options' => $plugin->getOptions(),
  168. 'metadata' => $metadata,
  169. ];
  170. }
  171. $this->moduleHandler->alter('contextual_links', $links, $group_name, $route_parameters);
  172. return $links;
  173. }
  174. }