Update to drupal 8.0.0-rc1. For more information, see https://www.drupal.org/node/2582663

This commit is contained in:
Greg Anderson 2015-10-08 11:40:12 -07:00
parent eb34d130a8
commit f32e58e4b1
8476 changed files with 211648 additions and 170042 deletions

View file

@ -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'];
}

View file

@ -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();

View file

@ -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'];
}

View file

@ -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'];
}
}

View file

@ -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'],
];
}

View file

@ -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.

View file

@ -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);

View file

@ -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' => '',

View file

@ -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);

View file

@ -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,

View file

@ -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'];
}
}

View file

@ -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;
}
/**

View file

@ -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']]);
}
}