Commit 2c2c1adc authored by catch's avatar catch

Issue #1316692 by tim.plunkett, dawehner: Convert hook_admin_paths() into...

Issue #1316692 by tim.plunkett, dawehner: Convert hook_admin_paths() into declarative properties on routes.
parent 8cbab149
......@@ -276,6 +276,10 @@ services:
class: Symfony\Component\Routing\RequestContext
calls:
- [fromRequest, ['@request']]
router.admin_context:
class: Drupal\Core\Routing\AdminContext
calls:
- [setRequest, ['@?request=']]
router.route_provider:
class: Drupal\Core\Routing\RouteProvider
arguments: ['@database', '@router.builder']
......
......@@ -2127,8 +2127,8 @@ function _drupal_add_js($data = NULL, $options = NULL) {
$current_path = current_path();
$current_path_is_admin = FALSE;
// The function path_is_admin() is not available on update.php pages.
if (!(defined('MAINTENANCE_MODE') && MAINTENANCE_MODE === 'update')) {
$current_path_is_admin = path_is_admin($current_path);
if (!(defined('MAINTENANCE_MODE'))) {
$current_path_is_admin = \Drupal::service('router.admin_context')->isAdminRoute();
}
$path = array(
'basePath' => base_path(),
......
......@@ -8,6 +8,7 @@
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\ParamConverter\ParamNotConvertedException;
use Drupal\Core\Routing\RequestHelper;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
/**
* Check if the current page is the front page.
......@@ -131,54 +132,18 @@ function path_load($conditions) {
* @return
* TRUE if the path is administrative, FALSE otherwise.
*
* @see path_get_admin_paths()
* @see hook_admin_paths()
* @see hook_admin_paths_alter()
* @deprecated Use \Drupal::service('router.admin_context')->isAdminRoute()
* service instead.
*/
function path_is_admin($path) {
$path_map = &drupal_static(__FUNCTION__);
if (!isset($path_map['admin'][$path])) {
$patterns = path_get_admin_paths();
$path_map['admin'][$path] = drupal_match_path($path, $patterns['admin']);
$path_map['non_admin'][$path] = drupal_match_path($path, $patterns['non_admin']);
try {
$parameters = \Drupal::service('router')->match('/' . $path);
$route = $parameters[RouteObjectInterface::ROUTE_OBJECT];
return \Drupal::service('router.admin_context')->isAdminRoute($route);
}
return $path_map['admin'][$path] && !$path_map['non_admin'][$path];
}
/**
* Gets a list of administrative and non-administrative paths.
*
* @return array
* An associative array containing the following keys:
* 'admin': An array of administrative paths and regular expressions
* in a format suitable for drupal_match_path().
* 'non_admin': An array of non-administrative paths and regular expressions.
*
* @see hook_admin_paths()
* @see hook_admin_paths_alter()
*/
function path_get_admin_paths() {
$patterns = &drupal_static(__FUNCTION__);
if (!isset($patterns)) {
$paths = \Drupal::moduleHandler()->invokeAll('admin_paths');
\Drupal::moduleHandler()->alter('admin_paths', $paths);
// Combine all admin paths into one array, and likewise for non-admin paths,
// for easier handling.
$patterns = array();
$patterns['admin'] = array();
$patterns['non_admin'] = array();
foreach ($paths as $path => $enabled) {
if ($enabled) {
$patterns['admin'][] = $path;
}
else {
$patterns['non_admin'][] = $path;
}
}
$patterns['admin'] = implode("\n", $patterns['admin']);
$patterns['non_admin'] = implode("\n", $patterns['non_admin']);
catch (ParamNotConvertedException $e) {
return FALSE;
}
return $patterns;
}
/**
......
<?php
/**
* @file
* Contains \Drupal\Core\Routing\AdminContext.
*/
namespace Drupal\Core\Routing;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
/**
* Provides a helper class to determine whether the route is an admin one.
*/
class AdminContext {
/**
* The route object.
*
* @var \Symfony\Component\Routing\Route
*/
protected $route;
/**
* Sets the request object to use.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request object.
*/
public function setRequest(Request $request) {
$this->route = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT);
}
/**
* Determines whether the active route is an admin one.
*
* @param \Symfony\Component\Routing\Route $route
* (optional) The route to determine whether it is an admin one. Per default
* this falls back to the route object on the active request.
*
* @return bool
* Returns TRUE if the route is an admin one, otherwise FALSE.
*/
public function isAdminRoute(Route $route = NULL) {
if (!$route) {
$route = $this->route;
if (!$route) {
return FALSE;
}
}
return (bool) $route->getOption('_admin_route');
}
}
......@@ -25,8 +25,7 @@ abstract class RouteSubscriberBase implements EventSubscriberInterface {
* The provider these routes belong to. For dynamically added routes, the
* provider name will be 'dynamic_routes'.
*/
protected function alterRoutes(RouteCollection $collection, $provider) {
}
abstract protected function alterRoutes(RouteCollection $collection, $provider);
/**
* {@inheritdoc}
......
......@@ -496,18 +496,6 @@ function block_menu_delete($menu) {
}
}
/**
* Implements hook_admin_paths().
*/
function block_admin_paths() {
$paths = array(
// Exclude the block demonstration page from admin treatment.
// This allows us to present this page in its true form, full page.
'admin/structure/block/demo/*' => FALSE,
);
return $paths;
}
/**
* Implements hook_language_delete().
*
......
......@@ -5,6 +5,8 @@ block.admin_demo:
requirements:
_access_theme: 'TRUE'
_permission: 'administer blocks'
options:
_admin_route: FALSE
block.admin_block_delete:
path: '/admin/structure/block/manage/{block}/delete'
......
......@@ -170,17 +170,3 @@ function custom_block_add_body_field($block_type_id, $label = 'Block body') {
return $instance;
}
/**
* Implements hook_admin_paths().
*/
function custom_block_admin_paths() {
$paths = array(
'block/add' => TRUE,
'block/add/*' => TRUE,
'block/*' => TRUE,
'block/*/delete' => TRUE,
'admin/structure/block/custom-blocks/*' => TRUE,
);
return $paths;
}
......@@ -10,6 +10,8 @@ custom_block.add_page:
defaults:
_content: 'Drupal\custom_block\Controller\CustomBlockController::add'
_title: 'Add custom block'
options:
_admin_route: TRUE
requirements:
_permission: 'administer blocks'
......@@ -18,6 +20,8 @@ custom_block.add_form:
defaults:
_content: 'Drupal\custom_block\Controller\CustomBlockController::addForm'
_title_callback: 'Drupal\custom_block\Controller\CustomBlockController::getAddFormTitle'
options:
_admin_route: TRUE
requirements:
_permission: 'administer blocks'
......@@ -28,11 +32,15 @@ custom_block.type_delete:
_title: 'Delete'
requirements:
_entity_access: 'custom_block_type.delete'
options:
_admin_route: TRUE
custom_block.edit:
path: '/block/{custom_block}'
defaults:
_entity_form: 'custom_block.edit'
options:
_admin_route: TRUE
requirements:
_entity_access: 'custom_block.update'
......@@ -40,6 +48,8 @@ custom_block.delete:
path: '/block/{custom_block}/delete'
defaults:
_entity_form: 'custom_block.delete'
options:
_admin_route: TRUE
requirements:
_entity_access: 'custom_block.delete'
......
......@@ -167,19 +167,6 @@ function book_menu_link_defaults() {
return $links;
}
/**
* Implements hook_admin_paths().
*/
function book_admin_paths() {
if (\Drupal::config('node.settings')->get('use_admin_theme')) {
$paths = array(
'node/*/outline' => TRUE,
'node/*/outline/remove' => TRUE,
);
return $paths;
}
}
/**
* Returns an array of all books.
*
......
......@@ -38,6 +38,8 @@ book.outline:
requirements:
_permission: 'administer book outlines'
_entity_access: 'node.view'
options:
_node_operation_route: TRUE
book.admin_edit:
path: '/admin/structure/book/{node}'
......@@ -56,6 +58,7 @@ book.remove:
_title: 'Remove from outline'
options:
_access_mode: 'ALL'
_node_operation_route: TRUE
requirements:
_permission: 'administer book outlines'
_entity_access: 'node.view'
......
......@@ -37,8 +37,7 @@ function edit_page_build(&$page) {
}
// In-place editing is only supported on the front-end.
$path = \Drupal::request()->attributes->get('_system_path');
if (path_is_admin($path)) {
if (\Drupal::service('router.admin_context')->isAdminRoute()) {
return;
}
......
......@@ -7,6 +7,7 @@
namespace Drupal\locale\ParamConverter;
use Drupal\Core\Routing\AdminContext;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Route;
use Drupal\Core\Config\ConfigFactoryInterface;
......@@ -37,15 +38,29 @@ class LocaleAdminPathConfigEntityConverter extends EntityConverter {
*/
protected $configFactory;
/**
* The route admin context to determine whether a route is an admin one.
*
* @var \Drupal\Core\Routing\AdminContext
*/
protected $adminContext;
/**
* Constructs a new EntityConverter.
*
* @param \Drupal\Core\Entity\EntityManagerInterface $entityManager
* @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
* The entity manager.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
* @param \Drupal\Core\Routing\AdminContext $admin_context
* The route admin context service.
*
*/
public function __construct(EntityManagerInterface $entity_manager, ConfigFactoryInterface $config_factory) {
$this->configFactory = $config_factory;
public function __construct(EntityManagerInterface $entity_manager, ConfigFactoryInterface $config_factory, AdminContext $admin_context) {
parent::__construct($entity_manager);
$this->configFactory = $config_factory;
$this->adminContext = $admin_context;
}
/**
......@@ -73,9 +88,7 @@ public function applies($definition, $name, Route $route) {
$entity_type_id = substr($definition['type'], strlen('entity:'));
$entity_type = $this->entityManager->getDefinition($entity_type_id);
if ($entity_type->isSubclassOf('\Drupal\Core\Config\Entity\ConfigEntityInterface')) {
// path_is_admin() needs the path without the leading slash.
$path = ltrim($route->getPath(), '/');
return path_is_admin($path);
return $this->adminContext->isAdminRoute($route);
}
}
return FALSE;
......
......@@ -3,7 +3,7 @@ services:
class: Drupal\locale\ParamConverter\LocaleAdminPathConfigEntityConverter
tags:
- { name: paramconverter, priority: 5 }
arguments: ['@entity.manager', '@config.factory']
arguments: ['@entity.manager', '@config.factory', '@router.admin_context']
locale.config.typed:
class: Drupal\locale\LocaleConfigManager
arguments: ['@config.storage', '@config.storage.schema', '@config.storage.installer', '@locale.storage', '@cache.config', '@config.factory']
......
<?php
/**
* @file
* Contains \Drupal\node\EventSubscriber\NodeAdminRouteSubscriber.
*/
namespace Drupal\node\EventSubscriber;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;
/**
* Sets the _admin_route for specific node-related routes.
*/
class NodeAdminRouteSubscriber extends RouteSubscriberBase {
/**
* The config factory.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $configFactory;
/**
* Constructs a new NodeAdminRouteSubscriber.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The config factory.
*/
public function __construct(ConfigFactoryInterface $config_factory) {
$this->configFactory = $config_factory;
}
/**
* {@inheritdoc}
*/
protected function alterRoutes(RouteCollection $collection, $provider) {
if ($this->configFactory->get('node.settings')->get('use_admin_theme')) {
foreach ($collection->all() as $route) {
if ($route->hasOption('_node_operation_route')) {
$route->setOption('_admin_route', TRUE);
}
}
}
}
}
......@@ -236,26 +236,6 @@ function node_uri(NodeInterface $node) {
);
}
/**
* Implements hook_admin_paths().
*/
function node_admin_paths() {
if (\Drupal::config('node.settings')->get('use_admin_theme')) {
$paths = array(
'node/*/edit' => TRUE,
'node/*/delete' => TRUE,
'node/*/revisions' => TRUE,
'node/*/revisions/*/revert' => TRUE,
'node/*/revisions/*/delete' => TRUE,
'node/*/translations' => TRUE,
'node/*/translations/*' => TRUE,
'node/add' => TRUE,
'node/add/*' => TRUE,
);
return $paths;
}
}
/**
* Gathers a listing of links to nodes.
*
......@@ -1318,6 +1298,7 @@ function node_form_system_themes_admin_form_submit($form, &$form_state) {
\Drupal::config('node.settings')
->set('use_admin_theme', $form_state['values']['use_admin_theme'])
->save();
\Drupal::service('router.builder')->setRebuildNeeded();
}
/**
......
......@@ -19,6 +19,8 @@ node.page_edit:
_entity_form: 'node.edit'
requirements:
_entity_access: 'node.update'
options:
_node_operation_route: TRUE
node.add_page:
path: '/node/add'
......@@ -27,6 +29,7 @@ node.add_page:
_content: '\Drupal\node\Controller\NodeController::addPage'
options:
_access_mode: 'ANY'
_node_operation_route: TRUE
requirements:
_permission: 'administer content types'
_node_add_access: 'node'
......@@ -38,6 +41,8 @@ node.add:
_title_callback: '\Drupal\node\Controller\NodeController::addPageTitle'
requirements:
_node_add_access: 'node:{node_type}'
options:
_node_operation_route: TRUE
node.view:
path: '/node/{node}'
......@@ -53,6 +58,8 @@ node.delete_confirm:
_entity_form: 'node.delete'
requirements:
_entity_access: 'node.delete'
options:
_node_operation_route: TRUE
node.revision_overview:
path: '/node/{node}/revisions'
......@@ -61,6 +68,8 @@ node.revision_overview:
_content: '\Drupal\node\Controller\NodeController::revisionOverview'
requirements:
_access_node_revision: 'view'
options:
_node_operation_route: TRUE
node.revision_show:
path: '/node/{node}/revisions/{node_revision}/view'
......@@ -77,6 +86,8 @@ node.revision_revert_confirm:
_title: 'Revert to earlier revision'
requirements:
_access_node_revision: 'update'
options:
_node_operation_route: TRUE
node.revision_delete_confirm:
path: '/node/{node}/revisions/{node_revision}/delete'
......@@ -85,6 +96,8 @@ node.revision_delete_confirm:
_title: 'Delete earlier revision'
requirements:
_access_node_revision: 'delete'
options:
_node_operation_route: TRUE
node.overview_types:
path: '/admin/structure/types'
......
......@@ -12,3 +12,8 @@ services:
arguments: ['@entity.manager']
tags:
- { name: access_check, applies_to: _node_add_access }
node.admin_path.route_subscriber:
class: Drupal\node\EventSubscriber\NodeAdminRouteSubscriber
arguments: ['@config.factory']
tags:
- { name: event_subscriber }
......@@ -74,6 +74,7 @@ public function testShortcutQuickLink() {
theme_enable(array('seven'));
\Drupal::config('system.theme')->set('admin', 'seven')->save();
$this->container->get('config.factory')->get('node.settings')->set('use_admin_theme', '1')->save();
$this->container->get('router.builder')->rebuild();
$this->drupalLogin($this->root_user);
$this->drupalGet('admin/config/system/cron');
......
......@@ -75,16 +75,6 @@ function shortcut_menu_link_defaults() {
return $links;
}
/**
* Implements hook_admin_paths().
*/
function shortcut_admin_paths() {
$paths = array(
'user/*/shortcuts' => TRUE,
);
return $paths;
}
/**
* Access callback for editing a shortcut set.
*
......
......@@ -74,4 +74,6 @@ shortcut.overview:
_title: 'Shortcuts'
requirements:
_access_shortcut_set_switch: 'TRUE'
options:
_admin_route: TRUE
<?php
/**
* @file
* Contains \Drupal\system\EventSubscriber\AdminRouteSubscriber
*/
namespace Drupal\system\EventSubscriber;
use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;
/**
* Adds the _admin_route option to each admin route.
*/
class AdminRouteSubscriber extends RouteSubscriberBase {
/**
* {@inheritdoc}
*/
protected function alterRoutes(RouteCollection $collection, $provider) {
foreach ($collection->all() as $route) {
if (strpos($route->getPath(), '/admin') === 0 && !$route->hasOption('_admin_route')) {
$route->setOption('_admin_route', TRUE);
}
}
}
}
......@@ -46,50 +46,6 @@ function hook_hook_info() {
return $hooks;
}
/**
* Define administrative paths.
*
* Modules may specify whether or not the routing paths they define are
* to be considered administrative. Other modules may use this information to
* display those pages differently.
*
* To change the administrative status of menu items defined in another module's
* routing paths, modules should implement hook_admin_paths_alter().
*
* @return
* An associative array. For each item, the key is the path in question, in
* a format acceptable to drupal_match_path(). The value for each item should
* be TRUE (for paths considered administrative) or FALSE (for non-
* administrative paths).
*
* @see drupal_match_path()
* @see hook_admin_paths_alter()
*/
function hook_admin_paths() {
$paths = array(
'mymodule/*/add' => TRUE,
'mymodule/*/edit' => TRUE,
);
return $paths;
}
/**
* Redefine administrative paths defined by other modules.
*
* @param $paths
* An associative array of administrative paths, as defined by implementations
* of hook_admin_paths().
*
* @see hook_admin_paths()
*/
function hook_admin_paths_alter(&$paths) {
// Treat all user pages as administrative.
$paths['user'] = TRUE;
$paths['user/*'] = TRUE;
// Treat the forum topic node form as a non-administrative page.
$paths['node/add/forum'] = FALSE;
}
/**
* Perform periodic actions.
*
......
......@@ -1142,7 +1142,7 @@ function system_page_build(&$page) {
// Ensure the same CSS is loaded in template_preprocess_maintenance_page().
$page['#attached']['library'][] = 'core/normalize';
$page['#attached']['library'][] = 'system/base';
if (path_is_admin(current_path())) {
if (\Drupal::service('router.admin_context')->isAdminRoute()) {
$page['#attached']['library'][] = 'system/admin';
}
......@@ -1970,21 +1970,6 @@ function theme_system_config_form($variables) {
return drupal_render_children($variables['form']);
}
/**
* Implements hook_admin_paths().
*/
function system_admin_paths() {
$paths = array(
'admin' => TRUE,
'admin/*' => TRUE,
'batch' => TRUE,
// This page should not be treated as administrative since it outputs its
// own content (outside of any administration theme).
'admin/reports/status/php' => FALSE,
);
return $paths;
}
/**
* Implements hook_path_update().
*/
......
......@@ -283,6 +283,10 @@ system.php:
_controller: 'Drupal\system\Controller\SystemInfoController::php'
requirements:
_permission: 'administer site configuration'
# This page should not be treated as administrative since it outputs its own
# content (outside of any administration theme).
options:
_admin_route: FALSE
system.admin_index:
path: '/admin/index'
......@@ -388,6 +392,8 @@ system.batch_page.normal:
_content: '\Drupal\system\Controller\BatchController::batchPage'
requirements:
_access: 'TRUE'
options:
_admin_route: TRUE
system.batch_page.json:
path: '/batch'
......@@ -396,6 +402,8 @@ system.batch_page.json:
requirements:
_access: 'TRUE'
_format: 'json'
options:
_admin_route: TRUE
system.update:
path: '/core/update.php'
......@@ -17,6 +17,10 @@ services:
class: Drupal\system\PathProcessor\PathProcessorFiles
tags:
- { name: path_processor_inbound, priority: 200 }
system.admin_path.route_subscriber:
class: Drupal\system\EventSubscriber\AdminRouteSubscriber
tags:
- { name: event_subscriber }
theme.negotiator.system.batch:
class: Drupal\system\Theme\BatchNegotiator
arguments: ['@batch.storage']
......
......@@ -247,19 +247,6 @@ function taxonomy_menu_link_defaults() {
return $links;
}
/**
* Implements hook_admin_paths().
*/
function taxonomy_admin_paths() {
$paths = array(
'taxonomy/term/*/edit' => TRUE,
'taxonomy/term/*/delete' => TRUE,
'taxonomy/term/*/translations' => TRUE,
'taxonomy/term/*/translations/*' => TRUE,
);
return $paths;
}
/**
* Checks and updates the hierarchy flag of a vocabulary.
<