diff --git a/core/authorize.php b/core/authorize.php index 086802e28f876d4b50e29a9927dbdf40d1db7612..97fbe24df98a8311f5ba52e7aa7a712824238131 100644 --- a/core/authorize.php +++ b/core/authorize.php @@ -21,7 +21,6 @@ */ use Drupal\Component\Utility\Settings; -use Drupal\Core\Page\DefaultHtmlPageRenderer; // Change the directory to the Drupal root. chdir('..'); @@ -156,7 +155,13 @@ function authorize_access_allowed() { if (!empty($output)) { drupal_add_http_header('Content-Type', 'text/html; charset=utf-8'); - print DefaultHtmlPageRenderer::renderPage($output, $page_title, 'maintenance', array( + $maintenance_page = array( + '#page' => array( + '#title' => $page_title, + ), + '#theme' => 'maintenance_page', + '#content' => $output, '#show_messages' => $show_messages, - )); + ); + print drupal_render($maintenance_page); } diff --git a/core/includes/batch.inc b/core/includes/batch.inc index 153a7de8f8ce13df557abef14b5b0acc7185a967..daa7b2fc3ed4aebb92022b9ec4ff2eacd8f1a193 100644 --- a/core/includes/batch.inc +++ b/core/includes/batch.inc @@ -16,7 +16,6 @@ use Drupal\Component\Utility\Timer; use Drupal\Core\Batch\Percentage; -use Drupal\Core\Page\DefaultHtmlPageRenderer; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RedirectResponse; @@ -122,14 +121,18 @@ function _batch_progress_page() { // the error message. ob_start(); $fallback = $current_set['error_message'] . '
' . $batch['error_message']; + $fallback = array( + '#theme' => 'maintenance_page', + '#title' => $current_set['title'], + '#content' => $fallback, + '#show_messages' => FALSE, + ); // We strip the end of the page using a marker in the template, so any // additional HTML output by PHP shows up inside the page rather than below // it. While this causes invalid HTML, the same would be true if we didn't, // as content is not allowed to appear after anyway. - $fallback = DefaultHtmlPageRenderer::renderPage($fallback, $current_set['title'], 'maintenance', array( - '#show_messages' => FALSE, - )); + $fallback = drupal_render($fallback); list($fallback) = explode('', $fallback); print $fallback; diff --git a/core/includes/errors.inc b/core/includes/errors.inc index 56cf71a2ea4091f3fb2493dbe8f846b99279cb21..56c276fdfaa247717b98912fc4b3fd5f714aa9c0 100644 --- a/core/includes/errors.inc +++ b/core/includes/errors.inc @@ -5,7 +5,6 @@ * Functions for error handling. */ -use Drupal\Core\Page\DefaultHtmlPageRenderer; use Drupal\Core\Utility\Error; use Drupal\Component\Utility\String; use Symfony\Component\HttpFoundation\Response; @@ -232,7 +231,12 @@ function _drupal_log_error($error, $fatal = FALSE) { install_display_output($output, $GLOBALS['install_state']); } else { - $output = DefaultHtmlPageRenderer::renderPage($message, 'Error'); + $output = array( + '#theme' => 'maintenance_page', + '#title' => 'Error', + '#content' => $message, + ); + $output = drupal_render($output); } $response = new Response($output, 500); diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index a2ef2c1282d377e52c3f5e6df9291e14edc42c7f..aa158a2823f3adeedfa4124b292e24c967592eb4 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -14,7 +14,6 @@ use Drupal\Core\Installer\Exception\NoProfilesException; use Drupal\Core\Language\Language; use Drupal\Core\Language\LanguageManager; -use Drupal\Core\Page\DefaultHtmlPageRenderer; use Drupal\Core\StringTranslation\Translator\FileTranslation; use Drupal\Core\Extension\ExtensionDiscovery; use Drupal\Core\DependencyInjection\ContainerBuilder; @@ -904,7 +903,6 @@ function install_display_output($output, $install_state) { // Only show the task list if there is an active task; otherwise, the page // request has ended before tasks have even been started, so there is nothing // meaningful to show. - $regions = array(); if (isset($install_state['active_task'])) { // Let the theming function know when every step of the installation has // been completed. @@ -914,10 +912,20 @@ function install_display_output($output, $install_state) { '#items' => install_tasks_to_display($install_state), '#active' => $active_task, ); - $regions['sidebar_first'] = $task_list; + drupal_add_region_content('sidebar_first', drupal_render($task_list)); } - - print DefaultHtmlPageRenderer::renderPage($output, $output['#title'], 'install', $regions); + $install_page = array( + '#theme' => 'install_page', + // $output has to be rendered here, because the install page template is not + // wrapped into the html template, which means that any #attached libraries + // in $output will not be loaded, because the wrapping HTML has been printed + // already. + '#content' => drupal_render($output), + ); + if (isset($output['#title'])) { + $install_page['#page']['#title'] = $output['#title']; + } + print drupal_render($install_page); exit; } diff --git a/core/includes/theme.inc b/core/includes/theme.inc index c51867e6adb99a0d67fb699ade68ade290681fe0..c63f6790ef9488738333fe655b999ded899a87ee 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -2073,26 +2073,16 @@ function template_preprocess_page(&$variables) { $variables['base_path'] = base_path(); $variables['front_page'] = url(); + $variables['feed_icons'] = drupal_get_feeds(); $variables['language'] = $language_interface; $variables['language']->dir = $language_interface->direction ? 'rtl' : 'ltr'; $variables['logo'] = theme_get_setting('logo.url'); + $variables['main_menu'] = theme_get_setting('features.main_menu') ? menu_main_menu() : array(); + $variables['secondary_menu'] = theme_get_setting('features.secondary_menu') ? menu_secondary_menu() : array(); + $variables['action_links'] = menu_get_local_actions(); $variables['site_name'] = (theme_get_setting('features.name') ? String::checkPlain($site_config->get('name')) : ''); $variables['site_slogan'] = (theme_get_setting('features.slogan') ? filter_xss_admin($site_config->get('slogan')) : ''); - - if (!defined('MAINTENANCE_MODE')) { - $variables['main_menu'] = theme_get_setting('features.main_menu') ? menu_main_menu() : array(); - $variables['secondary_menu'] = theme_get_setting('features.secondary_menu') ? menu_secondary_menu() : array(); - $variables['action_links'] = menu_get_local_actions(); - $variables['tabs'] = menu_local_tabs(); - $variables['feed_icons'] = drupal_get_feeds(); - } - else { - $variables['main_menu'] = array(); - $variables['secondary_menu'] = array(); - $variables['action_links'] = array(); - $variables['tabs'] = array(); - $variables['feed_icons'] = ''; - } + $variables['tabs'] = menu_local_tabs(); // Pass the main menu and secondary menu to the template as render arrays. if (!empty($variables['main_menu'])) { @@ -2133,12 +2123,10 @@ function template_preprocess_page(&$variables) { // re-use the cache of an already retrieved menu containing the active link // for the current page. // @see menu_tree_page_data() - if (!defined('MAINTENANCE_MODE')) { - $variables['breadcrumb'] = array( - '#theme' => 'breadcrumb', - '#breadcrumb' => \Drupal::service('breadcrumb')->build(\Drupal::request()->attributes->all()), - ); - } + $variables['breadcrumb'] = array( + '#theme' => 'breadcrumb', + '#breadcrumb' => \Drupal::service('breadcrumb')->build(\Drupal::request()->attributes->all()), + ); } /** @@ -2216,6 +2204,12 @@ function theme_get_suggestions($args, $base, $delimiter = '__') { * * Default template: maintenance-page.html.twig. * + * The variables array generated here is a mirror of + * template_preprocess_page(). This preprocessor will run its course when + * theme_maintenance_page() is invoked. An alternate template file of + * maintenance-page--offline.html.twig can be used when the database is offline + * to hide errors and completely replace the content. + * * @param array $variables * An associative array containing: * - content - An array of page content. @@ -2223,29 +2217,117 @@ function theme_get_suggestions($args, $base, $delimiter = '__') { * @see system_page_build() */ function template_preprocess_maintenance_page(&$variables) { - // @todo Rename the templates to page--maintenance + page--install. - template_preprocess_page($variables); - - $page_object = $variables['page']['#page']; - $attributes = $page_object->getBodyAttributes(); - $classes = $attributes['class']; - $classes[] = 'maintenance-page'; - $classes[] = 'in-maintenance'; - if (isset($variables['db_is_active']) && !$variables['db_is_active']) { - $classes[] = 'db-offline'; + $language_interface = \Drupal::languageManager()->getCurrentLanguage(); + + // Initializes attributes which are specific to the html element. + $variables['html_attributes'] = new Attribute; + + // HTML element attributes. + $variables['html_attributes']['lang'] = $language_interface->id; + $variables['html_attributes']['dir'] = $language_interface->direction ? 'rtl' : 'ltr'; + + // Add favicon + if (theme_get_setting('features.favicon')) { + $favicon = theme_get_setting('favicon.url'); + $type = theme_get_setting('favicon.mimetype'); + $build['#attached']['drupal_add_html_head_link'][][] = array( + 'rel' => 'shortcut icon', + 'href' => UrlHelper::stripDangerousProtocols($favicon), + 'type' => $type, + ); + drupal_render($build); } - $attributes['class'] = $classes; - // @see system_page_build() - $attached = array( - '#attached' => array( - 'library' => array( - 'core/normalize', - 'system/maintenance', - ), - ), - ); + foreach (system_region_list($GLOBALS['theme']) as $region_key => $region_name) { + if (!isset($variables[$region_key])) { + $variables[$region_key] = array(); + } + // Append region content set with drupal_add_region_content() as markup. + if ($region_content = drupal_get_region_content($region_key)) { + $variables[$region_key][]['#markup'] = $region_content; + } + } + + // Setup layout variable. + $variables['layout'] = 'none'; + if (!empty($variables['sidebar_first'])) { + $variables['layout'] = 'first'; + } + if (!empty($variables['sidebar_second'])) { + $variables['layout'] = ($variables['layout'] == 'first') ? 'both' : 'second'; + } + + $site_config = \Drupal::config('system.site'); + $site_name = $site_config->get('name'); + $site_slogan = $site_config->get('slogan'); + + // 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')), + ); + } + else { + $head_title = array('name' => String::checkPlain($site_name)); + if ($site_slogan) { + $head_title['slogan'] = strip_tags(filter_xss_admin($site_slogan)); + } + } + + // These are usually added from system_page_build() except maintenance.css. + // When the database is inactive it's not called so we add it here. + $default_css['library'][] = 'core/normalize'; + $default_css['library'][] = 'system/maintenance'; + $attached = array('#attached' => $default_css); drupal_render($attached); + $variables['messages'] = array( + '#theme' => 'status_messages', + '#access' => $variables['show_messages'], + ); + + $variables['head_title_array'] = $head_title; + $variables['head_title'] = implode(' | ', $head_title); + $variables['front_page'] = url(); + $variables['help'] = ''; + $variables['language'] = $language_interface; + $variables['logo'] = theme_get_setting('logo.url'); + $variables['site_name'] = (theme_get_setting('features.name') ? String::checkPlain($site_name) : ''); + $variables['site_slogan'] = (theme_get_setting('features.slogan') ? filter_xss_admin($site_slogan) : ''); + + // Compile a list of classes that are going to be applied to the body element. + $variables['attributes']['class'][] = 'maintenance-page'; + $variables['attributes']['class'][] = 'in-maintenance'; + if (isset($variables['db_is_active']) && !$variables['db_is_active']) { + $variables['attributes']['class'][] = 'db-offline'; + } + if ($variables['layout'] == 'both') { + $variables['attributes']['class'][] = 'two-sidebars'; + } + elseif ($variables['layout'] == 'none') { + $variables['attributes']['class'][] = 'no-sidebars'; + } + else { + $variables['attributes']['class'][] = 'one-sidebar'; + $variables['attributes']['class'][] = 'sidebar-' . $variables['layout']; + } + + $variables['head'] = drupal_get_html_head(); + + // While this code is used in the installer, the language module may not be + // enabled yet (even maybe no database set up yet), but an RTL language + // selected should result in RTL stylesheets loaded properly already. + $css = _drupal_add_css(); + include_once DRUPAL_ROOT . '/core/modules/language/language.module'; + // Wrapping drupal_get_css() and drupal_get_js() in an object so they can + // 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']; + } } /** @@ -2253,21 +2335,20 @@ function template_preprocess_maintenance_page(&$variables) { * * Default template: install-page.html.twig. * + * The variables array generated here is a mirror of + * template_preprocess_page(). This preprocessor will run its course when + * theme_install_page() is invoked. + * * @param array $variables * An associative array containing: * - content - An array of page content. * * @see template_preprocess_maintenance_page() + * */ function template_preprocess_install_page(&$variables) { template_preprocess_maintenance_page($variables); - - $page_object = $variables['page']['#page']; - $attributes = $page_object->getBodyAttributes(); - $classes = $attributes['class']; - $classes[] = 'install-page'; - $attributes['class'] = $classes; - + $variables['attributes']['class'][] = 'install-page'; // Override the site name that is displayed on the page, since Drupal is // still in the process of being installed. $distribution_name = String::checkPlain(drupal_install_profile_distribution_name()); @@ -2565,11 +2646,11 @@ function drupal_common_theme() { ), // From theme.maintenance.inc. 'maintenance_page' => array( - 'render element' => 'page', + 'variables' => array('content' => NULL, 'show_messages' => TRUE, 'page' => array()), 'template' => 'maintenance-page', ), 'install_page' => array( - 'render element' => 'page', + 'variables' => array('content' => NULL, 'show_messages' => TRUE, 'page' => array()), 'template' => 'install-page', ), 'task_list' => array( diff --git a/core/includes/theme.maintenance.inc b/core/includes/theme.maintenance.inc index 1cd436c4837dfa8a3dc2194a7c0e8ff338f71046..57183d6d4baea62469c10fcf208d63676e1b84dc 100644 --- a/core/includes/theme.maintenance.inc +++ b/core/includes/theme.maintenance.inc @@ -31,7 +31,6 @@ function _drupal_maintenance_theme() { require_once __DIR__ . '/unicode.inc'; require_once __DIR__ . '/file.inc'; require_once __DIR__ . '/module.inc'; - require_once __DIR__ . '/database.inc'; Unicode::check(); // Install and update pages are treated differently to prevent theming overrides. @@ -44,6 +43,13 @@ function _drupal_maintenance_theme() { } } else { + // The bootstrap was not complete. So we are operating in a crippled + // environment, we need to bootstrap just enough to allow hook invocations + // to work. See _drupal_log_error(). + if (!class_exists('Drupal\Core\Database\Database', FALSE)) { + require_once __DIR__ . '/database.inc'; + } + // Use the maintenance theme if specified, otherwise attempt to use the // default site theme. try { diff --git a/core/includes/update.inc b/core/includes/update.inc index f7642cdefbba98628a511ada73e184ba90318984..fd02de09ada0decbf200b1aef925da70a489bc66 100644 --- a/core/includes/update.inc +++ b/core/includes/update.inc @@ -14,7 +14,6 @@ use Drupal\Core\Config\FileStorage; use Drupal\Core\Config\ConfigException; use Drupal\Core\DrupalKernel; -use Drupal\Core\Page\DefaultHtmlPageRenderer; use Drupal\Core\Utility\Error; use Drupal\Component\Uuid\Uuid; use Drupal\Component\Utility\NestedArray; @@ -149,13 +148,19 @@ function update_check_requirements($skip_warnings = FALSE) { // them if the caller has indicated they should be skipped. if ($severity == REQUIREMENT_ERROR || ($severity == REQUIREMENT_WARNING && !$skip_warnings)) { update_task_list('requirements'); - $status_report = array( + $status = array( '#theme' => 'status_report', '#requirements' => $requirements, ); - $status_report['#suffix'] = 'Check the messages and try again.'; + $status_report = drupal_render($status); + $status_report .= 'Check the messages and try again.'; drupal_add_http_header('Content-Type', 'text/html; charset=utf-8'); - print DefaultHtmlPageRenderer::renderPage($status_report, 'Requirements problem'); + $maintenance_page = array( + '#theme' => 'maintenance_page', + '#title' => 'Requirements problem', + '#content' => $status_report, + ); + print drupal_render($maintenance_page); exit(); } } diff --git a/core/lib/Drupal/Core/Controller/ExceptionController.php b/core/lib/Drupal/Core/Controller/ExceptionController.php index 9302ab9f0816752022bf9a77634abf49e79e0461..d79567c3ba938610a91e73e10df465721ae14bb6 100644 --- a/core/lib/Drupal/Core/Controller/ExceptionController.php +++ b/core/lib/Drupal/Core/Controller/ExceptionController.php @@ -7,7 +7,6 @@ namespace Drupal\Core\Controller; -use Drupal\Core\Page\DefaultHtmlPageRenderer; use Drupal\Core\Page\HtmlPageRendererInterface; use Drupal\Core\StringTranslation\TranslationInterface; use Symfony\Component\DependencyInjection\ContainerAwareInterface; @@ -354,8 +353,15 @@ public function on500Html(FlattenException $exception, Request $request) { drupal_set_message($message, $class, TRUE); } - $content = t('The website has encountered an error. Please try again later.'); - $output = DefaultHtmlPageRenderer::renderPage($content, t('Error')); + $page_content = array( + '#theme' => 'maintenance_page', + '#content' => t('The website has encountered an error. Please try again later.'), + '#page' => array( + '#title' => t('Error'), + ), + ); + + $output = drupal_render($page_content); $response = new Response($output); $response->setStatusCode(500, '500 Service unavailable (with message)'); diff --git a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php index 93501d0fabd18d3c0d75921ef0b8ffee90e043f8..93bfb42f5eb0a23ca6755951ad1837dea8fc5206 100644 --- a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php @@ -7,8 +7,6 @@ namespace Drupal\Core\EventSubscriber; -use Drupal\Component\Utility\String; -use Drupal\Core\Page\DefaultHtmlPageRenderer; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\KernelEvents; @@ -46,10 +44,14 @@ public function onKernelRequestMaintenance(GetResponseEvent $event) { if ($request->attributes->get('_maintenance') != MENU_SITE_ONLINE && !($response instanceof RedirectResponse)) { // Deliver the 503 page. drupal_maintenance_theme(); - $content = filter_xss_admin(String::format(\Drupal::config('system.maintenance')->get('message'), array( - '@site' => \Drupal::config('system.site')->get('name'), - ))); - $content = DefaultHtmlPageRenderer::renderPage($content, t('Site under maintenance')); + $maintenance_page = array( + '#theme' => 'maintenance_page', + '#title' => t('Site under maintenance'), + '#content' => filter_xss_admin( + t(\Drupal::config('system.maintenance')->get('message'), array('@site' => \Drupal::config('system.site')->get('name'))) + ), + ); + $content = drupal_render($maintenance_page); $response = new Response('Service unavailable', 503); $response->setContent($content); $event->setResponse($response); diff --git a/core/lib/Drupal/Core/Page/DefaultHtmlPageRenderer.php b/core/lib/Drupal/Core/Page/DefaultHtmlPageRenderer.php index 4358eadb8f934131cdd479081aaecb69a854f81a..821d42d9e145cad2a6d7d25737950b42e63628a1 100644 --- a/core/lib/Drupal/Core/Page/DefaultHtmlPageRenderer.php +++ b/core/lib/Drupal/Core/Page/DefaultHtmlPageRenderer.php @@ -23,87 +23,4 @@ public function render(HtmlPage $page) { return drupal_render($render); } - /** - * Renders a page using a custom page theme hook and optional region content. - * - * Temporary shim to facilitate modernization progress for special front - * contollers (install.php, update.php, authorize.php), maintenance mode, and - * the exception handler. - * - * Do NOT use this method in your code. This method will be removed as soon - * as architecturally possible. - * - * This is functionally very similar to DefaultHtmlFragmentRenderer::render() - * but with the following important differences: - * - * - drupal_prepare_page() and hook_page_build() cannot be invoked on the - * maintenance and install pages, since possibly enabled page layout/block - * modules would replace the main page content with configured region - * content. - * - This function composes a complete page render array including a page - * template theme suggestion (as opposed to the main page content only). - * - The render cache and cache tags is skipped. - * - * @param array|string $main - * A render array or string containing the main page content. - * @param string $title - * (optional) The page title. - * @param string $theme - * (optional) The theme hook to use for rendering the page. Defaults to - * 'maintenance'. The given value will be appended with '_page' to compose - * the #theme property for #type 'page' currently; e.g., 'maintenance' - * becomes 'maintenance_page'. Ultimately this parameter will be converted - * into a page template theme suggestion; i.e., 'page__$theme'. - * @param array $regions - * (optional) Additional region content to add to the page. The given array - * is added to the page render array, so this parameter may also be used to - * pass e.g. the #show_messages property for #type 'page'. - * - * @return string - * The rendered HTML page. - * - * @internal - */ - public static function renderPage($main, $title = '', $theme = 'maintenance', array $regions = array()) { - // Automatically convert the main page content into a render array. - if (!is_array($main)) { - $main = array('#markup' => $main); - } - $page = new HtmlPage('', array(), $title); - $page_array = array( - '#type' => 'page', - // @todo Change into theme suggestions "page__$theme". - '#theme' => $theme . '_page', - '#title' => $title, - 'content' => array( - 'system_main' => $main, - ), - ); - // Append region content. - $page_array += $regions; - // Add default properties. - $page_array += element_info('page'); - - // hook_page_build() cannot be invoked on the maintenance and install pages, - // because the application is in an unknown or special state. - // In particular on the install page, invoking hook_page_build() directly - // after e.g. Block module has been installed would *replace* the installer - // output with the configured blocks of the installer theme (loaded from - // default configuration of the installation profile). - - // Allow modules and themes to alter the page render array. - // This allows e.g. themes to attach custom libraries. - \Drupal::moduleHandler()->alter('page', $page_array); - - // @todo Move preparePage() before alter() above, so $page_array['#page'] is - // available in hook_page_alter(), so that HTML attributes can be altered. - $page = \Drupal::service('html_fragment_renderer')->preparePage($page, $page_array); - - $page->setBodyTop(drupal_render($page_array['page_top'])); - $page->setBodyBottom(drupal_render($page_array['page_bottom'])); - $page->setContent(drupal_render($page_array)); - - return \Drupal::service('html_page_renderer')->render($page); - } - } diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/RenderElementTypesTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/RenderElementTypesTest.php index ef76a078e5d22777b8555d137f77b0343f08f470..001b8076b895cc9bdb276d80e33dcf55978dbe7b 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Common/RenderElementTypesTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Common/RenderElementTypesTest.php @@ -8,6 +8,10 @@ namespace Drupal\system\Tests\Common; use Drupal\Component\Utility\String; +use Drupal\Component\Utility\UrlHelper; +use Drupal\Component\Utility\Xss; +use Drupal\Core\Language\Language; +use Drupal\Core\Template\Attribute; use Drupal\simpletest\DrupalUnitTestBase; /** @@ -40,72 +44,216 @@ protected function setUp() { * Asserts that an array of elements is rendered properly. * * @param array $elements - * The render element array to test. - * @param string $expected_html - * The expected markup. - * @param string $message - * Assertion message. + * An array of associative arrays describing render elements and their + * expected markup. Each item in $elements must contain the following: + * - 'name': This human readable description will be displayed on the test + * results page. + * - 'value': This is the render element to test. + * - 'expected': This is the expected markup for the element in 'value'. */ - protected function assertElements(array $elements, $expected_html, $message) { - $actual_html = drupal_render($elements); + function assertElements($elements) { + foreach($elements as $element) { + // More complicated "expected" strings may contain placeholders. + if (!empty($element['placeholders'])) { + $element['expected'] = String::format($element['expected'], $element['placeholders']); + } - $out = ''; - $out .= ''; - $out .= ''; - $out .= '
' . String::checkPlain($expected_html) . '
' . String::checkPlain($actual_html) . '
'; - $this->verbose($out); + // We don't care about whitespace for the sake of comparing markup. + $value = new \DOMDocument(); + $value->preserveWhiteSpace = FALSE; + $value->loadXML(drupal_render($element['value'])); - $this->assertIdentical($actual_html, $expected_html, String::checkPlain($message)); + $expected = new \DOMDocument(); + $expected->preserveWhiteSpace = FALSE; + $expected->loadXML($element['expected']); + + $message = isset($element['name']) ? '"' . $element['name'] . '" input rendered correctly by drupal_render().' : NULL; + $this->assertIdentical($value->saveXML(), $expected->saveXML(), $message); + } } /** * Tests system #type 'container'. */ function testContainer() { - // Basic container with no attributes. - $this->assertElements(array( - '#type' => 'container', - '#markup' => 'foo', - ), "
foo
\n", "#type 'container' with no HTML attributes"); - - // Container with a class. - $this->assertElements(array( - '#type' => 'container', - '#markup' => 'foo', - '#attributes' => array( - 'class' => 'bar', + $elements = array( + // Basic container with no attributes. + array( + 'name' => "#type 'container' with no HTML attributes", + 'value' => array( + '#type' => 'container', + '#markup' => 'foo', + ), + 'expected' => '
foo
' . "\n", ), - ), '
foo
' . "\n", "#type 'container' with a class HTML attribute"); - - // Container with children. - $this->assertElements(array( - '#type' => 'container', - 'child' => array( - '#markup' => 'foo', + // Container with a class. + array( + 'name' => "#type 'container' with a class HTML attribute", + 'value' => array( + '#type' => 'container', + '#markup' => 'foo', + '#attributes' => array( + 'class' => 'bar', + ), + ), + 'expected' => '
foo
' . "\n", ), - ), "
foo
\n", "#type 'container' with child elements"); + // Container with children. + array( + 'name' => "#type 'container' with child elements", + 'value' => array( + '#type' => 'container', + 'child' => array( + '#markup' => 'foo', + ), + ), + 'expected' => '
foo
' . "\n", + ), + ); + + $this->assertElements($elements); } /** * Tests system #type 'html_tag'. */ function testHtmlTag() { - // Test auto-closure meta tag generation. - $this->assertElements(array( - '#type' => 'html_tag', - '#tag' => 'meta', - '#attributes' => array( - 'name' => 'description', - 'content' => 'Drupal test', + $elements = array( + // Test auto-closure meta tag generation. + array( + 'name' => "#type 'html_tag' auto-closure meta tag generation", + 'value' => array( + '#type' => 'html_tag', + '#tag' => 'meta', + '#attributes' => array( + 'name' => 'description', + 'content' => 'Drupal test', + ), + ), + 'expected' => '' . "\n", + ), + // Test title tag generation. + array( + 'name' => "#type 'html_tag' title tag generation", + 'value' => array( + '#type' => 'html_tag', + '#tag' => 'title', + '#value' => 'title test', + ), + 'expected' => 'title test' . "\n", + ), + ); + + $this->assertElements($elements); + } + + /** + * Tests common #theme 'maintenance_page'. + */ + function testMaintenancePage() { + // We need to simulate a lot of what would happen in the preprocess, or + // there's no way to make these tests portable. + + // HTML element attributes. + $html_attributes = new Attribute; + $language_interface = \Drupal::service('language_manager')->getCurrentLanguage(); + $html_attributes['lang'] = $language_interface->id; + $html_attributes['dir'] = $language_interface->direction ? 'rtl' : 'ltr'; + + $site_config = \Drupal::config('system.site'); + + // Add favicon. + $favicon = theme_get_setting('favicon.url'); + $type = theme_get_setting('favicon.mimetype'); + drupal_add_html_head_link(array('rel' => 'shortcut icon', 'href' => UrlHelper::stripDangerousProtocols($favicon), 'type' => $type)); + + // Build CSS links. + drupal_static_reset('_drupal_add_css'); + $default_css = array( + '#attached' => array( + 'library' => array( + 'core/normalize', + 'system/maintenance', + ), ), - ), '' . "\n", "#type 'html_tag' auto-closure meta tag generation"); - - // Test title tag generation. - $this->assertElements(array( - '#type' => 'html_tag', - '#tag' => 'title', - '#value' => 'title test', - ), "title test\n", "#type 'html_tag' title tag generation"); + ); + drupal_render($default_css); + $css = _drupal_add_css(); + + // Simulate the expected output of a "vanilla" maintenance page. + $expected = << + + + !head + !head_title + !styles + !scripts + + +
+
+ + Home + +
+

+ !site_name +

+
+
+
+ !title + !content +
+
+ + +EOT; + + $placeholders = array( + '!html_attributes' => $html_attributes->__toString(), + '!head' => drupal_get_html_head(), + '!head_title' => $site_config->get('name'), + '!styles' => drupal_get_css($css), + '!scripts' => drupal_get_js(), + '!attributes.class' => 'maintenance-page in-maintenance no-sidebars', + '!front_page' => url(), + '!logo' => theme_get_setting('logo.url'), + '!site_name' => $site_config->get('name'), + '!title' => '', + '!content' => 'foo', + ); + + // We have to reset drupal_add_css between each test. + drupal_static_reset(); + + // Test basic string for maintenance page content. + // No page title is set, so it should default to the site name. + $elements = array( + array( + 'name' => "#theme 'maintenance_page' with content of foo", + 'value' => array( + '#theme' => 'maintenance_page', + '#content' => 'foo', + '#show_messages' => FALSE, + ), + 'expected' => $expected, + 'placeholders' => $placeholders, + ), + ); + $this->assertElements($elements); + + // Test render array for maintenance page content. + drupal_static_reset(); + $elements[0]['name'] = "#theme 'maintenance_page' with content as a render array"; + $elements[0]['value']['#content'] = array('#markup' => 'foo'); + // Testing with a page title, which should be combined with the site name. + $title = t('A non-empty title'); + $elements[0]['value']['#page']['#title'] = $title; + $elements[0]['placeholders']['!title'] = '

' . $title . '

'; + $elements[0]['placeholders']['!head_title'] = strip_tags($title) . ' | ' . String::checkPlain($site_config->get('name')); + $this->assertElements($elements); } } diff --git a/core/modules/system/templates/install-page.html.twig b/core/modules/system/templates/install-page.html.twig index ad1c04cdda1470414acdc1e4e91aa567593e65cc..031013effe00807e8ea55362948b9aae83c6452a 100644 --- a/core/modules/system/templates/install-page.html.twig +++ b/core/modules/system/templates/install-page.html.twig @@ -3,14 +3,24 @@ * @file * Default theme implementation to display a Drupal installation page. * - * All available variables are mirrored in page.html.twig. - * Some may be blank but they are provided for consistency. + * All the available variables are mirrored in html.html.twig and + * page.html.twig. Some may be blank but they are provided for consistency. * * @see template_preprocess_install_page() * * @ingroup themeable */ #} + + + + {{ head }} + {{ head_title }} + {{ styles }} + {{ scripts }} + + +
@@ -31,25 +41,26 @@

{{ title }}

{% endif %} {{ messages }} - {{ page.content }} + {{ content }} - {% if page.sidebar_first %} + {% if sidebar_first %} {# /.l-sidebar-first #} {% endif %} - {% if page.sidebar_second %} + {% if sidebar_second %} {# /.l-sidebar-second #} {% endif %} - {% if page.footer %} + {% if footer %}
- {{ page.footer }} + {{ footer }}
{% endif %} -
{# /.l-container #} + + diff --git a/core/modules/system/templates/maintenance-page.html.twig b/core/modules/system/templates/maintenance-page.html.twig index 3b5a59ed3372e4bf144ab4fc6896b6e676dffb1a..b5275c71c1c51e40aa3e7788db82c37d01da8c50 100644 --- a/core/modules/system/templates/maintenance-page.html.twig +++ b/core/modules/system/templates/maintenance-page.html.twig @@ -3,7 +3,7 @@ * @file * Default theme implementation to display a single Drupal page while offline. * - * All available variables are mirrored in page.html.twig. + * All of the available variables are mirrored in html.html.twig. * Some may be blank but they are provided for consistency. * * @see template_preprocess_maintenance_page() @@ -11,6 +11,16 @@ * @ingroup themeable */ #} + + + + {{ head }} + {{ head_title }} + {{ styles }} + {{ scripts }} + + +
@@ -43,7 +53,7 @@ {{ messages }} - {{ page.content }} + {{ content }} {% if page.sidebar_first %} @@ -65,3 +75,6 @@ {% endif %}
{# /.l-container #} + + + diff --git a/core/themes/bartik/bartik.theme b/core/themes/bartik/bartik.theme index 414f5af5404e270ee4ce9464cd147c04138fc04f..6b99f45d12bfc78e47f758eaa151cf1473d2947c 100644 --- a/core/themes/bartik/bartik.theme +++ b/core/themes/bartik/bartik.theme @@ -5,6 +5,7 @@ * Functions to support theming in the Bartik theme. */ +use Drupal\Core\Template\RenderWrapper; use Drupal\Core\Template\Attribute; /** @@ -94,6 +95,7 @@ function bartik_preprocess_maintenance_page(&$variables) { if (!$variables['db_is_active']) { $variables['site_name'] = ''; } + $variables['styles'] = new RenderWrapper('drupal_get_css'); // Normally we could attach libraries via hook_page_alter(), but when the // database is inactive it's not called so we add them here. $libraries = array( @@ -103,7 +105,6 @@ function bartik_preprocess_maintenance_page(&$variables) { ), ), ); - drupal_render($libraries); // Set the options that apply to both page and maintenance page. _bartik_process_page($variables); diff --git a/core/themes/bartik/templates/maintenance-page.html.twig b/core/themes/bartik/templates/maintenance-page.html.twig index 5de50ccc0647f058f3ae700d7d92305e991660e4..45cd12c71020d15efc27258efb759e0c445d1a75 100644 --- a/core/themes/bartik/templates/maintenance-page.html.twig +++ b/core/themes/bartik/templates/maintenance-page.html.twig @@ -3,11 +3,25 @@ * @file * Bartik's theme implementation to display a single Drupal page while offline. * - * All available variables are mirrored in page.html.twig. + * All of the available variables are mirrored in html.html.twig. * * @see template_preprocess_maintenance_page() */ #} + + + + {{ head }} + {{ head_title }} + {{ styles }} + {{ scripts }} + + + + +
+ + + diff --git a/core/themes/seven/seven.theme b/core/themes/seven/seven.theme index 4aa6e5af085ef9bafda97b78b85ccf1a5c6409e5..c0a5a086817eded0996e2b55dabe98793fb0c475 100644 --- a/core/themes/seven/seven.theme +++ b/core/themes/seven/seven.theme @@ -5,6 +5,7 @@ * Functions to support theming in the Seven theme. */ +use Drupal\Core\Template\RenderWrapper; use Drupal\Component\Utility\String; /** @@ -268,11 +269,8 @@ function seven_element_info_alter(&$type) { * Implements hook_preprocess_install_page(). */ function seven_preprocess_install_page(&$variables) { - $page_object = $variables['page']['#page']; - $attributes = $page_object->getHtmlAttributes(); - $classes = $attributes['class']; - $classes[] = 'install-background'; - $attributes['class'] = $classes; + $variables['styles'] = new RenderWrapper('drupal_get_css'); + $variables['scripts'] = new RenderWrapper('drupal_get_js'); // Normally we could attach libraries via hook_page_alter(), but when the // database is inactive it's not called so we add them here. @@ -291,12 +289,8 @@ function seven_preprocess_install_page(&$variables) { * Implements hook_preprocess_maintenance_page(). */ function seven_preprocess_maintenance_page(&$variables) { - $page_object = $variables['page']['#page']; - $attributes = $page_object->getHtmlAttributes(); - $classes = $attributes['class']; - $classes[] = 'maintenance-background'; - $attributes['class'] = $classes; - + $variables['styles'] = new RenderWrapper('drupal_get_css'); + $variables['scripts'] = new RenderWrapper('drupal_get_js'); // // Normally we could attach libraries via hook_page_alter(), but when the // // database is inactive it's not called so we add them here. $libraries = array( diff --git a/core/themes/seven/templates/install-page.html.twig b/core/themes/seven/templates/install-page.html.twig index f86486edd05741ef544e89fec0e0b1dd9c182237..40f99045c5de2c24b40138766bfacf6e1d213311 100644 --- a/core/themes/seven/templates/install-page.html.twig +++ b/core/themes/seven/templates/install-page.html.twig @@ -3,12 +3,23 @@ * @file * Seven theme implementation to display a Drupal installation page. * - * All available variables are mirrored in page.html.twig. + * All the available variables are mirrored in html.html.twig and + * page.html.twig. * Some may be blank but they are provided for consistency. * * @see template_preprocess_install_page() */ #} + + + + {{ head }} + {{ head_title }} + {{ styles }} + {{ scripts }} + + +
@@ -24,9 +35,9 @@ {% endif %}
- {% if page.sidebar_first %} + {% if sidebar_first %} {# /.l-sidebar-first #} {% endif %} @@ -35,19 +46,22 @@

{{ title }}

{% endif %} {{ messages }} - {{ page.content }} + {{ content }} - {% if page.sidebar_second %} + {% if sidebar_second %} {# /.l-sidebar-second #} {% endif %} - {% if page.page_bottom %} + {% if footer %}
- {{ page.page_bottom }} + {{ footer }}
{% endif %}
{# /.l-container #} + + + diff --git a/core/themes/seven/templates/maintenance-page.html.twig b/core/themes/seven/templates/maintenance-page.html.twig index 9097297476a2cc79db5b7235c9debda5963a37f8..cec60b5b3189b7e933240cc4c3b75789ead23682 100644 --- a/core/themes/seven/templates/maintenance-page.html.twig +++ b/core/themes/seven/templates/maintenance-page.html.twig @@ -3,14 +3,26 @@ * @file * Seven's theme implementation to display a single Drupal page while offline. * - * All available variables are mirrored in page.html.twig. - * Some may be blank but they are provided for consistency. + * All of the available variables are mirrored in html.html.twig. * * @see template_preprocess_maintenance_page() + * @see seven_preprocess_maintenance_page() */ #} + + + + {{ head }} + {{ head_title }} + {{ styles }} + {{ scripts }} + + +
+ {{ page_top }} +
{% if site_name or site_slogan %}
@@ -24,9 +36,9 @@ {% endif %}
- {% if page.sidebar_first %} + {% if sidebar_first %} {# /.l-sidebar-first #} {% endif %} @@ -35,13 +47,16 @@

{{ title }}

{% endif %} {{ messages }} - {{ page.content }} + {{ content }} - {% if page.page_bottom %} + {% if footer %} {% endif %}
{# /.l-container #} + + + diff --git a/core/themes/seven/templates/page.html.twig b/core/themes/seven/templates/page.html.twig index bbc804f2def9f9bf48576dcafe2156a47b7cddb1..18b0bc18b6ca5ea054416e22288fb7ffed0b90b8 100644 --- a/core/themes/seven/templates/page.html.twig +++ b/core/themes/seven/templates/page.html.twig @@ -50,13 +50,13 @@ * comment/reply/12345). * * Regions: - * - page.page_top: Items for the header region. + * - page.header: Items for the header region. * - page.highlighted: Items for the highlighted content region. * - page.help: Dynamic help text, mostly for admin pages. * - page.content: The main content of the current page. * - page.sidebar_first: Items for the first sidebar. * - page.sidebar_second: Items for the second sidebar. - * - page.page_bottom: Items for the footer region. + * - page.footer: Items for the footer region. * * @see template_preprocess_page() * @see seven_preprocess_page() diff --git a/core/update.php b/core/update.php index 2c81857bb7e60de522b866e3f9e7b3f56c807c74..45904a20b7632e6ffb43f45de688ea4ccdf744ec 100644 --- a/core/update.php +++ b/core/update.php @@ -16,7 +16,6 @@ use Drupal\Component\Utility\Settings; use Drupal\Core\DrupalKernel; -use Drupal\Core\Page\DefaultHtmlPageRenderer; use Drupal\Core\Update\Form\UpdateScriptSelectionForm; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -460,8 +459,18 @@ function update_task_list($active = NULL) { } else { drupal_add_http_header('Content-Type', 'text/html; charset=utf-8'); - print DefaultHtmlPageRenderer::renderPage($output, $output['#title'], 'maintenance', array( + $maintenance_page = array( + '#theme' => 'maintenance_page', + // $output has to be rendered here, because the maintenance page template + // is not wrapped into the html template, which means that any #attached + // libraries in $output will not be loaded, because the wrapping HTML has + // been printed already. + '#content' => drupal_render($output), '#show_messages' => !$progress_page, - )); + ); + if (isset($output['#title'])) { + $maintenance_page['#page']['#title'] = $output['#title']; + } + print drupal_render($maintenance_page); } }