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: