diff --git a/core/core.services.yml b/core/core.services.yml
index 0fca40a..fb74703 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -333,7 +333,7 @@ services:
- { name: route_enhancer, priority: 10 }
controller.page:
class: Drupal\Core\Controller\HtmlPageController
- arguments: ['@http_kernel']
+ arguments: ['@controller_resolver']
controller.dialog:
class: Drupal\Core\Controller\DialogController
arguments: ['@http_kernel']
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 58aebe9..1b9c941 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -10,6 +10,7 @@
use Drupal\Core\DrupalKernel;
use Drupal\Core\Database\Database;
use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\Utility\Title;
use Symfony\Component\ClassLoader\ClassLoader;
use Symfony\Component\ClassLoader\ApcClassLoader;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -202,12 +203,9 @@
define('REQUEST_TIME', (int) $_SERVER['REQUEST_TIME']);
/**
- * Flag for drupal_set_title(); text is not sanitized, so run check_plain().
- */
-const CHECK_PLAIN = 0;
-
-/**
* Flag for drupal_set_title(); text has already been sanitized.
+ *
+ * @todo Move to the Title class.
*/
const PASS_THROUGH = -1;
@@ -1714,7 +1712,7 @@ function drupal_get_title() {
* Optional string value to assign to the page title; or if set to NULL
* (default), leaves the current title unchanged.
* @param $output
- * Optional flag - normally should be left as CHECK_PLAIN. Only set to
+ * Optional flag - normally should be left as Title::CHECK_PLAIN. Only set to
* PASS_THROUGH if you have already removed any possibly dangerous code
* from $title using a function like check_plain() or filter_xss(). With this
* flag the string will be passed through unchanged.
@@ -1722,7 +1720,7 @@ function drupal_get_title() {
* @return
* The updated title of the current page.
*/
-function drupal_set_title($title = NULL, $output = CHECK_PLAIN) {
+function drupal_set_title($title = NULL, $output = Title::CHECK_PLAIN) {
$stored_title = &drupal_static(__FUNCTION__);
if (isset($title)) {
diff --git a/core/includes/common.inc b/core/includes/common.inc
index a4805c0..d3325f1 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -3628,6 +3628,11 @@ function drupal_pre_render_dropbutton($element) {
function drupal_render_page($page) {
$main_content_display = &drupal_static('system_main_content_added', FALSE);
+ // Pull out the page title to set it back later.
+ if (is_array($page) && isset($page['#title'])) {
+ $title = $page['#title'];
+ }
+
// Allow menu callbacks to return strings or arbitrary arrays to render.
// If the array returned is not of #type page directly, we need to fill
// in the page with defaults.
@@ -3652,6 +3657,11 @@ function drupal_render_page($page) {
$page['content']['system_main'] = drupal_set_page_content();
}
+ // Set back the previously stored title.
+ if (isset($title)) {
+ $page['#title'] = $title;
+ }
+
return drupal_render($page);
}
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 7bff76e..00b5a42 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -8,6 +8,7 @@
* customized by user themes.
*/
+use Drupal\Component\Utility\String;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\Config;
use Drupal\Core\Language\Language;
@@ -2626,11 +2627,18 @@ function template_preprocess_html(&$variables) {
}
$site_config = config('system.site');
- // Construct page title.
- if (drupal_get_title()) {
+
+ // Construct the page title.
+ if (isset($variables['page']['#title'])) {
+ $head_title = array(
+ 'title' => strip_tags($variables['page']['#title']),
+ 'name' => String::checkPlain($site_config->get('name')),
+ );
+ }
+ elseif (drupal_get_title()) {
$head_title = array(
'title' => strip_tags(drupal_get_title()),
- 'name' => check_plain($site_config->get('name')),
+ 'name' => String::checkPlain($site_config->get('name')),
);
}
else {
@@ -2639,6 +2647,7 @@ function template_preprocess_html(&$variables) {
$head_title['slogan'] = strip_tags(filter_xss_admin($site_config->get('slogan')));
}
}
+
$variables['head_title_array'] = $head_title;
$variables['head_title'] = implode(' | ', $head_title);
@@ -2756,7 +2765,13 @@ function template_preprocess_page(&$variables) {
$variables['site_name'] = (theme_get_setting('features.name') ? check_plain($site_config->get('name')) : '');
$variables['site_slogan'] = (theme_get_setting('features.slogan') ? filter_xss_admin($site_config->get('slogan')) : '');
$variables['tabs'] = menu_local_tabs();
- $variables['title'] = new RenderWrapper('drupal_get_title');
+
+ if (isset($variables['page']['#title'])) {
+ $variables['title'] = $variables['page']['#title'];
+ }
+ else {
+ $variables['title'] = new RenderWrapper('drupal_get_title');
+ }
// Pass the main menu and secondary menu to the template as render arrays.
if (!empty($variables['main_menu'])) {
@@ -2934,7 +2949,13 @@ function template_preprocess_maintenance_page(&$variables) {
$site_slogan = $site_config->get('slogan');
// Construct page title
- if (drupal_get_title()) {
+ if (isset($variables['page']['#title'])) {
+ $head_title = array(
+ 'title' => strip_tags($variables['page']['#title']),
+ 'name' => check_plain($site_name),
+ );
+ }
+ elseif (drupal_get_title()) {
$head_title = array(
'title' => strip_tags(drupal_get_title()),
'name' => check_plain($site_name),
@@ -2978,7 +2999,6 @@ function template_preprocess_maintenance_page(&$variables) {
$variables['site_name'] = (theme_get_setting('features.name') ? check_plain($site_name) : '');
$variables['site_slogan'] = (theme_get_setting('features.slogan') ? filter_xss_admin($site_slogan) : '');
$variables['tabs'] = '';
- $variables['title'] = drupal_get_title();
// Compile a list of classes that are going to be applied to the body element.
$variables['attributes']['class'][] = 'maintenance-page';
@@ -3014,6 +3034,14 @@ function template_preprocess_maintenance_page(&$variables) {
// be called when printed.
$variables['styles'] = new RenderWrapper('drupal_get_css', array($css));
$variables['scripts'] = new RenderWrapper('drupal_get_js');
+
+ // Allow the page to define a title.
+ if (isset($variables['page']['#title'])) {
+ $variables['title'] = $variables['page']['#title'];
+ }
+ if (!isset($variables['title'])) {
+ $variables['title'] = drupal_get_title();
+ }
}
/**
diff --git a/core/lib/Drupal/Core/Controller/ControllerResolver.php b/core/lib/Drupal/Core/Controller/ControllerResolver.php
index 385f198..1c0cbbd 100644
--- a/core/lib/Drupal/Core/Controller/ControllerResolver.php
+++ b/core/lib/Drupal/Core/Controller/ControllerResolver.php
@@ -65,7 +65,7 @@ public function __construct(ContainerInterface $container, LoggerInterface $logg
* @throws \InvalidArgumentException
* If the controller class does not exist
*/
- protected function createController($controller) {
+ public function createController($controller) {
// Controller in the service:method notation.
$count = substr_count($controller, ':');
if ($count == 1) {
diff --git a/core/lib/Drupal/Core/Controller/HtmlPageController.php b/core/lib/Drupal/Core/Controller/HtmlPageController.php
index fd89081..d950e9d 100644
--- a/core/lib/Drupal/Core/Controller/HtmlPageController.php
+++ b/core/lib/Drupal/Core/Controller/HtmlPageController.php
@@ -7,6 +7,7 @@
namespace Drupal\Core\Controller;
+use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\HttpKernelInterface;
@@ -17,19 +18,20 @@
class HtmlPageController {
/**
- * The HttpKernel object to use for subrequests.
+ * The controller resolver.
*
- * @var \Symfony\Component\HttpKernel\HttpKernelInterface
+ * @var \Drupal\Core\Controller\ControllerResolver
*/
- protected $httpKernel;
+ protected $controllerResolver;
/**
- * Constructs a new HtmlPageController.
+ * Constructs a HtmlPageController instance.
*
- * @param \Symfony\Component\HttpKernel\HttpKernelInterface $kernel
+ * @param \Drupal\Core\Controller\ControllerResolver $controller_resolver
+ * The controller resolver.
*/
- public function __construct(HttpKernelInterface $kernel) {
- $this->httpKernel = $kernel;
+ public function __construct(ControllerResolver $controller_resolver) {
+ $this->controllerResolver = $controller_resolver;
}
/**
@@ -44,28 +46,32 @@ public function __construct(HttpKernelInterface $kernel) {
* A response object.
*/
public function content(Request $request, $_content) {
-
- // @todo When we have a Generator, we can replace the forward() call with
- // a render() call, which would handle ESI and hInclude as well. That will
- // require an _internal route. For examples, see:
- // https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing/internal.xml
- // https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/InternalController.php
- $attributes = clone $request->attributes;
- $controller = $_content;
-
- // We need to clean off the derived information and such so that the
- // subrequest can be processed properly without leaking data through.
- $attributes->remove('system_path');
- $attributes->remove('_content');
-
- $response = $this->httpKernel->forward($controller, $attributes->all(), $request->query->all());
-
- // For successful (HTTP status 200) responses, decorate with blocks.
- if ($response->isOk()) {
- $page_content = $response->getContent();
- $response = new Response(drupal_render_page($page_content));
+ $callable = $this->controllerResolver->createController($_content);
+ $arguments = $this->controllerResolver->getArguments($request, $callable);
+ $page_content = call_user_func_array($callable, $arguments);
+ if ($page_content instanceof Response) {
+ return $page_content;
+ }
+ if (!is_array($page_content)) {
+ $page_content = array(
+ '#markup' => $page_content,
+ );
+ }
+ // createController() will usually return a callable in array format,
+ // but it's not guaranteed.
+ $obj = NULL;
+ if (is_array($callable)) {
+ list($obj, $method) = $callable;
+ }
+ if (is_object($obj) && $obj instanceof TitleControllerInterface) {
+ $page_content['#title'] = $obj->getTitle($request);
+ }
+ elseif ($request->attributes->has('_title')) {
+ $page_content['#title'] = $request->attributes->get('_title');
}
+ $response = new Response(drupal_render_page($page_content));
return $response;
}
+
}
diff --git a/core/lib/Drupal/Core/Controller/TitleControllerInterface.php b/core/lib/Drupal/Core/Controller/TitleControllerInterface.php
new file mode 100644
index 0000000..fffd0fe
--- /dev/null
+++ b/core/lib/Drupal/Core/Controller/TitleControllerInterface.php
@@ -0,0 +1,27 @@
+entityManager->getStorageController('block')->create(array('plugin' => $plugin_id, 'theme' => $theme));
diff --git a/core/modules/system/lib/Drupal/system/Tests/System/PageTitleFilteringTest.php b/core/modules/system/lib/Drupal/system/Tests/System/PageTitleTest.php
similarity index 82%
rename from core/modules/system/lib/Drupal/system/Tests/System/PageTitleFilteringTest.php
rename to core/modules/system/lib/Drupal/system/Tests/System/PageTitleTest.php
index 7881a83..bdf0ddd 100644
--- a/core/modules/system/lib/Drupal/system/Tests/System/PageTitleFilteringTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/System/PageTitleTest.php
@@ -2,22 +2,24 @@
/**
* @file
- * Definition of Drupal\system\Tests\System\PageTitleFilteringTest.
+ * Contains \Drupal\system\Tests\System\PageTitleTest.
*/
namespace Drupal\system\Tests\System;
+use Drupal\Component\Utility\String;
use Drupal\Core\Language\Language;
+use Drupal\Core\Utility\Title;
use Drupal\simpletest\WebTestBase;
-class PageTitleFilteringTest extends WebTestBase {
+class PageTitleTest extends WebTestBase {
/**
* Modules to enable.
*
* @var array
*/
- public static $modules = array('node');
+ public static $modules = array('node', 'test_page_test');
protected $content_user;
protected $saved_title;
@@ -27,7 +29,7 @@ class PageTitleFilteringTest extends WebTestBase {
*/
public static function getInfo() {
return array(
- 'name' => 'HTML in page titles',
+ 'name' => 'Page titles',
'description' => 'Tests correct handling or conversion by drupal_set_title() and drupal_get_title() and checks the correct escaping of site name and slogan.',
'group' => 'System'
);
@@ -61,10 +63,10 @@ function tearDown() {
*/
function testTitleTags() {
$title = "string with HTML";
- // drupal_set_title's $filter is CHECK_PLAIN by default, so the title should be
+ // drupal_set_title's $filter is Title::CHECK_PLAIN by default, so the title should be
// returned with check_plain().
- drupal_set_title($title, CHECK_PLAIN);
- $this->assertTrue(strpos(drupal_get_title(), '') === FALSE, 'Tags in title converted to entities when $output is CHECK_PLAIN.');
+ drupal_set_title($title, Title::CHECK_PLAIN);
+ $this->assertTrue(strpos(drupal_get_title(), '') === FALSE, 'Tags in title converted to entities when $output is Title::CHECK_PLAIN.');
// drupal_set_title's $filter is passed as PASS_THROUGH, so the title should be
// returned with HTML.
drupal_set_title($title, PASS_THROUGH);
@@ -122,4 +124,18 @@ function testTitleXSS() {
$this->assertNoRaw($slogan, 'Check for the unfiltered version of the slogan.');
$this->assertRaw($slogan_filtered, 'Check for the filtered version of the slogan.');
}
+
+ /**
+ * Tests the page title of render arrays.
+ *
+ * @see \Drupal\test_page_test\Controller\Test::renderTitle()
+ */
+ public function testRenderTitle() {
+ $this->drupalGet('test-render-title');
+
+ $this->assertTitle('Foo | Drupal');
+ $result = $this->xpath('//h1[@class = "title"]');
+ $this->assertEqual('Foo', (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
new file mode 100644
index 0000000..e82d97a
--- /dev/null
+++ b/core/modules/system/tests/modules/test_page_test/lib/Drupal/test_page_test/Controller/Test.php
@@ -0,0 +1,38 @@
+