core/authorize.php | 3 +- core/core.services.yml | 56 ++-- core/includes/batch.inc | 3 +- core/includes/common.inc | 141 ---------- core/includes/errors.inc | 3 +- core/includes/install.core.inc | 3 +- core/includes/menu.inc | 10 +- core/includes/theme.inc | 104 +++---- .../Drupal/Core/Controller/DialogController.php | 157 ----------- .../Drupal/Core/Controller/HtmlControllerBase.php | 82 ------ .../Drupal/Core/Controller/HtmlPageController.php | 81 ------ core/lib/Drupal/Core/CoreServiceProvider.php | 3 + .../Core/Display/Annotation/DisplayVariant.php | 5 +- .../ContentControllerSubscriber.php | 37 ++- .../ContentFormControllerSubscriber.php | 4 +- .../EventSubscriber/DefaultExceptionSubscriber.php | 73 +---- .../Core/EventSubscriber/HtmlViewSubscriber.php | 110 -------- .../EventSubscriber/MainContentViewSubscriber.php | 107 ++++++++ .../EventSubscriber/MaintenanceModeSubscriber.php | 21 +- .../Drupal/Core/EventSubscriber/ViewSubscriber.php | 40 +-- .../Core/Page/DefaultHtmlFragmentRenderer.php | 155 ----------- .../Drupal/Core/Page/DefaultHtmlPageRenderer.php | 125 --------- core/lib/Drupal/Core/Page/FeedLinkElement.php | 32 --- core/lib/Drupal/Core/Page/HeadElement.php | 100 ------- core/lib/Drupal/Core/Page/HtmlFragment.php | 229 ---------------- .../lib/Drupal/Core/Page/HtmlFragmentInterface.php | 66 ----- .../Core/Page/HtmlFragmentRendererInterface.php | 38 --- core/lib/Drupal/Core/Page/HtmlPage.php | 234 ---------------- .../Drupal/Core/Page/HtmlPageRendererInterface.php | 29 -- core/lib/Drupal/Core/Page/LinkElement.php | 40 --- core/lib/Drupal/Core/Page/MetaElement.php | 81 ------ core/lib/Drupal/Core/Page/RenderHtmlRenderer.php | 88 ------ .../Core/Page/RenderHtmlRendererInterface.php | 31 --- .../Drupal/Core/Render/BareHtmlPageRenderer.php | 80 ++++++ .../Core/Render/BareHtmlPageRendererInterface.php | 79 ++++++ core/lib/Drupal/Core/Render/Element/Html.php | 2 +- core/lib/Drupal/Core/Render/Element/Page.php | 4 +- .../MainContent/AjaxRenderer.php} | 85 +----- .../Core/Render/MainContent/DialogRenderer.php | 92 +++++++ .../Core/Render/MainContent/HtmlRenderer.php | 304 +++++++++++++++++++++ .../MainContent/MainContentRendererInterface.php | 38 +++ .../MainContent/MainContentRenderersPass.php | 33 +++ .../Core/Render/MainContent/ModalRenderer.php | 42 +++ .../Render/PageDisplayVariantSelectionEvent.php | 77 ++++++ .../Plugin/DisplayVariant/SimplePageVariant.php | 47 ++++ core/lib/Drupal/Core/Render/RenderEvents.php | 22 ++ .../src/Authentication/Provider/BasicAuth.php | 2 +- core/modules/block/block.module | 36 +-- core/modules/block/block.services.yml | 4 + .../block/src/Controller/BlockController.php | 34 ++- .../BlockPageDisplayVariantSubscriber.php | 40 +++ .../{FullPageVariant.php => BlockPageVariant.php} | 12 +- ...ageVariantTest.php => BlockPageVariantTest.php} | 10 +- .../modules/node/src/Tests/Views/FrontPageTest.php | 2 +- core/modules/rest/src/Tests/ReadTest.php | 8 +- core/modules/rest/src/Tests/ResourceTest.php | 12 +- core/modules/simpletest/simpletest.module | 1 - .../system/src/Controller/BatchController.php | 92 +------ .../system/src/Controller/DbUpdateController.php | 19 +- .../system/src/Tests/Common/AddFeedTest.php | 18 +- .../system/src/Tests/Common/PageRenderTest.php | 9 +- .../src/Tests/System/MainContentFallbackTest.php | 21 +- core/modules/system/system.module | 80 ++++++ core/modules/system/system.routing.yml | 4 +- core/modules/system/templates/html.html.twig | 10 +- .../ajax_test/src/Form/AjaxTestDialogForm.php | 2 +- .../modules/ajax_test/src/Form/AjaxTestForm.php | 2 +- .../src/EventSubscriber/HtmlPageSubscriber.php | 45 --- .../system_module_test/system_module_test.module | 13 + .../system_module_test.services.yml | 5 - .../tests/modules/system_test/system_test.module | 13 - core/modules/system/theme.api.php | 74 +++++ core/modules/views/views.module | 24 +- ...AjaxControllerTest.php => AjaxRendererTest.php} | 58 +--- core/tests/Drupal/Tests/Core/Page/HtmlPageTest.php | 62 ----- core/themes/bartik/bartik.libraries.yml | 1 + core/themes/bartik/bartik.theme | 34 ++- core/themes/seven/css/theme/install-page.css | 2 +- core/themes/seven/css/theme/maintenance-page.css | 2 +- core/themes/seven/seven.theme | 55 ++-- 80 files changed, 1466 insertions(+), 2536 deletions(-) diff --git a/core/authorize.php b/core/authorize.php index 4b0f249..22bede6 100644 --- a/core/authorize.php +++ b/core/authorize.php @@ -25,7 +25,6 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Drupal\Core\Site\Settings; -use Drupal\Core\Page\DefaultHtmlPageRenderer; // Change the directory to the Drupal root. chdir('..'); @@ -150,7 +149,7 @@ function authorize_access_allowed() { if (!empty($output)) { $response->headers->set('Content-Type', 'text/html; charset=utf-8'); - $response->setContent(DefaultHtmlPageRenderer::renderPage($output, $page_title, 'maintenance', array( + $response->setContent(\Drupal::service('bare_html_page_renderer')->renderMaintenancePage($output, $page_title, array( '#show_messages' => $show_messages, ))); $response->send(); diff --git a/core/core.services.yml b/core/core.services.yml index 9c3f606..72af90f 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -650,15 +650,34 @@ services: class: Drupal\Core\EventSubscriber\RouteMethodSubscriber tags: - { name: event_subscriber } - controller.page: - class: Drupal\Core\Controller\HtmlPageController - arguments: ['@controller_resolver', '@title_resolver', '@render_html_renderer'] - controller.ajax: - class: Drupal\Core\Controller\AjaxController - arguments: ['@controller_resolver', '@element_info'] - controller.dialog: - class: Drupal\Core\Controller\DialogController - arguments: ['@controller_resolver', '@title_resolver'] + + # Main content view subscriber plus the renderers it uses. + main_content_view_subscriber: + class: Drupal\Core\EventSubscriber\MainContentViewSubscriber + arguments: ['@class_resolver', '@current_route_match', '%main_content_renderers%'] + tags: + - { name: event_subscriber } + main_content_renderer.html: + class: Drupal\Core\Render\MainContent\HtmlRenderer + arguments: ['@title_resolver', '@plugin.manager.display_variant', '@event_dispatcher', '@module_handler'] + tags: + - { name: render.main_content_renderer, format: html } + main_content_renderer.ajax: + class: Drupal\Core\Render\MainContent\AjaxRenderer + arguments: ['@element_info'] + tags: + - { name: render.main_content_renderer, format: drupal_ajax } + main_content_renderer.dialog: + class: Drupal\Core\Render\MainContent\DialogRenderer + arguments: ['@title_resolver'] + tags: + - { name: render.main_content_renderer, format: drupal_dialog } + main_content_renderer.modal: + class: Drupal\Core\Render\MainContent\ModalRenderer + arguments: ['@title_resolver'] + tags: + - { name: render.main_content_renderer, format: drupal_modal } + router_listener: class: Symfony\Component\HttpKernel\EventListener\RouterListener tags: @@ -671,19 +690,8 @@ services: tags: - { name: event_subscriber } arguments: ['@content_negotiation', '@title_resolver'] - html_view_subscriber: - class: Drupal\Core\EventSubscriber\HtmlViewSubscriber - tags: - - { name: event_subscriber } - arguments: ['@html_fragment_renderer', '@html_page_renderer'] - render_html_renderer: - class: Drupal\Core\Page\RenderHtmlRenderer - arguments: ['@url_generator'] - html_fragment_renderer: - class: Drupal\Core\Page\DefaultHtmlFragmentRenderer - arguments: ['@language_manager'] - html_page_renderer: - class: Drupal\Core\Page\DefaultHtmlPageRenderer + bare_html_page_renderer: + class: Drupal\Core\Render\BareHtmlPageRenderer private_key: class: Drupal\Core\PrivateKey arguments: ['@state'] @@ -732,7 +740,7 @@ services: arguments: ['@state', '@current_user'] maintenance_mode_subscriber: class: Drupal\Core\EventSubscriber\MaintenanceModeSubscriber - arguments: ['@maintenance_mode', '@config.factory', '@string_translation', '@url_generator', '@current_user'] + arguments: ['@maintenance_mode', '@config.factory', '@string_translation', '@url_generator', '@current_user', '@bare_html_page_renderer'] tags: - { name: event_subscriber } path_subscriber: @@ -777,7 +785,7 @@ services: class: Drupal\Core\EventSubscriber\DefaultExceptionSubscriber tags: - { name: event_subscriber } - arguments: ['@html_fragment_renderer', '@html_page_renderer', '@config.factory'] + arguments: ['@config.factory', '@bare_html_page_renderer'] exception.logger: class: Drupal\Core\EventSubscriber\ExceptionLoggingSubscriber tags: diff --git a/core/includes/batch.inc b/core/includes/batch.inc index 9e693f5..4f527b3 100644 --- a/core/includes/batch.inc +++ b/core/includes/batch.inc @@ -19,7 +19,6 @@ use Drupal\Component\Utility\UrlHelper; use Drupal\Core\Batch\Percentage; use Drupal\Core\Form\FormState; -use Drupal\Core\Page\DefaultHtmlPageRenderer; use Drupal\Core\Url; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -136,7 +135,7 @@ function _batch_progress_page() { // 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( + $fallback = \Drupal::service('bare_html_page_renderer')->renderMaintenancePage($fallback, $current_set['title'], array( '#show_messages' => FALSE, )); list($fallback) = explode('', $fallback); diff --git a/core/includes/common.inc b/core/includes/common.inc index 4163da2..001c9e7 100644 --- a/core/includes/common.inc +++ b/core/includes/common.inc @@ -1117,9 +1117,6 @@ function drupal_get_css($css = NULL, $skip_alter = FALSE, $theme_add_css = TRUE) '#type' => 'styles', '#items' => $css, ); - if (!empty($setting)) { - $styles['#attached']['js'][] = array('type' => 'setting', 'data' => $setting); - } return drupal_render($styles); } @@ -2262,39 +2259,6 @@ function drupal_page_set_cache(Response $response, Request $request) { } /** - * Sets the main page content value for later use. - * - * Given the nature of the Drupal page handling, this will be called once with - * a string or array. We store that and return it later as the block is being - * displayed. - * - * @param $content - * A string or renderable array representing the body of the page. - * - * @return - * If called without $content, a renderable array representing the body of - * the page. - */ -function drupal_set_page_content($content = NULL) { - $content_block = &drupal_static(__FUNCTION__, NULL); - $main_content_display = &drupal_static('system_main_content_added', FALSE); - - // Filter out each empty value, though allow '0' and 0, which would be - // filtered out by empty(). - if ($content !== NULL && $content !== '') { - $content_block = (is_array($content) ? $content : array('main' => array('#markup' => $content))); - } - else { - // Indicate that the main content has been requested. We assume that - // the module requesting the content will be adding it to the page. - // A module can indicate that it does not handle the content by setting - // the static variable back to FALSE after calling this function. - $main_content_display = TRUE; - return $content_block; - } -} - -/** * Pre-render callback: Renders a link into #markup. * * @deprecated Use \Drupal\Core\Render\Element\Link::preRenderLink(). @@ -2399,111 +2363,6 @@ function drupal_pre_render_links($element) { } /** - * Processes the page render array, enhancing it as necessary. - * - * @param $page - * A string or array representing the content of a page. The array consists of - * the following keys: - * - #type: Value is always 'page'. This pushes the theming through - * the page template (required). - * - #show_messages: Suppress drupal_get_message() items. Used by Batch - * API (optional). - * - * @return array - * The processed render array for the page. - * - * @see hook_page_attachments() - * @see hook_page_attachments_alter() - * @see hook_page_top() - * @see hook_page_bottom() - * @see element_info() - */ -function drupal_prepare_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. - if (is_string($page) || (is_array($page) && (!isset($page['#type']) || ($page['#type'] != 'page')))) { - drupal_set_page_content($page); - $page = element_info('page'); - } - - // Modules can add attachments. - $attachments = []; - foreach (\Drupal::moduleHandler()->getImplementations('page_attachments') as $module) { - $function = $module . '_page_attachments'; - $function($attachments); - } - if (array_diff(array_keys($attachments), ['#attached', '#post_render_cache']) !== []) { - throw new \LogicException('Only #attached and #post_render_cache may be set in hook_page_attachments().'); - } - // Modules and themes can alter page attachments. - \Drupal::moduleHandler()->alter('page_attachments', $attachments); - \Drupal::theme()->alter('page_attachments', $attachments); - if (array_diff(array_keys($attachments), ['#attached', '#post_render_cache']) !== []) { - throw new \LogicException('Only #attached and #post_render_cache may be set in hook_page_attachments_alter().'); - } - if (isset($attachments['#attached'])) { - $page['#attached'] = $attachments['#attached']; - } - if (isset($attachments['#post_render_cache'])) { - $page['#post_render_cache'] = $attachments['#post_render_cache']; - } - - // Modules can add renderable arrays to the top and bottom of the page. - $pseudo_page_top = []; - $pseudo_page_bottom = []; - foreach (\Drupal::moduleHandler()->getImplementations('page_top') as $module) { - $function = $module . '_page_top'; - $function($pseudo_page_top); - } - foreach (\Drupal::moduleHandler()->getImplementations('page_bottom') as $module) { - $function = $module . '_page_bottom'; - $function($pseudo_page_bottom); - } - if (!empty($pseudo_page_top)) { - $page['page_top'] = $pseudo_page_top; - } - if (!empty($pseudo_page_bottom)) { - $page['page_bottom'] = $pseudo_page_bottom; - } - - // @todo Clean this up as part of https://www.drupal.org/node/2352155. - if (\Drupal::moduleHandler()->moduleExists('block')) { - _block_page_build($page); - // Find all non-empty page regions, and add a theme wrapper function that - // allows them to be consistently themed. - $regions = system_region_list(\Drupal::theme()->getActiveTheme()->getName()); - foreach (array_keys($regions) as $region) { - if (!empty($page[$region])) { - $page[$region]['#theme_wrappers'][] = 'region'; - $page[$region]['#region'] = $region; - } - } - } - - // If no module has taken care of the main content, add it to the page now. - // This allows the site to still be usable even if no modules that - // control page regions (for example, the Block module) are enabled. - if (!$main_content_display) { - $page['content']['system_main'] = drupal_set_page_content(); - } - - // Set back the previously stored title. - if (isset($title)) { - $page['#title'] = $title; - } - - return $page; -} - -/** * Renders final HTML given a structured array tree. * * Calls drupal_render() in such a way that #post_render_cache callbacks are diff --git a/core/includes/errors.inc b/core/includes/errors.inc index 4065408..be30937 100644 --- a/core/includes/errors.inc +++ b/core/includes/errors.inc @@ -8,7 +8,6 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Component\Utility\Xss; use Drupal\Core\Logger\RfcLogLevel; -use Drupal\Core\Page\DefaultHtmlPageRenderer; use Drupal\Core\Utility\Error; use Symfony\Component\HttpFoundation\Response; @@ -236,7 +235,7 @@ function _drupal_log_error($error, $fatal = FALSE) { install_display_output($output, $GLOBALS['install_state']); } else { - $output = DefaultHtmlPageRenderer::renderPage($message, 'Error'); + $output = \Drupal::service('bare_html_page_renderer')->renderMaintenancePage($message, 'Error'); } $response = new Response($output, 500); diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index d6e40d1..2adc576 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -14,7 +14,6 @@ use Drupal\Core\Installer\InstallerKernel; use Drupal\Core\Language\Language; use Drupal\Core\Language\LanguageManager; -use Drupal\Core\Page\DefaultHtmlPageRenderer; use Drupal\Core\Site\Settings; use Drupal\Core\StringTranslation\Translator\FileTranslation; use Drupal\Core\Extension\ExtensionDiscovery; @@ -934,7 +933,7 @@ function install_display_output($output, $install_state) { 'ETag' => '"' . REQUEST_TIME . '"', ); $response->headers->add($default_headers); - $response->setContent(DefaultHtmlPageRenderer::renderPage($output, $output['#title'], 'install', $regions)); + $response->setContent(\Drupal::service('bare_html_page_renderer')->renderInstallPage($output, $output['#title'], $regions)); $response->send(); exit; } diff --git a/core/includes/menu.inc b/core/includes/menu.inc index 76aafd0..b42ff1c 100644 --- a/core/includes/menu.inc +++ b/core/includes/menu.inc @@ -110,13 +110,15 @@ * class and method. Page controller classes do not necessarily need to * implement any particular interface or extend any particular base class. The * only requirement is that the method specified in your *.routing.yml file - * return one of the following, depending on whether you specified _content or - * _controller in the routing file defaults section: + * returns: * - A render array (see the * @link theme_render Theme and render topic @endlink for more information), * if _content is used in the routing file. - * - A \Drupal\Core\Page\HtmlFragmentInterface object (fragment or page), if - * _content is used in the routing file. + * This render array is then rendered in the requested format (HTML, dialog, + * modal, AJAX are supported by default). In the case of HTML, it will be + * surrounded by blocks by default: the Block module is enabled by default, + * and hence its Page Display Variant that surrounds the main content with + * blocks is also used by default. * - A \Symfony\Component\HttpFoundation\Response object, if _controller is * used in the routing file. * As a note, if your module registers multiple simple routes, it is usual diff --git a/core/includes/theme.inc b/core/includes/theme.inc index e772289..493ceab 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -18,8 +18,6 @@ use Drupal\Core\Config\StorageException; use Drupal\Core\Extension\Extension; use Drupal\Core\Extension\ExtensionNameLengthException; -use Drupal\Core\Page\LinkElement; -use Drupal\Core\Page\MetaElement; use Drupal\Core\Template\Attribute; use Drupal\Core\Theme\ThemeSettings; use Drupal\Component\Utility\NestedArray; @@ -1646,22 +1644,37 @@ function drupal_pre_render_html(array $element) { * - page: A render element representing the page. */ function template_preprocess_html(&$variables) { - /** @var $page \Drupal\Core\Page\HtmlPage */ - $page = $variables['page_object']; + $variables['page'] = $variables['html']['page']; + unset($variables['html']['page']); + $variables['page_top'] = NULL; + if (isset($variables['html']['page_top'])) { + $variables['page_top'] = $variables['html']['page_top']; + unset($variables['html']['page_top']); + } + $variables['page_bottom'] = NULL; + if (isset($variables['html']['page_bottom'])) { + $variables['page_bottom'] = $variables['html']['page_bottom']; + unset($variables['html']['page_bottom']); + } - $variables['html_attributes'] = $page->getHtmlAttributes(); - $variables['attributes'] = $page->getBodyAttributes(); - $variables['page'] = $page; + $variables['html_attributes'] = new Attribute(); + + // HTML element attributes. + $language_interface = \Drupal::languageManager()->getCurrentLanguage(); + $variables['html_attributes']['lang'] = $language_interface->getId(); + $variables['html_attributes']['dir'] = $language_interface->getDirection(); // Compile a list of classes that are going to be applied to the body element. // This allows advanced theming based on context (home page, node of certain // type, etc.). - $body_classes = $variables['attributes']['class']; + if (isset($variables['db_is_active']) && !$variables['db_is_active']) { + $variables['attributes']['class'][] = 'db-offline'; + } // Add a class that tells us whether the page is viewed by an authenticated // user. if ($variables['logged_in']) { - $body_classes[] = 'user-logged-in'; + $variables['attributes']['class'][] = 'user-logged-in'; } // Add a class that tells us what path the page is located make it possible // to theme the page depending on the current path (e.g. node, admin, user, @@ -1669,20 +1682,18 @@ function template_preprocess_html(&$variables) { $path = \Drupal::request()->getPathInfo(); if (drupal_is_front_page()) { - $body_classes[] = 'path-frontpage'; + $variables['attributes']['class'][] = 'path-frontpage'; } else { $segment = explode('/', $path); - $body_classes[] = 'path-' . drupal_html_class($segment[1]); + $variables['attributes']['class'][] = 'path-' . drupal_html_class($segment[1]); } - $variables['attributes']['class'] = $body_classes; - $site_config = \Drupal::config('system.site'); // Construct page title. - if ($page->hasTitle()) { + if (!empty($variables['page']['#title'])) { $head_title = array( - 'title' => SafeMarkup::set(trim(strip_tags($page->getTitle()))), + 'title' => SafeMarkup::set(trim(strip_tags($variables['page']['#title']))), 'name' => String::checkPlain($site_config->get('name')), ); } @@ -1710,35 +1721,27 @@ function template_preprocess_html(&$variables) { } $variables['head_title'] = SafeMarkup::set($output); - // @todo Remove drupal_*_html_head() and refactor accordingly. - $html_heads = drupal_get_html_head(FALSE); - uasort($html_heads, 'Drupal\Component\Utility\SortArray::sortByWeightElement'); - foreach ($html_heads as $name => $tag) { - if ($tag['#tag'] == 'link') { - $link = new LinkElement($name, isset($tag['#attributes']['content']) ? $tag['#attributes']['content'] : NULL, $tag['#attributes']); - if (!empty($tag['#noscript'])) { - $link->setNoScript(); - } - $page->addLinkElement($link); - } - elseif ($tag['#tag'] == 'meta') { - $metatag = new MetaElement(NULL, $tag['#attributes']); - if (!empty($tag['#noscript'])) { - $metatag->setNoScript(); - } - $page->addMetaElement($metatag); - } + // Collect all attachments. This must happen in the preprocess function for + // #type => html, to ensure that attachments added in #pre_render callbacks + // for #type => html are included. + $attached = $variables['html']['#attached']; + $attached = drupal_merge_attached($attached, $variables['page']['#attached']); + if (isset($variables['page_top'])) { + $attached = drupal_merge_attached($attached, $variables['page_top']['#attached']); } - - // Add favicon. - if (theme_get_setting('features.favicon')) { - $url = UrlHelper::stripDangerousProtocols(theme_get_setting('favicon.url')); - $link = new LinkElement($url, 'shortcut icon', ['type' => theme_get_setting('favicon.mimetype')]); - $page->addLinkElement($link); + if (isset($variables['page_bottom'])) { + $attached = drupal_merge_attached($attached, $variables['page_bottom']['#attached']); } - $variables['page_top'][] = array('#markup' => $page->getBodyTop()); - $variables['page_bottom'][] = array('#markup' => $page->getBodyBottom()); + // Render the attachments into HTML markup to be used directly in the template + // for #type => html: html.html.twig. + $all_attached = ['#attached' => $attached]; + drupal_process_attached($all_attached); + + $variables['styles'] = drupal_get_css(); + $variables['scripts'] = drupal_get_js(); + $variables['scripts_bottom'] = drupal_get_js('footer'); + $variables['head'] = drupal_get_html_head(FALSE); } /** @@ -1749,8 +1752,6 @@ function template_preprocess_html(&$variables) { * Most themes use their own copy of page.html.twig. The default is located * inside "modules/system/page.html.twig". Look in there for the full list of * variables. - * - * @see DefaultHtmlFragmentRenderer::render() */ function template_preprocess_page(&$variables) { $language_interface = \Drupal::languageManager()->getCurrentLanguage(); @@ -1890,15 +1891,6 @@ 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'; - if (isset($variables['db_is_active']) && !$variables['db_is_active']) { - $classes[] = 'db-offline'; - } - $attributes['class'] = $classes; - // @see system_page_attachments() $variables['#attached']['library'][] = 'core/normalize'; $variables['#attached']['library'][] = 'system/maintenance'; @@ -1918,12 +1910,6 @@ function template_preprocess_maintenance_page(&$variables) { 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; - // 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()); @@ -2161,7 +2147,7 @@ function drupal_common_theme() { return array( // From theme.inc. 'html' => array( - 'variables' => array('page_object' => NULL), + 'render element' => 'html', ), 'page' => array( 'render element' => 'page', diff --git a/core/lib/Drupal/Core/Controller/DialogController.php b/core/lib/Drupal/Core/Controller/DialogController.php deleted file mode 100644 index 6aef7fa..0000000 --- a/core/lib/Drupal/Core/Controller/DialogController.php +++ /dev/null @@ -1,157 +0,0 @@ -controllerResolver = $controller_resolver; - $this->titleResolver = $title_resolver; - } - - /** - * Displays content in a modal dialog. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * The request object. - * @param \Drupal\Core\Routing\RouteMatchInterface $route_match - * The route match. - * @param mixed $_content - * A controller definition string, or a callable object/closure. - * - * @return \Drupal\Core\Ajax\AjaxResponse - * AjaxResponse to return the content wrapper in a modal dialog. - */ - public function modal(Request $request, RouteMatchInterface $route_match, $_content) { - return $this->dialog($request, $route_match, $_content, TRUE); - } - - /** - * Displays content in a dialog. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * The request object. - * @param \Drupal\Core\Routing\RouteMatchInterface $route_match - * The route match. - * @param mixed $_content - * A controller definition string, or a callable object/closure. - * @param bool $modal - * (optional) TRUE to render a modal dialog. Defaults to FALSE. - * - * @return \Drupal\Core\Ajax\AjaxResponse - * AjaxResponse to return the content wrapper in a dialog. - */ - public function dialog(Request $request, RouteMatchInterface $route_match, $_content, $modal = FALSE) { - $page_content = $this->getContentResult($request, $_content); - - // Allow controllers to return a HtmlPage or a Response object directly. - if ($page_content instanceof HtmlPage) { - $page_content = $page_content->getContent(); - } - if ($page_content instanceof Response) { - $page_content = $page_content->getContent(); - } - - // Most controllers return a render array, but some return a string. - if (!is_array($page_content)) { - $page_content = array( - '#markup' => $page_content, - ); - } - - $content = drupal_render_root($page_content); - drupal_process_attached($page_content); - $title = isset($page_content['#title']) ? $page_content['#title'] : $this->titleResolver->getTitle($request, $route_match->getRouteObject()); - $response = new AjaxResponse(); - // Fetch any modal options passed in from data-dialog-options. - $options = $request->request->get('dialogOptions', array()); - // Set modal flag and re-use the modal ID. - if ($modal) { - $options['modal'] = TRUE; - $target = '#drupal-modal'; - } - else { - // Generate the target wrapper for the dialog. - if (isset($options['target'])) { - // If the target was nominated in the incoming options, use that. - $target = $options['target']; - // Ensure the target includes the #. - if (substr($target, 0, 1) != '#') { - $target = '#' . $target; - } - // This shouldn't be passed on to jQuery.ui.dialog. - unset($options['target']); - } - else { - // Generate a target based on the route id. - $route_name = $route_match->getRouteName(); - $target = '#' . drupal_html_id("drupal-dialog-$route_name"); - } - } - $response->addCommand(new OpenDialogCommand($target, $title, $content, $options)); - return $response; - } - - /** - * Returns the result of invoking the sub-controller. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * The request object. - * @param mixed $controller_definition - * A controller definition string, or a callable object/closure. - * - * @return mixed - * The result of invoking the controller. Render arrays, strings, HtmlPage, - * and HtmlFragment objects are possible. - */ - public function getContentResult(Request $request, $controller_definition) { - if ($controller_definition instanceof \Closure) { - $callable = $controller_definition; - } - else { - $callable = $this->controllerResolver->getControllerFromDefinition($controller_definition); - } - $arguments = $this->controllerResolver->getArguments($request, $callable); - $page_content = call_user_func_array($callable, $arguments); - - return $page_content; - } - -} diff --git a/core/lib/Drupal/Core/Controller/HtmlControllerBase.php b/core/lib/Drupal/Core/Controller/HtmlControllerBase.php deleted file mode 100644 index 88e0efe..0000000 --- a/core/lib/Drupal/Core/Controller/HtmlControllerBase.php +++ /dev/null @@ -1,82 +0,0 @@ -titleResolver = $title_resolver; - $this->renderHtmlRenderer = $render_html_renderer; - } - - /** - * Converts a render array into an HtmlFragment object. - * - * @param array|\Drupal\Core\Page\HtmlFragmentInterface|\Symfony\Component\HttpFoundation\Response $page_content - * The page content area to display. - * @param \Symfony\Component\HttpFoundation\Request $request - * The request object. - * - * @return \Drupal\Core\Page\HtmlPage - * A page object. - * - * @throws \InvalidArgumentException - * Thrown if the controller returns a string. - */ - protected function createHtmlFragment($page_content, Request $request) { - // Allow controllers to return a HtmlFragment or a Response object directly. - if ($page_content instanceof HtmlFragment || $page_content instanceof Response) { - return $page_content; - } - - if (is_string($page_content)) { - throw new \InvalidArgumentException('_content controllers are not allowed to return strings. You can return a render array, a html fragment or a response object.'); - } - - $fragment = $this->renderHtmlRenderer->render($page_content); - - if (!$fragment->getTitle() && $route = $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)) { - $fragment->setTitle($this->titleResolver->getTitle($request, $route), Title::PASS_THROUGH); - } - - return $fragment; - } - -} diff --git a/core/lib/Drupal/Core/Controller/HtmlPageController.php b/core/lib/Drupal/Core/Controller/HtmlPageController.php deleted file mode 100644 index efc11ac..0000000 --- a/core/lib/Drupal/Core/Controller/HtmlPageController.php +++ /dev/null @@ -1,81 +0,0 @@ -controllerResolver = $controller_resolver; - } - - /** - * Controller method for generic HTML pages. - * - * @param Request $request - * The request object. - * @param callable $_content - * The body content callable that contains the body region of this page. - * - * @return \Symfony\Component\HttpFoundation\Response - * A response object. - */ - public function content(Request $request, $_content) { - $page_content = $this->getContentResult($request, $_content); - return $this->createHtmlFragment($page_content, $request); - } - - /** - * Returns the result of invoking the sub-controller. - * - * @param \Symfony\Component\HttpFoundation\Request $request - * The request object. - * @param mixed $controller_definition - * A controller definition string, or a callable object/closure. - * - * @return array - * The render array that results from invoking the controller. - */ - public function getContentResult(Request $request, $controller_definition) { - if ($controller_definition instanceof \Closure) { - $callable = $controller_definition; - } - else { - $callable = $this->controllerResolver->getControllerFromDefinition($controller_definition); - } - $arguments = $this->controllerResolver->getArguments($request, $callable); - $page_content = call_user_func_array($callable, $arguments); - - return $page_content; - } - -} diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php index db54f91..3767c9f 100644 --- a/core/lib/Drupal/Core/CoreServiceProvider.php +++ b/core/lib/Drupal/Core/CoreServiceProvider.php @@ -20,6 +20,7 @@ use Drupal\Core\DependencyInjection\Compiler\RegisterAccessChecksPass; use Drupal\Core\DependencyInjection\Compiler\RegisterServicesForDestructionPass; use Drupal\Core\Plugin\PluginManagerPass; +use Drupal\Core\Render\MainContent\MainContentRenderersPass; use Symfony\Component\DependencyInjection\Compiler\PassConfig; /** @@ -54,6 +55,8 @@ public function register(ContainerBuilder $container) { $container->addCompilerPass(new StackedKernelPass()); + $container->addCompilerPass(new MainContentRenderersPass()); + // Collect tagged handler services as method calls on consumer services. $container->addCompilerPass(new TaggedHandlersPass()); $container->addCompilerPass(new RegisterStreamWrappersPass()); diff --git a/core/lib/Drupal/Core/Display/Annotation/DisplayVariant.php b/core/lib/Drupal/Core/Display/Annotation/DisplayVariant.php index 3c05cf3..3bf7492 100644 --- a/core/lib/Drupal/Core/Display/Annotation/DisplayVariant.php +++ b/core/lib/Drupal/Core/Display/Annotation/DisplayVariant.php @@ -25,8 +25,9 @@ * * Plugin namespace: Plugin\DisplayVariant * - * For a working example, see - * \Drupal\block\Plugin\DisplayVariant\FullPageVariant + * For working examples, see + * - \Drupal\Core\Render\Plugin\DisplayVariant\SimplePageVariant + * - \Drupal\block\Plugin\DisplayVariant\BlockPageVariant * * @see \Drupal\Core\Display\VariantInterface * @see \Drupal\Core\Display\VariantBase diff --git a/core/lib/Drupal/Core/EventSubscriber/ContentControllerSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ContentControllerSubscriber.php index 377ae93..0d928705 100644 --- a/core/lib/Drupal/Core/EventSubscriber/ContentControllerSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/ContentControllerSubscriber.php @@ -13,7 +13,13 @@ use Symfony\Component\HttpKernel\KernelEvents; /** - * Defines a subscriber for setting the format of the request. + * Defines a subscriber to negotiate a _controller to use for a _content route. + * + * @todo Remove this event subscriber after both + * https://www.drupal.org/node/2092647 and https://www.drupal.org/node/2331919 + * have landed. + * + * @see \Drupal\Core\EventSubscriber\MainContentViewSubscriber */ class ContentControllerSubscriber implements EventSubscriberInterface { @@ -35,20 +41,10 @@ public function __construct(ContentNegotiation $negotiation) { } /** - * Associative array of supported mime types and their appropriate controller. - * - * @var array - */ - protected $types = array( - 'drupal_dialog' => 'controller.dialog:dialog', - 'drupal_modal' => 'controller.dialog:modal', - 'html' => 'controller.page:content', - 'drupal_ajax' => 'controller.ajax:content', - ); - - /** * Sets the derived request format on the request. * + * @todo Remove when https://www.drupal.org/node/2331919 lands. + * * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event * The event to process. */ @@ -61,19 +57,20 @@ public function onRequestDeriveFormat(GetResponseEvent $event) { } /** - * Sets the _controller on a request based on the request format. + * Sets _content (if it exists) as the _controller. + * + * @todo Remove when https://www.drupal.org/node/2092647 lands. * * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event * The event to process. */ - public function onRequestDeriveContentWrapper(GetResponseEvent $event) { + public function onRequestDeriveController(GetResponseEvent $event) { $request = $event->getRequest(); $controller = $request->attributes->get('_controller'); - if (empty($controller) && ($type = $request->getRequestFormat())) { - if (isset($this->types[$type])) { - $request->attributes->set('_controller', $this->types[$type]); - } + $content = $request->attributes->get('_content'); + if (empty($controller) && !empty($content)) { + $request->attributes->set('_controller', $content); } } @@ -85,7 +82,7 @@ public function onRequestDeriveContentWrapper(GetResponseEvent $event) { */ static function getSubscribedEvents() { $events[KernelEvents::REQUEST][] = array('onRequestDeriveFormat', 31); - $events[KernelEvents::REQUEST][] = array('onRequestDeriveContentWrapper', 30); + $events[KernelEvents::REQUEST][] = array('onRequestDeriveController', 30); return $events; } diff --git a/core/lib/Drupal/Core/EventSubscriber/ContentFormControllerSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ContentFormControllerSubscriber.php index 43bdd8b..b1d8411 100644 --- a/core/lib/Drupal/Core/EventSubscriber/ContentFormControllerSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/ContentFormControllerSubscriber.php @@ -57,6 +57,8 @@ public function __construct(ClassResolverInterface $class_resolver, ControllerRe /** * Sets the _controller on a request based on the request format. * + * @todo Remove when https://www.drupal.org/node/2092647 lands. + * * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event * The event to process. */ @@ -65,7 +67,7 @@ public function onRequestDeriveFormWrapper(GetResponseEvent $event) { if ($form = $request->attributes->get('_form')) { $wrapper = new HtmlFormController($this->classResolver, $this->controllerResolver, $this->container, $form, $this->formBuilder); - $request->attributes->set('_content', array($wrapper, 'getContentResult')); + $request->attributes->set('_controller', array($wrapper, 'getContentResult')); } } diff --git a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php index 78a9376..b8d23c4 100644 --- a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php @@ -11,11 +11,7 @@ use Drupal\Component\Utility\String; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\ContentNegotiation; -use Drupal\Core\Form\EnforcedResponse; -use Drupal\Core\Page\DefaultHtmlPageRenderer; -use Drupal\Core\Page\HtmlFragment; -use Drupal\Core\Page\HtmlFragmentRendererInterface; -use Drupal\Core\Page\HtmlPageRendererInterface; +use Drupal\Core\Render\BareHtmlPageRendererInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\Utility\Error; use Symfony\Component\Debug\Exception\FlattenException; @@ -37,20 +33,6 @@ class DefaultExceptionSubscriber implements EventSubscriberInterface { use StringTranslationTrait; /** - * The fragment renderer. - * - * @var \Drupal\Core\Page\HtmlFragmentRendererInterface - */ - protected $fragmentRenderer; - - /** - * The page renderer. - * - * @var \Drupal\Core\Page\HtmlPageRendererInterface - */ - protected $htmlPageRenderer; - - /** * @var string * * One of the error level constants defined in bootstrap.inc. @@ -65,19 +47,23 @@ class DefaultExceptionSubscriber implements EventSubscriberInterface { protected $configFactory; /** + * The bare HTML page renderer. + * + * @var \Drupal\Core\Render\BareHtmlPageRendererInterface + */ + protected $bareHtmlPageRenderer; + + /** * Constructs a new DefaultExceptionHtmlSubscriber. * - * @param \Drupal\Core\Page\HtmlFragmentRendererInterface $fragment_renderer - * The fragment renderer. - * @param \Drupal\Core\Page\HtmlPageRendererInterface $page_renderer - * The page renderer. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The configuration factory. + * @param \Drupal\Core\Render\BareHtmlPageRendererInterface $bare_html_page_renderer + * The bare HTML page renderer. */ - public function __construct(HtmlFragmentRendererInterface $fragment_renderer, HtmlPageRendererInterface $page_renderer, ConfigFactoryInterface $config_factory) { - $this->fragmentRenderer = $fragment_renderer; - $this->htmlPageRenderer = $page_renderer; + public function __construct(ConfigFactoryInterface $config_factory, BareHtmlPageRendererInterface $bare_html_page_renderer) { $this->configFactory = $config_factory; + $this->bareHtmlPageRenderer = $bare_html_page_renderer; } /** @@ -146,7 +132,7 @@ protected function onHtml(GetResponseForExceptionEvent $event) { } $content = $this->t('The website has encountered an error. Please try again later.'); - $output = DefaultHtmlPageRenderer::renderPage($content, $this->t('Error')); + $output = $this->bareHtmlPageRenderer->renderMaintenancePage($content, $this->t('Error')); $response = new Response($output); if ($exception instanceof HttpExceptionInterface) { @@ -187,39 +173,6 @@ protected function onJson(GetResponseForExceptionEvent $event) { } /** - * Creates an Html response for the provided criteria. - * - * @param $title - * The page title of the response. - * @param $body - * The body of the error page. - * @param $response_code - * The HTTP response code of the response. - * @return \Symfony\Component\HttpFoundation\Response - * An error Response object ready to return to the browser. - */ - protected function createHtmlResponse($title, $body, $response_code) { - $fragment = new HtmlFragment($body); - $fragment->setTitle($title); - - // Normally the EnforcedFormResponseSubscriber takes care of the - // EnforcedResponseException. But outside of HttpKernel::handleRaw(), it is - // necessary to catch and handle it manually. - try { - $page = $this->fragmentRenderer->render($fragment, $response_code); - return new Response($this->htmlPageRenderer->render($page), $page->getStatusCode()); - } - catch (\Exception $e) { - if ($response = EnforcedResponse::createFromException($e)) { - return $response; - } - else { - throw $e; - } - } - } - - /** * Handles errors for this subscriber. * * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event diff --git a/core/lib/Drupal/Core/EventSubscriber/HtmlViewSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/HtmlViewSubscriber.php deleted file mode 100644 index b51e491..0000000 --- a/core/lib/Drupal/Core/EventSubscriber/HtmlViewSubscriber.php +++ /dev/null @@ -1,110 +0,0 @@ -fragmentRenderer = $fragment_renderer; - $this->pageRenderer = $page_renderer; - } - - /** - * Converts an HtmlFragment into an HtmlPage. - * - * @param \Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent $event - * The Event to process. - */ - public function onHtmlFragment(GetResponseForControllerResultEvent $event) { - $fragment = $event->getControllerResult(); - if ($fragment instanceof HtmlFragment && !$fragment instanceof HtmlPage) { - $page = $this->fragmentRenderer->render($fragment); - $event->setControllerResult($page); - } - } - - /** - * Renders an HtmlPage object to a Response. - * - * @param \Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent $event - * The Event to process. - */ - public function onHtmlPage(GetResponseForControllerResultEvent $event) { - $page = $event->getControllerResult(); - if ($page instanceof HtmlPage) { - // In case renderPage() returns NULL due to an error cast it to a string - // so as to not cause issues with Response. This also allows renderPage - // to return an object implementing __toString(), but that is not - // recommended. - $response = new Response((string) $this->pageRenderer->render($page), $page->getStatusCode()); - if ($tags = $page->getCacheTags()) { - $response->headers->set('X-Drupal-Cache-Tags', implode(' ', $tags)); - } - if ($keys = $page->getCacheKeys()) { - $response->headers->set('cache_keys', serialize($keys)); - } - if ($max_age = $page->getCacheMaxAge()) { - $response->headers->set('cache_max_age', $max_age); - } - - // Set the generator in the HTTP header. - list($version) = explode('.', \Drupal::VERSION, 2); - $response->headers->set('X-Generator', 'Drupal ' . $version . ' (http://drupal.org)'); - - $event->setResponse($response); - } - } - - /** - * Registers the methods in this class that should be listeners. - * - * @return array - * An array of event listener definitions. - */ - static function getSubscribedEvents() { - $events[KernelEvents::VIEW][] = array('onHtmlFragment', 100); - $events[KernelEvents::VIEW][] = array('onHtmlPage', 50); - - return $events; - } - -} diff --git a/core/lib/Drupal/Core/EventSubscriber/MainContentViewSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/MainContentViewSubscriber.php new file mode 100644 index 0000000..fcc51a3 --- /dev/null +++ b/core/lib/Drupal/Core/EventSubscriber/MainContentViewSubscriber.php @@ -0,0 +1,107 @@ +classResolver = $class_resolver; + $this->routeMatch = $route_match; + $this->mainContentRenderers = $main_content_renderers; + } + + /** + * Sets a response given a (main content) render array. + * + * @param \Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent $event + * The event to process. + */ + public function onViewRenderArray(GetResponseForControllerResultEvent $event) { + $request = $event->getRequest(); + $result = $event->getControllerResult(); + + $format = $request->getRequestFormat(); + + // Render the controller result into a response if it's a render array. + if (is_array($result)) { + if (isset($this->mainContentRenderers[$format])) { + $renderer = $this->classResolver->getInstanceFromDefinition($this->mainContentRenderers[$format]); + $event->setResponse($renderer->renderResponse($result, $request, $this->routeMatch)); + } + else { + $supported_formats = array_keys($this->mainContentRenderers); + $supported_mimetypes = array_map([$request, 'getMimeType'], $supported_formats); + $event->setResponse(new JsonResponse([ + 'message' => 'Not Acceptable.', + 'supported_mime_types' => $supported_mimetypes, + ], 406)); + } + } + } + + /** + * {@inheritdoc} + */ + static function getSubscribedEvents() { + $events[KernelEvents::VIEW][] = ['onViewRenderArray']; + + return $events; + } + +} diff --git a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php index f09e3e3..164dec0 100644 --- a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php @@ -10,7 +10,7 @@ use Drupal\Component\Utility\String; use Drupal\Component\Utility\Xss; use Drupal\Core\Config\ConfigFactoryInterface; -use Drupal\Core\Page\DefaultHtmlPageRenderer; +use Drupal\Core\Render\BareHtmlPageRendererInterface; use Drupal\Core\Routing\RouteMatch; use Drupal\Core\Routing\UrlGeneratorInterface; use Drupal\Core\Session\AccountInterface; @@ -58,6 +58,13 @@ class MaintenanceModeSubscriber implements EventSubscriberInterface { protected $urlGenerator; /** + * The bare HTML page renderer. + * + * @var \Drupal\Core\Render\BareHtmlPageRendererInterface + */ + protected $bareHtmlPageRenderer; + + /** * Constructs a new MaintenanceModeSubscriber. * * @param \Drupal\Core\Site\MaintenanceModeInterface $maintenance_mode @@ -70,13 +77,16 @@ class MaintenanceModeSubscriber implements EventSubscriberInterface { * The url generator. * @param \Drupal\Core\Session\AccountInterface $account * The current user. + * @param \Drupal\Core\Render\BareHtmlPageRendererInterface $bare_html_page_renderer + * The bare HTML page renderer. */ - public function __construct(MaintenanceModeInterface $maintenance_mode, ConfigFactoryInterface $config_factory, TranslationInterface $translation, UrlGeneratorInterface $url_generator, AccountInterface $account) { + public function __construct(MaintenanceModeInterface $maintenance_mode, ConfigFactoryInterface $config_factory, TranslationInterface $translation, UrlGeneratorInterface $url_generator, AccountInterface $account, BareHtmlPageRendererInterface $bare_html_page_renderer) { $this->maintenanceMode = $maintenance_mode; $this->config = $config_factory; $this->stringTranslation = $translation; $this->urlGenerator = $url_generator; $this->account = $account; + $this->bareHtmlPageRenderer = $bare_html_page_renderer; } /** @@ -95,11 +105,8 @@ public function onKernelRequestMaintenance(GetResponseEvent $event) { $content = Xss::filterAdmin(String::format($this->config->get('system.maintenance')->get('message'), array( '@site' => $this->config->get('system.site')->get('name'), ))); - // @todo Break the dependency on DefaultHtmlPageRenderer, see: - // https://www.drupal.org/node/2295609 - $content = DefaultHtmlPageRenderer::renderPage($content, $this->t('Site under maintenance')); - $response = new Response('Service unavailable', 503); - $response->setContent($content); + $output = $this->bareHtmlPageRenderer->renderMaintenancePage($content, $this->t('Site under maintenance')); + $response = new Response($output, 503); $event->setResponse($response); } else { diff --git a/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php index 7f13698..0b0c274 100644 --- a/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/ViewSubscriber.php @@ -7,9 +7,7 @@ namespace Drupal\Core\EventSubscriber; -use Drupal\Core\Ajax\AjaxResponseRenderer; use Drupal\Core\Controller\TitleResolverInterface; -use Drupal\Core\Page\HtmlPage; use Symfony\Cmf\Component\Routing\RouteObjectInterface; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\JsonResponse; @@ -43,12 +41,6 @@ class ViewSubscriber implements EventSubscriberInterface { */ protected $titleResolver; - /** - * The Ajax response renderer. - * - * @var \Drupal\Core\Ajax\AjaxResponseRenderer - */ - protected $ajaxRenderer; /** * Constructs a new ViewSubscriber. @@ -57,13 +49,10 @@ class ViewSubscriber implements EventSubscriberInterface { * The content negotiation. * @param \Drupal\Core\Controller\TitleResolverInterface $title_resolver * The title resolver. - * @param \Drupal\Core\Ajax\AjaxResponseRenderer $ajax_renderer - * The ajax response renderer. */ - public function __construct(ContentNegotiation $negotiation, TitleResolverInterface $title_resolver, AjaxResponseRenderer $ajax_renderer) { + public function __construct(ContentNegotiation $negotiation, TitleResolverInterface $title_resolver) { $this->negotiation = $negotiation; $this->titleResolver = $title_resolver; - $this->ajaxRenderer = $ajax_renderer; } /** @@ -79,14 +68,8 @@ public function __construct(ContentNegotiation $negotiation, TitleResolverInterf * The Event to process. */ public function onView(GetResponseForControllerResultEvent $event) { - $request = $event->getRequest(); - // For a master request, we process the result and wrap it as needed. - // For a subrequest, all we want is the string value. We assume that - // is just an HTML string from a controller, so wrap that into a response - // object. The subrequest's response will get dissected and placed into - // the larger page as needed. if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) { $method = 'on' . $this->negotiation->getContentType($request); @@ -97,27 +80,6 @@ public function onView(GetResponseForControllerResultEvent $event) { $event->setResponse(new Response('Not Acceptable', 406)); } } - else { - // This is a new-style Symfony-esque subrequest, which means we assume - // the body is not supposed to be a complete page but just a page - // fragment. - $page_result = $event->getControllerResult(); - if ($page_result instanceof HtmlPage || $page_result instanceof Response) { - return $page_result; - } - if (!is_array($page_result)) { - $page_result = array( - '#markup' => $page_result, - ); - } - - // If no title was returned fall back to one defined in the route. - if (!isset($page_result['#title'])) { - $page_result['#title'] = $this->titleResolver->getTitle($request, $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT)); - } - - $event->setResponse(new Response(drupal_render_root($page_result))); - } } public function onJson(GetResponseForControllerResultEvent $event) { diff --git a/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php b/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php deleted file mode 100644 index 0cdd613..0000000 --- a/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php +++ /dev/null @@ -1,155 +0,0 @@ -languageManager = $language_manager; - } - - /** - * {@inheritdoc} - */ - public function render(HtmlFragmentInterface $fragment, $status_code = 200) { - // Converts the given HTML fragment which represents the main content region - // of the page into a render array. - $page_content['main'] = array( - '#markup' => $fragment->getContent(), - ); - $page_content['#title'] = $fragment->getTitle(); - - if ($fragment instanceof CacheableInterface) { - $page_content['main']['#cache']['tags'] = $fragment->getCacheTags(); - } - - // Build the full page array by calling drupal_prepare_page(), which invokes - // hook_page_build(). This adds the other regions to the page. - $page_array = drupal_prepare_page($page_content); - - // Build the HtmlPage object. - $page = new HtmlPage('', array(), $fragment->getTitle()); - $page = $this->preparePage($page, $page_array); - $page->setBodyTop(drupal_render_root($page_array['page_top'])); - $page->setBodyBottom(drupal_render_root($page_array['page_bottom'])); - $page->setContent(drupal_render_root($page_array)); - $page->setStatusCode($status_code); - - drupal_process_attached($page_array); - if (isset($page_array['page_top'])) { - drupal_process_attached($page_array['page_top']); - } - if (isset($page_array['page_bottom'])) { - drupal_process_attached($page_array['page_bottom']); - } - - if ($fragment instanceof CacheableInterface) { - // Persist cache tags associated with this page. Also associate the - // "rendered" cache tag. This allows us to invalidate the entire render - // cache, regardless of the cache bin. - $cache_tags = Cache::mergeTags( - isset($page_array['page_top']) ? $page_array['page_top']['#cache']['tags'] : [], - $page_array['#cache']['tags'], - isset($page_array['page_bottom']) ? $page_array['page_bottom']['#cache']['tags'] : [], - ['rendered'] - ); - // Only keep unique cache tags. We need to prevent duplicates here already - // rather than only in the cache layer, because they are also used by - // reverse proxies (like Varnish), not only by Drupal's page cache. - $page->setCacheTags(array_unique($cache_tags)); - } - - return $page; - } - - /** - * Enhances a page object based on a render array. - * - * @param \Drupal\Core\Page\HtmlPage $page - * The page object to enhance. - * @param array $page_array - * The page array to extract onto the page object. - * - * @return \Drupal\Core\Page\HtmlPage - * The modified page object. - */ - public function preparePage(HtmlPage $page, &$page_array) { - $page_array['#page'] = $page; - - // HTML element attributes. - $language_interface = $this->languageManager->getCurrentLanguage(); - $html_attributes = $page->getHtmlAttributes(); - $html_attributes['lang'] = $language_interface->getId(); - $html_attributes['dir'] = $language_interface->getDirection(); - - $this->setDefaultMetaTags($page); - - // Add libraries and CSS used by this theme. - $active_theme = \Drupal::theme()->getActiveTheme(); - foreach ($active_theme->getLibraries() as $library) { - $page_array['#attached']['library'][] = $library; - } - foreach ($active_theme->getStyleSheets() as $media => $stylesheets) { - foreach ($stylesheets as $stylesheet) { - $page_array['#attached']['css'][$stylesheet] = array( - 'group' => CSS_AGGREGATE_THEME, - 'every_page' => TRUE, - 'media' => $media - ); - } - } - - return $page; - } - - /** - * Apply the default meta tags to the page object. - * - * @param \Drupal\Core\Page\HtmlPage $page - * The html page. - */ - protected function setDefaultMetaTags(HtmlPage $page) { - // Add default elements. Make sure the Content-Type comes first because the - // IE browser may be vulnerable to XSS via encoding attacks from any content - // that comes before this META tag, such as a TITLE tag. - $page->addMetaElement(new MetaElement(NULL, array( - 'name' => 'charset', - 'charset' => 'utf-8', - ))); - // Show Drupal and the major version number in the META GENERATOR tag. - // Get the major version. - list($version) = explode('.', \Drupal::VERSION, 2); - $page->addMetaElement(new MetaElement('Drupal ' . $version . ' (http://drupal.org)', array( - 'name' => 'Generator', - ))); - - // Display the html.html.twig's default mobile metatags for responsive design. - $page->addMetaElement(new MetaElement(NULL, array('name' => 'viewport', 'content' => 'width=device-width, initial-scale=1.0'))); - } - -} diff --git a/core/lib/Drupal/Core/Page/DefaultHtmlPageRenderer.php b/core/lib/Drupal/Core/Page/DefaultHtmlPageRenderer.php deleted file mode 100644 index eea3e16..0000000 --- a/core/lib/Drupal/Core/Page/DefaultHtmlPageRenderer.php +++ /dev/null @@ -1,125 +0,0 @@ - 'html', - '#page_object' => $page, - ); - // drupal_render() will render the 'html' template, which will call - // HtmlPage::getScripts(). But normally we can only run - // drupal_process_attached() after drupal_render(). Hence any assets - // attached to '#type' => 'html' will be lost. This is a work-around for - // that limitation, until the HtmlPage object contains its assets — this is - // an unfortunate intermediate consequence of the way HtmlPage dictates page - // rendering and how that differs from how drupal_render() works. - $render += element_info($render['#type']); - drupal_process_attached($render); - 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 - * controllers (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_root($page_array['page_top'])); - $page->setBodyBottom(drupal_render_root($page_array['page_bottom'])); - $page->setContent(drupal_render_root($page_array)); - drupal_process_attached($page_array); - if (isset($page_array['page_top'])) { - drupal_process_attached($page_array['page_top']); - } - if (isset($page_array['page_bottom'])) { - drupal_process_attached($page_array['page_bottom']); - } - - return \Drupal::service('html_page_renderer')->render($page); - } - -} diff --git a/core/lib/Drupal/Core/Page/FeedLinkElement.php b/core/lib/Drupal/Core/Page/FeedLinkElement.php deleted file mode 100644 index 648963b..0000000 --- a/core/lib/Drupal/Core/Page/FeedLinkElement.php +++ /dev/null @@ -1,32 +0,0 @@ -. - * - * @var bool - */ - protected $noScript = FALSE; - - /** - * Renders this object to an HTML element string. - * - * @return string - */ - public function __toString() { - // Render the attributes via the attribute template class. - // @todo Should HeadElement just extend the Attribute classes? - $attributes = new Attribute($this->attributes); - $rendered = (string) $attributes; - - $string = "<{$this->element}{$rendered} />"; - if ($this->noScript) { - $string = ""; - } - return SafeMarkup::set($string); - } - - /** - * Sets an attribute on this element. - * - * @param mixed $key - * The attribute to set. - * @param mixed $value - * The value to which to set it. - * - * @return $this - */ - public function setAttribute($key, $value) { - $this->attributes[$key] = $value; - return $this; - } - - /** - * Gets all the attributes. - * - * @return array - * An array of all the attributes keyed by name of attribute. - */ - public function &getAttributes() { - return $this->attributes; - } - - /** - * Sets if this element should be wrapped in