Update to Drupal 8.0.0-beta15. For more information, see: https://www.drupal.org/node/2563023

This commit is contained in:
Pantheon Automation 2015-09-04 13:20:09 -07:00 committed by Greg Anderson
parent 2720a9ec4b
commit f3791f1da3
1898 changed files with 54300 additions and 11481 deletions

View file

@ -191,10 +191,11 @@ class LocalActionManager extends DefaultPluginManager implements LocalActionMana
'url' => Url::fromRoute($route_name, $route_parameters),
'localized_options' => $plugin->getOptions($this->routeMatch),
),
'#access' => $this->accessManager->checkNamedRoute($route_name, $route_parameters, $this->account),
'#access' => $this->accessManager->checkNamedRoute($route_name, $route_parameters, $this->account, TRUE),
'#weight' => $plugin->getWeight(),
);
}
return $links;
}

View file

@ -11,6 +11,12 @@ use Drupal\Core\Routing\RouteMatchInterface;
/**
* Defines an interface for menu local tasks.
*
* Menu local tasks are are typically rendered as navigation tabs above the
* content region, though other presentations are possible. It is convention
* that the titles of these tasks should be short verbs if possible.
*
* @see \Drupal\Core\Menu\LocalTaskManagerInterface
*/
interface LocalTaskInterface {

View file

@ -81,6 +81,13 @@ class LocalTaskManager extends DefaultPluginManager implements LocalTaskManagerI
*/
protected $instances = array();
/**
* The local task render arrays for the current route.
*
* @var array
*/
protected $taskData;
/**
* The route provider to load routes by name.
*
@ -296,40 +303,75 @@ 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);
// Find out whether the user has access to the task.
$access = $this->accessManager->checkNamedRoute($route_name, $route_parameters, $this->account);
if ($access) {
$active = $this->isRouteActive($current_route_name, $route_name, $route_parameters);
$active = $this->isRouteActive($current_route_name, $route_name, $route_parameters);
// The plugin may have been set active in getLocalTasksForRoute() if
// one of its child tabs is the active tab.
$active = $active || $child->getActive();
// @todo It might make sense to use link render elements instead.
// The plugin may have been set active in getLocalTasksForRoute() if
// one of its child tabs is the active tab.
$active = $active || $child->getActive();
// @todo It might make sense to use link render elements instead.
$link = array(
'title' => $this->getTitle($child),
'url' => Url::fromRoute($route_name, $route_parameters),
'localized_options' => $child->getOptions($this->routeMatch),
);
$build[$level][$plugin_id] = array(
'#theme' => 'menu_local_task',
'#link' => $link,
'#active' => $active,
'#weight' => $child->getWeight(),
'#access' => $access,
);
}
$link = [
'title' => $this->getTitle($child),
'url' => Url::fromRoute($route_name, $route_parameters),
'localized_options' => $child->getOptions($this->routeMatch),
];
$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),
];
}
}
return $build;
}
/**
* {@inheritdoc}
*/
public function getLocalTasks($route_name, $level = 0) {
if (!isset($this->taskData[$route_name])) {
// Look for route-based tabs.
$this->taskData[$route_name] = [
'tabs' => [],
];
if (!$this->requestStack->getCurrentRequest()->attributes->has('exception')) {
// Safe to build tasks only when no exceptions raised.
$data = [];
$local_tasks = $this->getTasksBuild($route_name);
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);
}
}
if (isset($this->taskData[$route_name]['tabs'][$level])) {
return [
'tabs' => $this->taskData[$route_name]['tabs'][$level],
'route_name' => $route_name,
];
}
return [
'tabs' => [],
'route_name' => $route_name,
];
}
/**
* Determines whether the route of a certain local task is currently active.
*

View file

@ -53,4 +53,22 @@ interface LocalTaskManagerInterface extends PluginManagerInterface {
*/
public function getTasksBuild($current_route_name);
/**
* Collects the local tasks (tabs) for the current route.
*
* @param string $route_name
* The route for which to make renderable local tasks.
* @param int $level
* The level of tasks you ask for. Primary tasks are 0, secondary are 1.
*
* @return array
* An array containing
* - tabs: Local tasks render array for the requested level.
* - route_name: The route name for the current page used to collect the
* local tasks.
*
* @see hook_menu_local_tasks_alter()
*/
public function getLocalTasks($route_name, $level = 0);
}

View file

@ -96,7 +96,7 @@ class MenuLinkDefault extends MenuLinkBase implements ContainerFactoryPluginInte
if ($persist) {
// Always save the menu name as an override to avoid defaulting to tools.
$overrides['menu_name'] = $this->pluginDefinition['menu_name'];
$this->staticOverride->saveOverride($this->getPluginId(), $overrides);
$this->staticOverride->saveOverride($this->getPluginId(), $this->pluginDefinition);
}
return $this->pluginDefinition;
}

View file

@ -0,0 +1,125 @@
<?php
/**
* @file
* Contains \Drupal\Core\Plugin\Block\LocalActionsBlock.
*/
namespace Drupal\Core\Menu\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Menu\LocalActionManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Routing\RouteMatchInterface;
/**
* Provides a block to display the local actions.
*
* @Block(
* id = "local_actions_block",
* admin_label = @Translation("Primary admin actions")
* )
*/
class LocalActionsBlock extends BlockBase implements ContainerFactoryPluginInterface {
/**
* The local action manager.
*
* @var \Drupal\Core\Menu\LocalActionManagerInterface
*/
protected $localActionManager;
/**
* The route match.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $routeMatch;
/**
* Creates a LocalActionsBlock instance.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Menu\LocalActionManagerInterface $local_action_manager
* A local action manager.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, LocalActionManagerInterface $local_action_manager, RouteMatchInterface $route_match) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->localActionManager = $local_action_manager;
$this->routeMatch = $route_match;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('plugin.manager.menu.local_action'),
$container->get('current_route_match')
);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return ['label_display' => FALSE];
}
/**
* {@inheritdoc}
*/
public function build() {
$route_name = $this->routeMatch->getRouteName();
$local_actions = $this->localActionManager->getActionsForRoute($route_name);
if (empty($local_actions)) {
return [];
}
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'];
}
}

View file

@ -0,0 +1,187 @@
<?php
/**
* @file
* Contains \Drupal\Core\Menu\Plugin\Block\LocalTasksBlock.
*/
namespace Drupal\Core\Menu\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Menu\LocalTaskManagerInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Routing\RouteMatchInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a "Tabs" block to display the local tasks.
*
* @Block(
* id = "local_tasks_block",
* admin_label = @Translation("Tabs"),
* )
*/
class LocalTasksBlock extends BlockBase implements ContainerFactoryPluginInterface {
/**
* The local task manager.
*
* @var \Drupal\Core\Menu\LocalTaskManagerInterface
*/
protected $localTaskManager;
/**
* The route match.
*
* @var \Drupal\Core\Routing\RouteMatchInterface
*/
protected $routeMatch;
/**
* Creates a LocalTasksBlock instance.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Menu\LocalTaskManagerInterface $local_task_manager
* The local task manager.
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The route match.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, LocalTaskManagerInterface $local_task_manager, RouteMatchInterface $route_match) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->localTaskManager = $local_task_manager;
$this->routeMatch = $route_match;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('plugin.manager.menu.local_task'),
$container->get('current_route_match')
);
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'label_display' => FALSE,
'primary' => TRUE,
'secondary' => TRUE,
];
}
/**
* {@inheritdoc}
*/
public function build() {
$config = $this->configuration;
$tabs = [
'#theme' => 'menu_local_tasks',
];
// Add only selected levels for the printed output.
if ($config['primary']) {
$links = $this->localTaskManager->getLocalTasks($this->routeMatch->getRouteName(), 0);
// Do not display single tabs.
$tabs += [
'#primary' => count(Element::getVisibleChildren($links['tabs'])) > 1 ? $links['tabs'] : [],
];
}
if ($config['secondary']) {
$links = $this->localTaskManager->getLocalTasks($this->routeMatch->getRouteName(), 1);
// Do not display single tabs.
$tabs += [
'#secondary' => count(Element::getVisibleChildren($links['tabs'])) > 1 ? $links['tabs'] : [],
];
}
if (empty($tabs['#primary']) && empty($tabs['#secondary'])) {
return [];
}
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'];
}
/**
* {@inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state) {
$config = $this->configuration;
$defaults = $this->defaultConfiguration();
$form['levels'] = [
'#type' => 'details',
'#title' => $this->t('Shown tabs'),
'#description' => $this->t('Select tabs being shown in the block'),
// Open if not set to defaults.
'#open' => $defaults['primary'] !== $config['primary'] || $defaults['secondary'] !== $config['secondary'],
];
$form['levels']['primary'] = [
'#type' => 'checkbox',
'#title' => $this->t('Show primary tabs'),
'#default_value' => $config['primary'],
];
$form['levels']['secondary'] = [
'#type' => 'checkbox',
'#title' => $this->t('Show secondary tabs'),
'#default_value' => $config['secondary'],
];
return $form;
}
/**
* {@inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state) {
$levels = $form_state->getValue('levels');
$this->configuration['primary'] = $levels['primary'];
$this->configuration['secondary'] = $levels['secondary'];
}
}

View file

@ -122,6 +122,22 @@
* 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
@ -377,48 +393,35 @@ function hook_menu_links_discovered_alter(&$links) {
}
/**
* Alter tabs and actions displayed on the page before they are rendered.
* Alter local tasks displayed on the page before they are rendered.
*
* This hook is invoked by menu_local_tasks(). The system-determined tabs and
* actions are passed in by reference. Additional tabs or actions may be added.
* actions are passed in by reference. Additional tabs may be added.
*
* Each tab or action is an associative array containing:
* The local tasks are under the 'tabs' element and keyed by plugin ID.
*
* Each local task is an associative array containing:
* - #theme: The theme function to use to render.
* - #link: An associative array containing:
* - title: The localized title of the link.
* - href: The system path to link to.
* - url: a Url object.
* - localized_options: An array of options to pass to _l().
* - #weight: The link's weight compared to other links.
* - #active: Whether the link should be marked as 'active'.
*
* @param array $data
* An associative array containing:
* - actions: A list of of actions keyed by their href, each one being an
* associative array as described above.
* - tabs: A list of (up to 2) tab levels that contain a list of of tabs keyed
* by their href, each one being an associative array as described above.
* An associative array containing list of (up to 2) tab levels that contain a
* list of of tabs keyed by their href, each one being an associative array
* as described above.
* @param string $route_name
* The route name of the page.
*
* @ingroup menu
*/
function hook_menu_local_tasks(&$data, $route_name) {
// Add an action linking to node/add to all pages.
$data['actions']['node/add'] = array(
'#theme' => 'menu_local_action',
'#link' => array(
'title' => t('Add content'),
'url' => Url::fromRoute('node.add_page'),
'localized_options' => array(
'attributes' => array(
'title' => t('Add content'),
),
),
),
);
function hook_menu_local_tasks_alter(&$data, $route_name) {
// Add a tab linking to node/add to all pages.
$data['tabs'][0]['node/add'] = array(
$data['tabs'][0]['node.add_page'] = array(
'#theme' => 'menu_local_task',
'#link' => array(
'title' => t('Example tab'),
@ -432,25 +435,6 @@ function hook_menu_local_tasks(&$data, $route_name) {
);
}
/**
* Alter tabs and actions displayed on the page before they are rendered.
*
* This hook is invoked by menu_local_tasks(). The system-determined tabs and
* actions are passed in by reference. Existing tabs or actions may be altered.
*
* @param array $data
* An associative array containing tabs and actions. See
* hook_menu_local_tasks() for details.
* @param string $route_name
* The route name of the page.
*
* @see hook_menu_local_tasks()
*
* @ingroup menu
*/
function hook_menu_local_tasks_alter(&$data, $route_name) {
}
/**
* Alter local actions plugins.
*
@ -546,12 +530,8 @@ function hook_contextual_links_plugins_alter(array &$contextual_links) {
/**
* Perform alterations to the breadcrumb built by the BreadcrumbManager.
*
* @param array $breadcrumb
* An array of breadcrumb link a tags, returned by the breadcrumb manager
* build method, for example
* @code
* array('<a href="/">Home</a>');
* @endcode
* @param \Drupal\Core\Breadcrumb\Breadcrumb $breadcrumb
* A breadcrumb object returned by BreadcrumbBuilderInterface::build().
* @param \Drupal\Core\Routing\RouteMatchInterface $route_match
* The current route match.
* @param array $context
@ -562,9 +542,9 @@ function hook_contextual_links_plugins_alter(array &$contextual_links) {
*
* @ingroup menu
*/
function hook_system_breadcrumb_alter(array &$breadcrumb, \Drupal\Core\Routing\RouteMatchInterface $route_match, array $context) {
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[] = Drupal::l(t('Text'), 'example_route_name');
$breadcrumb->addLink(Drupal::l(t('Text'), 'example_route_name'));
}
/**