diff --git a/core/lib/Drupal/Core/Controller/HtmlPageController.php b/core/lib/Drupal/Core/Controller/HtmlPageController.php
index d87b3ed..f5649dc 100644
--- a/core/lib/Drupal/Core/Controller/HtmlPageController.php
+++ b/core/lib/Drupal/Core/Controller/HtmlPageController.php
@@ -66,8 +66,18 @@ public function content(Request $request, $_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'] = $request->attributes->get('_title');
+ if (!isset($page_content['#title'])) {
+ // A dynamic title takes priority.
+ if ($request->attributes->has('_title_callback')) {
+ $callback = $request->attributes->get('_title_callback');
+ $callable = $this->controllerResolver->getControllerFromDefinition($callback);
+ $arguments = $this->controllerResolver->getArguments($request, $callable);
+ $page_content['#title'] = call_user_func_array($callable, $arguments);
+ }
+ elseif ($request->attributes->has('_title')) {
+ // Fall back to a static string from the route.
+ $page_content['#title'] = $request->attributes->get('_title');
+ }
}
$response = new Response(drupal_render_page($page_content));
diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module
index e5321d4..4fcb546 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 cadbb14..f5ff870 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 942fa9e..08b8691 100644
--- a/core/modules/system/lib/Drupal/system/Tests/System/PageTitleTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/System/PageTitleTest.php
@@ -128,14 +128,29 @@ 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');
$result = $this->xpath('//h1');
$this->assertEqual('Foo', (string) $result[0]);
+
+ // Test the static '_title' route option.
+ $this->drupalGet('test-static-title');
+
+ $this->assertTitle('Static title | Drupal');
+ $result = $this->xpath('//h1');
+ $this->assertEqual('Static title', (string) $result[0]);
+
+ // Test the dynamic '_title_callback' route option.
+ $this->drupalGet('test-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 850ca2d..d73838c 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
@@ -26,4 +26,26 @@ public function renderTitle() {
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 0f45d10..2afeb19 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
@@ -4,3 +4,19 @@ test_page_render_title:
_content: 'Drupal\test_page_test\Controller\Test::renderTitle'
requirements:
_access: 'TRUE'
+
+test_page_static_title:
+ pattern: '/test-static-title'
+ defaults:
+ _content: 'Drupal\test_page_test\Controller\Test::renderPage'
+ _title: 'Static title'
+ requirements:
+ _access: 'TRUE'
+
+test_page_dynamic_title:
+ pattern: '/test-dynamic-title'
+ defaults:
+ _content: 'Drupal\test_page_test\Controller\Test::renderPage'
+ _title_callback: 'Drupal\test_page_test\Controller\Test::dynamicTitle'
+ 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..bace8ed 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\Core\Entity\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\Core\Entity\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 4768f93..20d55c6 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -902,13 +902,9 @@ function user_menu() {
$items['user/%user'] = array(
'title' => 'My account',
- '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),
+ 'route_name' => 'user_view',
);
+
$items['user/%user/view'] = array(
'title' => 'View',
'type' => MENU_DEFAULT_LOCAL_TASK,
@@ -1066,13 +1062,6 @@ function user_menu_title() {
}
/**
- * Menu item title callback - use the user name.
- */
-function user_page_title(UserInterface $account = NULL) {
- return $account ? $account->getUsername() : '';
-}
-
-/**
* Try to validate the user's login credentials locally.
*
* @param $name
@@ -1379,18 +1368,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 90570ea..6539a7c 100644
--- a/core/modules/user/user.routing.yml
+++ b/core/modules/user/user.routing.yml
@@ -96,6 +96,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: