Update to drupal 8.0.0-rc1. For more information, see https://www.drupal.org/node/2582663
This commit is contained in:
parent
eb34d130a8
commit
f32e58e4b1
8476 changed files with 211648 additions and 170042 deletions
|
@ -19,7 +19,7 @@ class ContextualLinkDefault extends PluginBase implements ContextualLinkInterfac
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle(Request $request = NULL) {
|
||||
// The title from YAML file discovery may be a TranslationWrapper object.
|
||||
// The title from YAML file discovery may be a TranslatableMarkup object.
|
||||
return (string) $this->pluginDefinition['title'];
|
||||
}
|
||||
|
||||
|
|
|
@ -206,15 +206,14 @@ class DefaultMenuLinkTreeManipulators {
|
|||
$access_result = AccessResult::allowed();
|
||||
}
|
||||
else {
|
||||
// Use the definition here since that's a lot faster than creating a Url
|
||||
// object that we don't need.
|
||||
$definition = $instance->getPluginDefinition();
|
||||
// 'url' should only be populated for external links.
|
||||
if (!empty($definition['url']) && empty($definition['route_name'])) {
|
||||
$url = $instance->getUrlObject();
|
||||
|
||||
// When no route name is specified, this must be an external link.
|
||||
if (!$url->isRouted()) {
|
||||
$access_result = AccessResult::allowed();
|
||||
}
|
||||
else {
|
||||
$access_result = $this->accessManager->checkNamedRoute($definition['route_name'], $definition['route_parameters'], $this->account, TRUE);
|
||||
$access_result = $this->accessManager->checkNamedRoute($url->getRouteName(), $url->getRouteParameters(), $this->account, TRUE);
|
||||
}
|
||||
}
|
||||
return $access_result->cachePerPermissions();
|
||||
|
|
|
@ -71,7 +71,7 @@ class LocalActionDefault extends PluginBase implements LocalActionInterface, Con
|
|||
*/
|
||||
public function getTitle(Request $request = NULL) {
|
||||
// Subclasses may pull in the request or specific attributes as parameters.
|
||||
// The title from YAML file discovery may be a TranslationWrapper object.
|
||||
// The title from YAML file discovery may be a TranslatableMarkup object.
|
||||
return (string) $this->pluginDefinition['title'];
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
namespace Drupal\Core\Menu;
|
||||
|
||||
use Drupal\Component\Plugin\PluginBase;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheableDependencyInterface;
|
||||
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
|
||||
use Drupal\Core\Routing\RouteMatchInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
@ -15,7 +17,7 @@ use Symfony\Component\HttpFoundation\Request;
|
|||
/**
|
||||
* Default object used for LocalTaskPlugins.
|
||||
*/
|
||||
class LocalTaskDefault extends PluginBase implements LocalTaskInterface {
|
||||
class LocalTaskDefault extends PluginBase implements LocalTaskInterface, CacheableDependencyInterface {
|
||||
|
||||
use DependencySerializationTrait;
|
||||
|
||||
|
@ -78,7 +80,7 @@ class LocalTaskDefault extends PluginBase implements LocalTaskInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTitle(Request $request = NULL) {
|
||||
// The title from YAML file discovery may be a TranslationWrapper object.
|
||||
// The title from YAML file discovery may be a TranslatableMarkup object.
|
||||
return (string) $this->pluginDefinition['title'];
|
||||
}
|
||||
|
||||
|
@ -143,4 +145,34 @@ class LocalTaskDefault extends PluginBase implements LocalTaskInterface {
|
|||
return $this->routeProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheTags() {
|
||||
if (!isset($this->pluginDefinition['cache_tags'])) {
|
||||
return [];
|
||||
}
|
||||
return $this->pluginDefinition['cache_tags'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
if (!isset($this->pluginDefinition['cache_contexts'])) {
|
||||
return [];
|
||||
}
|
||||
return $this->pluginDefinition['cache_contexts'];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
if (!isset($this->pluginDefinition['cache_max_age'])) {
|
||||
return Cache::PERMANENT;
|
||||
}
|
||||
return $this->pluginDefinition['cache_max_age'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,7 +10,9 @@ namespace Drupal\Core\Menu;
|
|||
use Drupal\Component\Plugin\Exception\PluginException;
|
||||
use Drupal\Core\Access\AccessManagerInterface;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Cache\CacheBackendInterface;
|
||||
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
|
||||
use Drupal\Core\Controller\ControllerResolverInterface;
|
||||
use Drupal\Core\Extension\ModuleHandlerInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
|
@ -265,15 +267,16 @@ class LocalTaskManager extends DefaultPluginManager implements LocalTaskManagerI
|
|||
foreach ($children[$parent] as $plugin_id => $task_info) {
|
||||
$plugin = $this->createInstance($plugin_id);
|
||||
$this->instances[$route_name][$level][$plugin_id] = $plugin;
|
||||
// Normally, _l() compares the href of every link with the current
|
||||
// path and sets the active class accordingly. But the parents of
|
||||
// the current local task may be on a different route in which
|
||||
// case we have to set the class manually by flagging it active.
|
||||
// Normally, the link generator compares the href of every link with
|
||||
// the current path and sets the active class accordingly. But the
|
||||
// parents of the current local task may be on a different route in
|
||||
// which case we have to set the class manually by flagging it
|
||||
// active.
|
||||
if (!empty($parents[$plugin_id]) && $route_name != $task_info['route_name']) {
|
||||
$plugin->setActive();
|
||||
}
|
||||
if (isset($children[$plugin_id])) {
|
||||
// This tab has visible children
|
||||
// This tab has visible children.
|
||||
$next_parent = $plugin_id;
|
||||
}
|
||||
}
|
||||
|
@ -288,7 +291,7 @@ class LocalTaskManager extends DefaultPluginManager implements LocalTaskManagerI
|
|||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getTasksBuild($current_route_name) {
|
||||
public function getTasksBuild($current_route_name, RefinableCacheableDependencyInterface &$cacheability) {
|
||||
$tree = $this->getLocalTasksForRoute($current_route_name);
|
||||
$build = array();
|
||||
|
||||
|
@ -303,14 +306,15 @@ class LocalTaskManager extends DefaultPluginManager implements LocalTaskManagerI
|
|||
// of SQL queries that would otherwise be triggered by the access manager.
|
||||
$routes = $route_names ? $this->routeProvider->getRoutesByNames($route_names) : array();
|
||||
|
||||
// @todo add cacheability data in https://www.drupal.org/node/2511516 so
|
||||
// that we are not re-building inaccessible links on every page request.
|
||||
foreach ($tree as $level => $instances) {
|
||||
/** @var $instances \Drupal\Core\Menu\LocalTaskInterface[] */
|
||||
foreach ($instances as $plugin_id => $child) {
|
||||
$route_name = $child->getRouteName();
|
||||
$route_parameters = $child->getRouteParameters($this->routeMatch);
|
||||
|
||||
// Given that the active flag depends on the route we have to add the
|
||||
// route cache context.
|
||||
$cacheability->addCacheContexts(['route']);
|
||||
$active = $this->isRouteActive($current_route_name, $route_name, $route_parameters);
|
||||
|
||||
// The plugin may have been set active in getLocalTasksForRoute() if
|
||||
|
@ -323,13 +327,15 @@ class LocalTaskManager extends DefaultPluginManager implements LocalTaskManagerI
|
|||
'url' => Url::fromRoute($route_name, $route_parameters),
|
||||
'localized_options' => $child->getOptions($this->routeMatch),
|
||||
];
|
||||
$access = $this->accessManager->checkNamedRoute($route_name, $route_parameters, $this->account, TRUE);
|
||||
$build[$level][$plugin_id] = [
|
||||
'#theme' => 'menu_local_task',
|
||||
'#link' => $link,
|
||||
'#active' => $active,
|
||||
'#weight' => $child->getWeight(),
|
||||
'#access' => $this->accessManager->checkNamedRoute($route_name, $route_parameters, $this->account, TRUE),
|
||||
'#access' => $access,
|
||||
];
|
||||
$cacheability->addCacheableDependency($access)->addCacheableDependency($child);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -341,21 +347,25 @@ class LocalTaskManager extends DefaultPluginManager implements LocalTaskManagerI
|
|||
*/
|
||||
public function getLocalTasks($route_name, $level = 0) {
|
||||
if (!isset($this->taskData[$route_name])) {
|
||||
$cacheability = new CacheableMetadata();
|
||||
$cacheability->addCacheContexts(['route']);
|
||||
// Look for route-based tabs.
|
||||
$this->taskData[$route_name] = [
|
||||
'tabs' => [],
|
||||
'cacheability' => $cacheability,
|
||||
];
|
||||
|
||||
if (!$this->requestStack->getCurrentRequest()->attributes->has('exception')) {
|
||||
// Safe to build tasks only when no exceptions raised.
|
||||
$data = [];
|
||||
$local_tasks = $this->getTasksBuild($route_name);
|
||||
$local_tasks = $this->getTasksBuild($route_name, $cacheability);
|
||||
foreach ($local_tasks as $tab_level => $items) {
|
||||
$data[$tab_level] = empty($data[$tab_level]) ? $items : array_merge($data[$tab_level], $items);
|
||||
}
|
||||
$this->taskData[$route_name]['tabs'] = $data;
|
||||
// Allow modules to alter local tasks.
|
||||
$this->moduleHandler->alter('menu_local_tasks', $this->taskData[$route_name], $route_name);
|
||||
$this->moduleHandler->alter('menu_local_tasks', $this->taskData[$route_name], $route_name, $cacheability);
|
||||
$this->taskData[$route_name]['cacheability'] = $cacheability;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -363,12 +373,14 @@ class LocalTaskManager extends DefaultPluginManager implements LocalTaskManagerI
|
|||
return [
|
||||
'tabs' => $this->taskData[$route_name]['tabs'][$level],
|
||||
'route_name' => $route_name,
|
||||
'cacheability' => $this->taskData[$route_name]['cacheability'],
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'tabs' => [],
|
||||
'route_name' => $route_name,
|
||||
'cacheability' => $this->taskData[$route_name]['cacheability'],
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
namespace Drupal\Core\Menu;
|
||||
|
||||
use Drupal\Component\Plugin\PluginManagerInterface;
|
||||
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
|
||||
|
||||
/**
|
||||
* Manages discovery and instantiation of menu local task plugins.
|
||||
|
@ -47,11 +48,13 @@ interface LocalTaskManagerInterface extends PluginManagerInterface {
|
|||
*
|
||||
* @param string $current_route_name
|
||||
* The route for which to make renderable local tasks.
|
||||
* @param \Drupal\Core\Cache\RefinableCacheableDependencyInterface $cacheability
|
||||
* The cacheability metadata for the local tasks.
|
||||
*
|
||||
* @return array
|
||||
* A render array as expected by menu-local-tasks.html.twig.
|
||||
*/
|
||||
public function getTasksBuild($current_route_name);
|
||||
public function getTasksBuild($current_route_name, RefinableCacheableDependencyInterface &$cacheability);
|
||||
|
||||
/**
|
||||
* Collects the local tasks (tabs) for the current route.
|
||||
|
|
|
@ -130,7 +130,7 @@ abstract class MenuLinkBase extends PluginBase implements MenuLinkInterface {
|
|||
$options['attributes']['title'] = $description;
|
||||
}
|
||||
if (empty($this->pluginDefinition['url'])) {
|
||||
return new Url($this->pluginDefinition['route_name'], $this->pluginDefinition['route_parameters'], $options);
|
||||
return new Url($this->getRouteName(), $this->getRouteParameters(), $options);
|
||||
}
|
||||
else {
|
||||
return Url::fromUri($this->pluginDefinition['url'], $options);
|
||||
|
|
|
@ -41,10 +41,10 @@ class MenuLinkManager implements MenuLinkManagerInterface {
|
|||
// The external URL if this link has one (required if route_name is empty).
|
||||
'url' => '',
|
||||
// The static title for the menu link. If this came from a YAML definition
|
||||
// or other safe source this may be a TranslationWrapper object.
|
||||
// or other safe source this may be a TranslatableMarkup object.
|
||||
'title' => '',
|
||||
// The description. If this came from a YAML definition or other safe source
|
||||
// this may be be a TranslationWrapper object.
|
||||
// this may be be a TranslatableMarkup object.
|
||||
'description' => '',
|
||||
// The plugin ID of the parent link (or NULL for a top-level link).
|
||||
'parent' => '',
|
||||
|
|
|
@ -243,26 +243,28 @@ class MenuLinkTree implements MenuLinkTreeInterface {
|
|||
if ($data->access instanceof AccessResultInterface && !$data->access->isAllowed()) {
|
||||
continue;
|
||||
}
|
||||
$element = [];
|
||||
|
||||
$class = ['menu-item'];
|
||||
// Set a class for the <li>-tag. Only set 'expanded' class if the link
|
||||
// also has visible children within the current tree.
|
||||
// Set a variable for the <li> tag. Only set 'expanded' to true if the
|
||||
// link also has visible children within the current tree.
|
||||
$element['is_expanded'] = FALSE;
|
||||
$element['is_collapsed'] = FALSE;
|
||||
if ($data->hasChildren && !empty($data->subtree)) {
|
||||
$class[] = 'menu-item--expanded';
|
||||
$element['is_expanded'] = TRUE;
|
||||
}
|
||||
elseif ($data->hasChildren) {
|
||||
$class[] = 'menu-item--collapsed';
|
||||
$element['is_collapsed'] = TRUE;
|
||||
}
|
||||
// Set a class if the link is in the active trail.
|
||||
// Set a helper variable to indicate whether the link is in the active
|
||||
// trail.
|
||||
$element['in_active_trail'] = FALSE;
|
||||
if ($data->inActiveTrail) {
|
||||
$class[] = 'menu-item--active-trail';
|
||||
$element['in_active_trail'] = TRUE;
|
||||
}
|
||||
|
||||
// Note: links are rendered in the menu.html.twig template; and they
|
||||
// automatically bubble their associated cacheability metadata.
|
||||
$element = array();
|
||||
$element['attributes'] = new Attribute();
|
||||
$element['attributes']['class'] = $class;
|
||||
$element['title'] = $link->getTitle();
|
||||
$element['url'] = $link->getUrlObject();
|
||||
$element['url']->setOption('set_active_class', TRUE);
|
||||
|
|
|
@ -157,6 +157,9 @@ class MenuTreeStorage implements MenuTreeStorageInterface {
|
|||
foreach ($definitions as $id => $link) {
|
||||
// Flag this link as discovered, i.e. saved via rebuild().
|
||||
$link['discovered'] = 1;
|
||||
// Note: The parent we set here might be just stored in the {menu_tree}
|
||||
// table, so it will not end up in $top_links. Therefore the later loop
|
||||
// on the orphan links, will handle those cases.
|
||||
if (!empty($link['parent'])) {
|
||||
$children[$link['parent']][$id] = $id;
|
||||
}
|
||||
|
@ -174,8 +177,18 @@ class MenuTreeStorage implements MenuTreeStorageInterface {
|
|||
// Handle any children we didn't find starting from top-level links.
|
||||
foreach ($children as $orphan_links) {
|
||||
foreach ($orphan_links as $id) {
|
||||
// Force it to the top level.
|
||||
$links[$id]['parent'] = '';
|
||||
// Check for a parent that is not loaded above since only internal links
|
||||
// are loaded above.
|
||||
$parent = $this->loadFull($links[$id]['parent']);
|
||||
// If there is a parent add it to the links to be used in
|
||||
// ::saveRecursive().
|
||||
if ($parent) {
|
||||
$links[$links[$id]['parent']] = $parent;
|
||||
}
|
||||
else {
|
||||
// Force it to the top level.
|
||||
$links[$id]['parent'] = '';
|
||||
}
|
||||
$this->saveRecursive($id, $children, $links);
|
||||
}
|
||||
}
|
||||
|
@ -1249,14 +1262,14 @@ class MenuTreeStorage implements MenuTreeStorageInterface {
|
|||
'default' => '',
|
||||
),
|
||||
'title' => array(
|
||||
'description' => 'The serialized title for the link. May be a TranslationWrapper.',
|
||||
'description' => 'The serialized title for the link. May be a TranslatableMarkup.',
|
||||
'type' => 'blob',
|
||||
'size' => 'big',
|
||||
'not null' => FALSE,
|
||||
'serialize' => TRUE,
|
||||
),
|
||||
'description' => array(
|
||||
'description' => 'The serialized description of this link - used for admin pages and title attribute. May be a TranslationWrapper.',
|
||||
'description' => 'The serialized description of this link - used for admin pages and title attribute. May be a TranslatableMarkup.',
|
||||
'type' => 'blob',
|
||||
'size' => 'big',
|
||||
'not null' => FALSE,
|
||||
|
|
|
@ -91,35 +91,11 @@ class LocalActionsBlock extends BlockBase implements ContainerFactoryPluginInter
|
|||
return $local_actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::buildConfigurationForm($form, $form_state);
|
||||
|
||||
// The "Primary admin actions" block is never cacheable because hooks creating local
|
||||
// actions don't provide cacheability metadata.
|
||||
// @todo Remove after https://www.drupal.org/node/2511516 has landed.
|
||||
$form['cache']['#disabled'] = TRUE;
|
||||
$form['cache']['#description'] = $this->t('This block is never cacheable.');
|
||||
$form['cache']['max_age']['#value'] = 0;
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
// @todo Remove after https://www.drupal.org/node/2511516 has landed.
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
return ['route.name'];
|
||||
return ['route'];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
namespace Drupal\Core\Menu\Plugin\Block;
|
||||
|
||||
use Drupal\Core\Block\BlockBase;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Menu\LocalTaskManagerInterface;
|
||||
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
|
||||
|
@ -88,7 +89,7 @@ class LocalTasksBlock extends BlockBase implements ContainerFactoryPluginInterfa
|
|||
*/
|
||||
public function build() {
|
||||
$config = $this->configuration;
|
||||
|
||||
$cacheability = new CacheableMetadata();
|
||||
$tabs = [
|
||||
'#theme' => 'menu_local_tasks',
|
||||
];
|
||||
|
@ -96,6 +97,7 @@ class LocalTasksBlock extends BlockBase implements ContainerFactoryPluginInterfa
|
|||
// Add only selected levels for the printed output.
|
||||
if ($config['primary']) {
|
||||
$links = $this->localTaskManager->getLocalTasks($this->routeMatch->getRouteName(), 0);
|
||||
$cacheability = $cacheability->merge($links['cacheability']);
|
||||
// Do not display single tabs.
|
||||
$tabs += [
|
||||
'#primary' => count(Element::getVisibleChildren($links['tabs'])) > 1 ? $links['tabs'] : [],
|
||||
|
@ -103,48 +105,20 @@ class LocalTasksBlock extends BlockBase implements ContainerFactoryPluginInterfa
|
|||
}
|
||||
if ($config['secondary']) {
|
||||
$links = $this->localTaskManager->getLocalTasks($this->routeMatch->getRouteName(), 1);
|
||||
$cacheability = $cacheability->merge($links['cacheability']);
|
||||
// Do not display single tabs.
|
||||
$tabs += [
|
||||
'#secondary' => count(Element::getVisibleChildren($links['tabs'])) > 1 ? $links['tabs'] : [],
|
||||
];
|
||||
}
|
||||
|
||||
$build = [];
|
||||
$cacheability->applyTo($build);
|
||||
if (empty($tabs['#primary']) && empty($tabs['#secondary'])) {
|
||||
return [];
|
||||
return $build;
|
||||
}
|
||||
|
||||
return $tabs;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
|
||||
$form = parent::buildConfigurationForm($form, $form_state);
|
||||
|
||||
// The "Page actions" block is never cacheable because of hooks creating
|
||||
// local tasks doesn't provide cacheability metadata.
|
||||
// @todo Remove after https://www.drupal.org/node/2511516 has landed.
|
||||
$form['cache']['#disabled'] = TRUE;
|
||||
$form['cache']['#description'] = $this->t('This block is never cacheable.');
|
||||
$form['cache']['max_age']['#value'] = 0;
|
||||
|
||||
return $form;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheMaxAge() {
|
||||
// @todo Remove after https://www.drupal.org/node/2511516 has landed.
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getCacheContexts() {
|
||||
return ['route.name'];
|
||||
return $build + $tabs;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,28 +2,18 @@
|
|||
|
||||
/**
|
||||
* @file
|
||||
* Hooks and documentation related to the menu system, routing, and links.
|
||||
* Hooks and documentation related to the menu system and links.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup menu Menu and routing system
|
||||
* @defgroup menu Menu system
|
||||
* @{
|
||||
* Define the navigation menus, and route page requests to code based on URLs.
|
||||
* Define the navigation menus, local actions and tasks, and contextual links.
|
||||
*
|
||||
* @section sec_overview Overview and terminology
|
||||
* The Drupal routing system defines how Drupal responds to URL requests that
|
||||
* the web server passes on to Drupal. The routing system is based on the
|
||||
* @link http://symfony.com Symfony framework. @endlink The central idea is
|
||||
* that Drupal subsystems and modules can register routes (basically, URL
|
||||
* paths and context); they can also register to respond dynamically to
|
||||
* routes, for more flexibility. When Drupal receives a URL request, it will
|
||||
* attempt to match the request to a registered route, and query dynamic
|
||||
* responders. If a match is made, Drupal will then instantiate the required
|
||||
* classes, gather the data, format it, and send it back to the web browser.
|
||||
* Otherwise, Drupal will return a 404 or 403 response.
|
||||
*
|
||||
* The menu system uses routes; it is used for navigation menus, local tasks,
|
||||
* local actions, and contextual links:
|
||||
* The menu system uses routes; see the
|
||||
* @link routing Routing API topic @endlink for more information. It is used
|
||||
* for navigation menus, local tasks, local actions, and contextual links:
|
||||
* - Navigation menus are hierarchies of menu links; links point to routes or
|
||||
* URLs.
|
||||
* - Menu links and their hierarchies can be defined by Drupal subsystems
|
||||
|
@ -38,116 +28,10 @@
|
|||
* Contextual Links module handles the gathering and rendering of contextual
|
||||
* links.
|
||||
*
|
||||
* The following sections of this topic provide an overview of the routing and
|
||||
* menu APIs. For more detailed information, see
|
||||
* https://www.drupal.org/developing/api/8/routing and
|
||||
* The following sections of this topic provide an overview of the menu API.
|
||||
* For more detailed information, see
|
||||
* https://www.drupal.org/developing/api/8/menu
|
||||
*
|
||||
* @section sec_register Registering simple routes
|
||||
* To register a route, add lines similar to this to a module_name.routing.yml
|
||||
* file in your top-level module directory:
|
||||
* @code
|
||||
* dblog.overview:
|
||||
* path: '/admin/reports/dblog'
|
||||
* defaults:
|
||||
* _controller: '\Drupal\dblog\Controller\DbLogController::overview'
|
||||
* _title: 'Recent log messages'
|
||||
* requirements:
|
||||
* _permission: 'access site reports'
|
||||
* @endcode
|
||||
* Some notes:
|
||||
* - The first line is the machine name of the route. Typically, it is prefixed
|
||||
* by the machine name of the module that defines the route, or the name of
|
||||
* a subsystem.
|
||||
* - The 'path' line gives the URL path of the route (relative to the site's
|
||||
* base URL).
|
||||
* - The 'defaults' section tells how to build the main content of the route,
|
||||
* and can also give other information, such as the page title and additional
|
||||
* arguments for the route controller method. There are several possibilities
|
||||
* for how to build the main content, including:
|
||||
* - _controller: A callable, usually a method on a page controller class
|
||||
* (see @ref sec_controller below for details).
|
||||
* - _form: A form controller class. See the
|
||||
* @link form_api Form API topic @endlink for more information about
|
||||
* form controllers.
|
||||
* - _entity_form: A form for editing an entity. See the
|
||||
* @link entity_api Entity API topic @endlink for more information.
|
||||
* - The 'requirements' section is used in Drupal to give access permission
|
||||
* instructions (it has other uses in the Symfony framework). Most
|
||||
* routes have a simple permission-based access scheme, as shown in this
|
||||
* example. See the @link user_api Permission system topic @endlink for
|
||||
* more information about permissions.
|
||||
*
|
||||
* See https://www.drupal.org/node/2092643 for more details about *.routing.yml
|
||||
* files, and https://www.drupal.org/node/2122201 for information on how to
|
||||
* set up dynamic routes. The @link events Events topic @endlink is also
|
||||
* relevant to dynamic routes.
|
||||
*
|
||||
* @section sec_placeholders Defining routes with placeholders
|
||||
* Some routes have placeholders in them, and these can also be defined in a
|
||||
* module_name.routing.yml file, as in this example from the Block module:
|
||||
* @code
|
||||
* entity.block.edit_form:
|
||||
* path: '/admin/structure/block/manage/{block}'
|
||||
* defaults:
|
||||
* _entity_form: 'block.default'
|
||||
* _title: 'Configure block'
|
||||
* requirements:
|
||||
* _entity_access: 'block.update'
|
||||
* @endcode
|
||||
* In the path, '{block}' is a placeholder - it will be replaced by the
|
||||
* ID of the block that is being configured by the entity system. See the
|
||||
* @link entity_api Entity API topic @endlink for more information.
|
||||
*
|
||||
* @section sec_controller Route controllers for simple routes
|
||||
* For simple routes, after you have defined the route in a *.routing.yml file
|
||||
* (see @ref sec_register above), the next step is to define a page controller
|
||||
* class and method. Page controller classes do not necessarily need to
|
||||
* implement any particular interface or extend any particular base class. The
|
||||
* only requirement is that the method specified in your *.routing.yml file
|
||||
* returns:
|
||||
* - A render array (see the
|
||||
* @link theme_render Theme and render topic @endlink for more information).
|
||||
* This render array is then rendered in the requested format (HTML, dialog,
|
||||
* modal, AJAX are supported by default). In the case of HTML, it will be
|
||||
* surrounded by blocks by default: the Block module is enabled by default,
|
||||
* and hence its Page Display Variant that surrounds the main content with
|
||||
* blocks is also used by default.
|
||||
* - A \Symfony\Component\HttpFoundation\Response object.
|
||||
* As a note, if your module registers multiple simple routes, it is usual
|
||||
* (and usually easiest) to put all of their methods on one controller class.
|
||||
*
|
||||
* If the route has placeholders (see @ref sec_placeholders above) the
|
||||
* placeholders will be passed to the method (using reflection) by name.
|
||||
* For example, the placeholder '{myvar}' in a route will become the $myvar
|
||||
* parameter to the method.
|
||||
*
|
||||
* Additionally, if a parameter is typed to one of the following special classes
|
||||
* the system will pass those values as well.
|
||||
*
|
||||
* - \Symfony\Component\HttpFoundation\Request: The raw Symfony request object.
|
||||
* It is generally only useful if the controller needs access to the query
|
||||
* parameters of the request. By convention, this parameter is usually named
|
||||
* $request.
|
||||
* - \Psr\Http\Message\ServerRequestInterface: The raw request, represented
|
||||
* using the PSR-7 ServerRequest format. This object is derived as necessary
|
||||
* from the Symfony request, so if either will suffice the Symfony request
|
||||
* will be slightly more performant. By convention this parameter is usually
|
||||
* named $request.
|
||||
* - \Drupal\Core\Routing\RouteMatchInterface: The "route match" data from
|
||||
* this request. This object contains various standard data derived from
|
||||
* the request and routing process. Consult the interface for details.
|
||||
*
|
||||
* Most controllers will need to display some information stored in the Drupal
|
||||
* database, which will involve using one or more Drupal services (see the
|
||||
* @link container Services and container topic @endlink). In order to properly
|
||||
* inject services, a controller should implement
|
||||
* \Drupal\Core\DependencyInjection\ContainerInjectionInterface; simple
|
||||
* controllers can do this by extending the
|
||||
* \Drupal\Core\Controller\ControllerBase class. See
|
||||
* \Drupal\dblog\Controller\DbLogController for a straightforward example of
|
||||
* a controller class.
|
||||
*
|
||||
* @section sec_links Defining menu links for the administrative menu
|
||||
* Routes for administrative tasks can be added to the main Drupal
|
||||
* administrative menu hierarchy. To do this, add lines like the following to a
|
||||
|
@ -351,10 +235,10 @@
|
|||
* The value corresponding to each machine name key is an associative array
|
||||
* that may contain the following key-value pairs:
|
||||
* - title: (required) The title of the menu link. If this should be
|
||||
* translated, create a \Drupal\Core\StringTranslation\TranslationWrapper
|
||||
* translated, create a \Drupal\Core\StringTranslation\TranslatableMarkup
|
||||
* object.
|
||||
* - description: The description of the link. If this should be
|
||||
* translated, create a \Drupal\Core\StringTranslation\TranslationWrapper
|
||||
* translated, create a \Drupal\Core\StringTranslation\TranslatableMarkup
|
||||
* object.
|
||||
* - route_name: (optional) The route name to be used to build the path.
|
||||
* Either the route_name or url element must be provided.
|
||||
|
@ -372,21 +256,22 @@
|
|||
* this menu item (as a result of other properties), then the menu link is
|
||||
* always expanded, equivalent to its 'always expanded' checkbox being set
|
||||
* in the UI.
|
||||
* - options: (optional) An array of options to be passed to _l() when
|
||||
* generating a link from this menu item.
|
||||
* - options: (optional) An array of options to be passed to
|
||||
* \Drupal\Core\Utility\LinkGeneratorInterface::generate() when generating
|
||||
* a link from this menu item.
|
||||
*
|
||||
* @ingroup menu
|
||||
*/
|
||||
function hook_menu_links_discovered_alter(&$links) {
|
||||
// Change the weight and title of the user.logout link.
|
||||
$links['user.logout']['weight'] = -10;
|
||||
$links['user.logout']['title'] = new \Drupal\Core\StringTranslation\TranslationWrapper('Logout');
|
||||
$links['user.logout']['title'] = new \Drupal\Core\StringTranslation\TranslatableMarkup('Logout');
|
||||
// Conditionally add an additional link with a title that's not translated.
|
||||
if (\Drupal::moduleHandler()->moduleExists('search')) {
|
||||
$links['menu.api.search'] = array(
|
||||
'title' => \Drupal::config('system.site')->get('name'),
|
||||
'route_name' => 'menu.api.search',
|
||||
'description' => new \Drupal\Core\StringTranslation\TranslationWrapper('View popular search phrases for this site.'),
|
||||
'description' => new \Drupal\Core\StringTranslation\TranslatableMarkup('View popular search phrases for this site.'),
|
||||
'parent' => 'system.admin_reports',
|
||||
);
|
||||
}
|
||||
|
@ -405,7 +290,8 @@ function hook_menu_links_discovered_alter(&$links) {
|
|||
* - #link: An associative array containing:
|
||||
* - title: The localized title of the link.
|
||||
* - url: a Url object.
|
||||
* - localized_options: An array of options to pass to _l().
|
||||
* - localized_options: An array of options to pass to
|
||||
* \Drupal\Core\Utility\LinkGeneratorInterface::generate().
|
||||
* - #weight: The link's weight compared to other links.
|
||||
* - #active: Whether the link should be marked as 'active'.
|
||||
*
|
||||
|
@ -504,7 +390,7 @@ function hook_contextual_links_alter(array &$links, $group, array $route_paramet
|
|||
// Dynamically use the menu name for the title of the menu_edit contextual
|
||||
// link.
|
||||
$menu = \Drupal::entityManager()->getStorage('menu')->load($route_parameters['menu']);
|
||||
$links['menu_edit']['title'] = t('Edit menu: !label', array('!label' => $menu->label()));
|
||||
$links['menu_edit']['title'] = t('Edit menu: @label', array('@label' => $menu->label()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -544,7 +430,7 @@ function hook_contextual_links_plugins_alter(array &$contextual_links) {
|
|||
*/
|
||||
function hook_system_breadcrumb_alter(\Drupal\Core\Breadcrumb\Breadcrumb &$breadcrumb, \Drupal\Core\Routing\RouteMatchInterface $route_match, array $context) {
|
||||
// Add an item to the end of the breadcrumb.
|
||||
$breadcrumb->addLink(Drupal::l(t('Text'), 'example_route_name'));
|
||||
$breadcrumb->addLink(\Drupal\Core\Link::createFromRoute(t('Text'), 'example_route_name'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -553,15 +439,20 @@ function hook_system_breadcrumb_alter(\Drupal\Core\Breadcrumb\Breadcrumb &$bread
|
|||
* @param array $variables
|
||||
* An associative array of variables defining a link. The link may be either a
|
||||
* "route link" using \Drupal\Core\Utility\LinkGenerator::link(), which is
|
||||
* exposed as the 'link_generator' service or a link generated by _l(). If the
|
||||
* link is a "route link", 'route_name' will be set, otherwise 'path' will be
|
||||
* set. The following keys can be altered:
|
||||
* - text: The link text for the anchor tag as a translated string.
|
||||
* exposed as the 'link_generator' service or a link generated by
|
||||
* \Drupal\Core\Utility\LinkGeneratorInterface::generate(). If the link is a
|
||||
* "route link", 'route_name' will be set; otherwise, 'path' will be set.
|
||||
* The following keys can be altered:
|
||||
* - text: The link text for the anchor tag. If the hook implementation
|
||||
* changes this text it needs to preserve the safeness of the original text.
|
||||
* Using t() or \Drupal\Component\Utility\SafeMarkup::format() with
|
||||
* @placeholder is recommended as this will escape the original text if
|
||||
* necessary. If the resulting text is not marked safe it will be escaped.
|
||||
* - url_is_active: Whether or not the link points to the currently active
|
||||
* URL.
|
||||
* - url: The \Drupal\Core\Url object.
|
||||
* - options: An associative array of additional options that will be passed
|
||||
* to either \Drupal\Core\Routing\UrlGenerator::generateFromPath() or
|
||||
* to either \Drupal\Core\Utility\UnroutedUrlAssembler::assemble() or
|
||||
* \Drupal\Core\Routing\UrlGenerator::generateFromRoute() to generate the
|
||||
* href attribute for this link, and also used when generating the link.
|
||||
* Defaults to an empty array. It may contain the following elements:
|
||||
|
@ -577,17 +468,14 @@ function hook_system_breadcrumb_alter(\Drupal\Core\Breadcrumb\Breadcrumb &$bread
|
|||
* must be a string; other elements are more flexible, as they just need
|
||||
* to work as an argument for the constructor of the class
|
||||
* Drupal\Core\Template\Attribute($options['attributes']).
|
||||
* - html: Whether or not HTML should be allowed as the link text. If FALSE,
|
||||
* the text will be run through
|
||||
* \Drupal\Component\Utility\SafeMarkup::checkPlain() before being output.
|
||||
*
|
||||
* @see \Drupal\Core\Routing\UrlGenerator::generateFromPath()
|
||||
* @see \Drupal\Core\Utility\UnroutedUrlAssembler::assemble()
|
||||
* @see \Drupal\Core\Routing\UrlGenerator::generateFromRoute()
|
||||
*/
|
||||
function hook_link_alter(&$variables) {
|
||||
// Add a warning to the end of route links to the admin section.
|
||||
if (isset($variables['route_name']) && strpos($variables['route_name'], 'admin') !== FALSE) {
|
||||
$variables['text'] .= ' (Warning!)';
|
||||
$variables['text'] = t('@text (Warning!)', ['@text' => $variables['text']]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Reference in a new issue