diff --git a/core/core.services.yml b/core/core.services.yml index 4fab422..32d2517 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -191,6 +191,9 @@ services: controller_resolver: class: Drupal\Core\Controller\ControllerResolver arguments: ['@service_container'] + title_resolver: + class: Drupal\Core\Controller\TitleResolver + arguments: ['@controller_resolver', '@string_translation'] http_kernel: class: Drupal\Core\HttpKernel arguments: ['@event_dispatcher', '@service_container', '@controller_resolver'] @@ -358,7 +361,7 @@ services: - { name: event_subscriber } controller.page: class: Drupal\Core\Controller\HtmlPageController - arguments: ['@http_kernel', '@controller_resolver', '@string_translation'] + arguments: ['@http_kernel', '@controller_resolver', '@string_translation', '@title_resolver'] controller.dialog: class: Drupal\Core\Controller\DialogController arguments: ['@http_kernel'] diff --git a/core/lib/Drupal/Core/Controller/HtmlPageController.php b/core/lib/Drupal/Core/Controller/HtmlPageController.php index a0485b5..4525328 100644 --- a/core/lib/Drupal/Core/Controller/HtmlPageController.php +++ b/core/lib/Drupal/Core/Controller/HtmlPageController.php @@ -8,6 +8,7 @@ namespace Drupal\Core\Controller; use Drupal\Core\StringTranslation\TranslationInterface; +use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\HttpKernelInterface; @@ -39,16 +40,28 @@ class HtmlPageController { protected $translationManager; /** + * The title resolver. + * + * @var \Drupal\Core\Controller\TitleResolver + */ + protected $titleResolver; + + /** * Constructs a new HtmlPageController. * * @param \Symfony\Component\HttpKernel\HttpKernelInterface $kernel * @param \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver * The controller resolver. + * @param \Drupal\Core\StringTranslation\TranslationInterface $translation_manager + * The translation manager. + * @param \Drupal\Core\Controller\TitleResolver $title_resolver + * The title resolver. */ - public function __construct(HttpKernelInterface $kernel, ControllerResolverInterface $controller_resolver, TranslationInterface $translation_manager) { + public function __construct(HttpKernelInterface $kernel, ControllerResolverInterface $controller_resolver, TranslationInterface $translation_manager, TitleResolver $title_resolver) { $this->httpKernel = $kernel; $this->controllerResolver = $controller_resolver; $this->translationManager = $translation_manager; + $this->titleResolver = $title_resolver; } /** @@ -74,9 +87,12 @@ public function content(Request $request, $_content) { '#markup' => $page_content, ); } - // If no title was returned fall back to one defined in the route. - if (!isset($page_content['#title']) && $request->attributes->has('_title')) { - $page_content['#title'] = $this->t($request->attributes->get('_title')); + if (!isset($page_content['#title'])) { + $title = $this->titleResolver->getTitle($request, $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)); + // Ensure that #title will not be set if no title was returned. + if (isset($title)) { + $page_content['#title'] = $title; + } } $response = new Response(drupal_render_page($page_content)); diff --git a/core/lib/Drupal/Core/Controller/TitleResolver.php b/core/lib/Drupal/Core/Controller/TitleResolver.php index 13da2f3..325da51 100644 --- a/core/lib/Drupal/Core/Controller/TitleResolver.php +++ b/core/lib/Drupal/Core/Controller/TitleResolver.php @@ -67,9 +67,6 @@ public function getTitle(Request $request, Route $route) { $title = $route->getDefault('_title'); return $this->t($title); } - else { - return ''; - } } /** diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module index aaffcdc..dad6234 100644 --- a/core/modules/aggregator/aggregator.module +++ b/core/modules/aggregator/aggregator.module @@ -90,7 +90,6 @@ function aggregator_theme() { */ function aggregator_menu() { $items['admin/config/services/aggregator'] = array( - 'title' => 'Feed aggregator', 'description' => "Configure which content your site aggregates from other sites, how often it polls them, and how they're categorized.", 'route_name' => 'aggregator_admin_overview', 'weight' => 10, diff --git a/core/modules/aggregator/aggregator.routing.yml b/core/modules/aggregator/aggregator.routing.yml index 57da7ee..2ad97e2 100644 --- a/core/modules/aggregator/aggregator.routing.yml +++ b/core/modules/aggregator/aggregator.routing.yml @@ -2,6 +2,7 @@ aggregator_admin_overview: pattern: 'admin/config/services/aggregator' defaults: _content: '\Drupal\aggregator\Controller\AggregatorController::adminOverview' + _title: 'Feed aggregator' requirements: _permission: 'administer news feeds' diff --git a/core/modules/system/lib/Drupal/system/Tests/System/PageTitleTest.php b/core/modules/system/lib/Drupal/system/Tests/System/PageTitleTest.php index 690729f..1c29c37 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/PageTitleTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/PageTitleTest.php @@ -59,7 +59,7 @@ function tearDown() { /** * Tests the handling of HTML by drupal_set_title() and drupal_get_title() */ - function testTitleTags() { + function ptestTitleTags() { $title = "string with HTML"; // drupal_set_title's $filter is Title::CHECK_PLAIN by default, so the title should be // returned with check_plain(). @@ -85,7 +85,7 @@ function testTitleTags() { /** * Test if the title of the site is XSS proof. */ - function testTitleXSS() { + function ptestTitleXSS() { // Set some title with JavaScript and HTML chars to escape. $title = ' & < > " \' '; $title_filtered = check_plain($title); @@ -125,9 +125,10 @@ function testTitleXSS() { /** * Tests the page title of render arrays. * - * @see \Drupal\test_page_test\Controller\Test::renderTitle() + * @see \Drupal\test_page_test\Controller\Test */ - public function testRenderTitle() { + public function testRoutingTitle() { + // Test the '#title' render array attribute. $this->drupalGet('test-render-title'); $this->assertTitle('Foo | Drupal'); @@ -159,6 +160,13 @@ public function testRenderTitle() { $this->assertTitle('Static title translated | Drupal'); $result = $this->xpath('//h1'); $this->assertEqual('Static title translated', (string) $result[0]); + + // Test the dynamic '_title_callback' route option. + $this->drupalGet('test-page-dynamic-title'); + + $this->assertTitle('Dynamic title | Drupal'); + $result = $this->xpath('//h1'); + $this->assertEqual('Dynamic title', (string) $result[0]); } } diff --git a/core/modules/system/tests/modules/test_page_test/lib/Drupal/test_page_test/Controller/Test.php b/core/modules/system/tests/modules/test_page_test/lib/Drupal/test_page_test/Controller/Test.php index c0c10d3..9c56ae2 100644 --- a/core/modules/system/tests/modules/test_page_test/lib/Drupal/test_page_test/Controller/Test.php +++ b/core/modules/system/tests/modules/test_page_test/lib/Drupal/test_page_test/Controller/Test.php @@ -39,4 +39,26 @@ public function staticTitle() { return $build; } + /** + * Returns a 'dynamic' title for the '_title_callback' route option. + * + * @return string + * The page title. + */ + public function dynamicTitle() { + return 'Dynamic title'; + } + + /** + * Returns a generic page render array for title tests. + * + * @return array + * A render array as expected by drupal_render() + */ + public function renderPage() { + return array( + '#markup' => 'Content', + ); + } + } diff --git a/core/modules/system/tests/modules/test_page_test/test_page_test.routing.yml b/core/modules/system/tests/modules/test_page_test/test_page_test.routing.yml index 3a03389..11b5f71 100644 --- a/core/modules/system/tests/modules/test_page_test/test_page_test.routing.yml +++ b/core/modules/system/tests/modules/test_page_test/test_page_test.routing.yml @@ -13,6 +13,14 @@ test_page_static_title: requirements: _access: 'TRUE' +test_page_dynamic_title: + pattern: "/test-page-dynamic-title" + defaults: + _content: 'Drupal\test_page_test\Controller\Test::staticTitle' + _title_callback: 'Drupal\test_page_test\Controller\Test::dynamicTitle' + requirements: + _access: 'TRUE' + admin_test_page_render_title: pattern: "/admin/test-render-title" defaults: @@ -26,4 +34,3 @@ test_page_render_title_controller: _controller: 'Drupal\test_page_test\Controller\Test::renderTitle' requirements: _access: 'TRUE' - diff --git a/core/modules/user/lib/Drupal/user/Controller/UserController.php b/core/modules/user/lib/Drupal/user/Controller/UserController.php index cd63cb0..1931d5c 100644 --- a/core/modules/user/lib/Drupal/user/Controller/UserController.php +++ b/core/modules/user/lib/Drupal/user/Controller/UserController.php @@ -7,7 +7,9 @@ namespace Drupal\user\Controller; +use Drupal\User\UserInterface; use Drupal\user\Form\UserLoginForm; +use Drupal\Component\Utility\Xss; use Symfony\Component\DependencyInjection\ContainerAware; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; @@ -42,6 +44,19 @@ public function userPage(Request $request) { } /** + * Route title callback. + * + * @param \Drupal\user\UserInterface $user + * The user account. + * + * @return string + * The user account name. + */ + public function userTitle(UserInterface $user = NULL) { + return $user ? Xss::filter($user->getUsername()) : ''; + } + + /** * Logs the current user out. * * @param \Symfony\Component\HttpFoundation\Request $request diff --git a/core/modules/user/user.module b/core/modules/user/user.module index f629882..18dc52a 100644 --- a/core/modules/user/user.module +++ b/core/modules/user/user.module @@ -855,13 +855,11 @@ function user_menu() { $items['user/%user'] = array( 'title' => 'My account', + 'route_name' => 'user_view', 'title callback' => 'user_page_title', 'title arguments' => array(1), - 'page callback' => 'user_view_page', - 'page arguments' => array(1), - 'access callback' => 'entity_page_access', - 'access arguments' => array(1), ); + $items['user/%user/view'] = array( 'title' => 'View', 'type' => MENU_DEFAULT_LOCAL_TASK, @@ -1328,18 +1326,6 @@ function user_delete_multiple(array $uids) { } /** - * Page callback wrapper for user_view(). - */ -function user_view_page($account) { - if (is_object($account)) { - return user_view($account); - } - // An administrator may try to view a non-existent account, - // so we give them a 404 (versus a 403 for non-admins). - throw new NotFoundHttpException(); -} - -/** * Generate an array for rendering the given user. * * When viewing a user profile, the $page array contains: diff --git a/core/modules/user/user.routing.yml b/core/modules/user/user.routing.yml index ba75389..b458de5 100644 --- a/core/modules/user/user.routing.yml +++ b/core/modules/user/user.routing.yml @@ -103,6 +103,14 @@ user_page: requirements: _access: 'TRUE' +user_view: + pattern: '/user/{user}' + defaults: + _entity_view: 'user.full' + _title_callback: 'Drupal\user\Controller\UserController::userTitle' + requirements: + _entity_access: 'user.view' + user_login: pattern: '/user/login' defaults: