diff --git a/core/authorize.php b/core/authorize.php index 35277f6..d5baed4 100644 --- a/core/authorize.php +++ b/core/authorize.php @@ -20,11 +20,11 @@ * @link authorize Authorized operation helper functions @endlink */ +use Drupal\Core\Render\CacheableHtmlResponse; use Drupal\Core\DrupalKernel; use Drupal\Core\Url; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; use Drupal\Core\Site\Settings; // Change the directory to the Drupal root. @@ -85,7 +85,7 @@ function authorize_access_allowed(Request $request) { $content = []; $show_messages = TRUE; -$response = new Response(); +$response = new CacheableHtmlResponse(); if (authorize_access_allowed($request)) { // Load both the Form API and Batch API. require_once __DIR__ . '/includes/form.inc'; @@ -159,9 +159,15 @@ function authorize_access_allowed(Request $request) { } if (!empty($content)) { + $bare_html_page_renderer = \Drupal::service('bare_html_page_renderer'); $response->headers->set('Content-Type', 'text/html; charset=utf-8'); - $response->setContent(\Drupal::service('bare_html_page_renderer')->renderBarePage($content, $page_title, 'maintenance_page', array( + $response->setContent($bare_html_page_renderer->renderBarePage($content, $page_title, 'maintenance_page', array( '#show_messages' => $show_messages, ))); + + // Filter the response as it does not go via the usual chain, + // but there are assets on there that need to be rendered. + $bare_html_page_renderer->filterResponse($response); + $response->send(); } diff --git a/core/core.services.yml b/core/core.services.yml index 58db84c..84ecdca 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -897,7 +897,7 @@ services: arguments: ['@router', '@router.request_context', NULL, '@request_stack'] bare_html_page_renderer: class: Drupal\Core\Render\BareHtmlPageRenderer - arguments: ['@renderer'] + arguments: ['@renderer', '@render_cache'] lazy: true private_key: class: Drupal\Core\PrivateKey diff --git a/core/includes/batch.inc b/core/includes/batch.inc index 7d905cd..a5d6242 100644 --- a/core/includes/batch.inc +++ b/core/includes/batch.inc @@ -135,9 +135,11 @@ 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 = \Drupal::service('bare_html_page_renderer')->renderBarePage(['#markup' => $fallback], $current_set['title'], 'maintenance_page', array( + $bare_html_page_renderer = \Drupal::service('bare_html_page_renderer'); + $content = $bare_html_page_renderer->renderBarePage(['#markup' => $fallback], $current_set['title'], 'maintenance_page', array( '#show_messages' => FALSE, )); + $fallback = $bare_html_page_renderer->filterRenderArray($content); list($fallback) = explode('', $fallback); print $fallback; diff --git a/core/includes/errors.inc b/core/includes/errors.inc index 5e38a97..8caf68f 100644 --- a/core/includes/errors.inc +++ b/core/includes/errors.inc @@ -241,12 +241,18 @@ function _drupal_log_error($error, $fatal = FALSE) { '#markup' => $message, ); install_display_output($output, $GLOBALS['install_state']); + exit; } - else { - $output = \Drupal::service('bare_html_page_renderer')->renderBarePage(['#markup' => $message], 'Error', 'maintenance_page'); - } - $response = new Response($output, 500); + $bare_html_page_renderer = \Drupal::service('bare_html_page_renderer'); + $output = $bare_html_page_renderer->renderBarePage(['#markup' => $message], 'Error', 'maintenance_page', TRUE); + + $response = new CacheableHtmlResponse($output, 500); + + // Filter the response as it does not go via the usual chain, + // but there are assets on there that need to be rendered. + $bare_html_page_renderer->filterResponse($response); + $response->setStatusCode(500, '500 Service unavailable (with message)'); // An exception must halt script execution. $response->send(); diff --git a/core/includes/theme.inc b/core/includes/theme.inc index d8f3136..0e7639e 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -1311,41 +1311,11 @@ function template_preprocess_html(&$variables) { // @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0. $variables['head_title_array'] = $head_title; - // 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']); - } - if (isset($variables['page_bottom'])) { - $attached = drupal_merge_attached($attached, $variables['page_bottom']['#attached']); - } - - // Render the attachments into HTML markup to be used directly in the template - // for #type => html: html.html.twig. - $all_attached = ['#attached' => $attached]; - $assets = AttachedAssets::createFromRenderArray($all_attached); - // Take Ajax page state into account, to allow for something like Turbolinks - // to be implemented without altering core. - // @see https://github.com/rails/turbolinks/ - $ajax_page_state = \Drupal::request()->request->get('ajax_page_state'); - $assets->setAlreadyLoadedLibraries(isset($ajax_page_state) ? explode(',', $ajax_page_state['libraries']) : []); - // Optimize CSS/JS if necessary, but only during normal site operation. - $optimize_css = !defined('MAINTENANCE_MODE') && \Drupal::config('system.performance')->get('css.preprocess'); - $optimize_js = !defined('MAINTENANCE_MODE') && \Drupal::config('system.performance')->get('js.preprocess'); - // Render the asset collections. - $asset_resolver = \Drupal::service('asset.resolver'); - $variables['styles'] = \Drupal::service('asset.css.collection_renderer')->render($asset_resolver->getCssAssets($assets, $optimize_css)); - list($js_assets_header, $js_assets_footer) = $asset_resolver->getJsAssets($assets, $optimize_js); - $js_collection_renderer = \Drupal::service('asset.js.collection_renderer'); - $variables['scripts'] = $js_collection_renderer->render($js_assets_header); - $variables['scripts_bottom'] = $js_collection_renderer->render($js_assets_footer); - - // Handle all non-asset attachments. - drupal_process_attached($all_attached); - $variables['head'] = drupal_get_html_head(FALSE); + // @todo Move to a service. + $variables['styles']['#markup'] = ''; + $variables['scripts']['#markup'] = ''; + $variables['scripts_bottom']['#markup'] = ''; + $variables['head']['#markup'] = ''; } /** diff --git a/core/lib/Drupal/Core/Ajax/AjaxResponse.php b/core/lib/Drupal/Core/Ajax/AjaxResponse.php index befb3be..c2d8d06 100644 --- a/core/lib/Drupal/Core/Ajax/AjaxResponse.php +++ b/core/lib/Drupal/Core/Ajax/AjaxResponse.php @@ -9,6 +9,8 @@ use Drupal\Core\Asset\AttachedAssets; use Drupal\Core\Render\Renderer; +use Drupal\Core\Render\AttachmentsResponseInterface; +use Drupal\Core\Render\AttachmentsResponseTrait; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; @@ -19,7 +21,9 @@ * * @ingroup ajax */ -class AjaxResponse extends JsonResponse { +class AjaxResponse extends JsonResponse implements AttachmentsResponseInterface { + + use AttachmentsResponseTrait; /** * The array of ajax commands. @@ -29,32 +33,6 @@ class AjaxResponse extends JsonResponse { protected $commands = array(); /** - * The attachments for this Ajax response. - * - * @var array - */ - protected $attachments = [ - 'library' => [], - 'drupalSettings' => [], - ]; - - /** - * Sets attachments for this Ajax response. - * - * When this Ajax response is rendered, it will take care of generating the - * necessary Ajax commands, if any. - * - * @param array $attachments - * An #attached array. - * - * @return $this - */ - public function setAttachments(array $attachments) { - $this->attachments = $attachments; - return $this; - } - - /** * Add an AJAX command to the response. * * @param \Drupal\Core\Ajax\CommandInterface $command @@ -79,7 +57,7 @@ public function addCommand(CommandInterface $command, $prepend = FALSE) { 'library' => $assets->getLibraries(), 'drupalSettings' => $assets->getSettings(), ]; - $attachments = $this->getRenderer()->mergeAttachments($this->attachments, $attachments); + $attachments = $this->getRenderer()->mergeAttachments($this->getAttachments(), $attachments); $this->setAttachments($attachments); } @@ -171,11 +149,13 @@ protected function ajaxRender(Request $request) { $optimize_css = !defined('MAINTENANCE_MODE') && $config->get('css.preprocess'); $optimize_js = !defined('MAINTENANCE_MODE') && $config->get('js.preprocess'); + $attachments = $this->getAttachments(); + // Resolve the attached libraries into asset collections. $assets = new AttachedAssets(); - $assets->setLibraries(isset($this->attachments['library']) ? $this->attachments['library'] : []) + $assets->setLibraries(isset($attachments['library']) ? $attachments['library'] : []) ->setAlreadyLoadedLibraries(isset($ajax_page_state) ? explode(',', $ajax_page_state['libraries']) : []) - ->setSettings(isset($this->attachments['drupalSettings']) ? $this->attachments['drupalSettings'] : []); + ->setSettings(isset($attachments['drupalSettings']) ? $attachments['drupalSettings'] : []); $asset_resolver = \Drupal::service('asset.resolver'); $css_assets = $asset_resolver->getCssAssets($assets, $optimize_css); list($js_assets_header, $js_assets_footer) = $asset_resolver->getJsAssets($assets, $optimize_js); diff --git a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php index 883927c..17a7315 100644 --- a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php @@ -10,12 +10,12 @@ use Drupal\Component\Utility\SafeMarkup; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Render\BareHtmlPageRendererInterface; +use Drupal\Core\Render\CacheableHtmlResponse; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\Utility\Error; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface; use Symfony\Component\HttpKernel\KernelEvents; @@ -130,7 +130,7 @@ protected function onHtml(GetResponseForExceptionEvent $event) { $content = $this->t('The website encountered an unexpected error. Please try again later.'); $output = $this->bareHtmlPageRenderer->renderBarePage(['#markup' => $content], $this->t('Error'), 'maintenance_page'); - $response = new Response($output); + $response = new CacheableHtmlResponse($output); if ($exception instanceof HttpExceptionInterface) { $response->setStatusCode($exception->getStatusCode()); diff --git a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php index bee69a1..fdaeb25 100644 --- a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php @@ -8,6 +8,8 @@ namespace Drupal\Core\EventSubscriber; use Drupal\Component\Datetime\DateTimePlus; +use Drupal\Core\Asset\AttachedAssets; +use Drupal\Core\Ajax\AjaxResponse; use Drupal\Core\Cache\CacheableResponseInterface; use Drupal\Core\Cache\CacheContextsManager; use Drupal\Core\Config\Config; @@ -15,6 +17,7 @@ use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\PageCache\RequestPolicyInterface; use Drupal\Core\PageCache\ResponsePolicyInterface; +use Drupal\Core\Render\AttachmentsResponseInterface; use Drupal\Core\Site\Settings; use Symfony\Component\HttpFoundation\BinaryFileResponse; use Symfony\Component\HttpFoundation\Request; @@ -114,14 +117,8 @@ public function onRespond(FilterResponseEvent $event) { // https://www.owasp.org/index.php/List_of_useful_HTTP_headers $response->headers->set('X-Content-Type-Options', 'nosniff', FALSE); - // Attach globally-declared headers to the response object so that Symfony - // can send them for us correctly. - // @todo Remove this once drupal_process_attached() no longer calls - // _drupal_add_http_header(), which has its own static. Instead, - // _drupal_process_attached() should use - // \Symfony\Component\HttpFoundation\Response->headers->set(), which is - // already documented on the (deprecated) _drupal_process_attached() to - // become the final, intended mechanism. + static::processAttachments($response); + $headers = drupal_get_http_header(); foreach ($headers as $name => $value) { // Symfony special-cases the 'Status' header. @@ -158,6 +155,54 @@ public function onRespond(FilterResponseEvent $event) { } } + public static function processAttachments($response) { + // @todo Move to a service. + if ($response instanceof AttachmentsResponseInterface && !($response instanceof AjaxResponse)) { + // Render the attachments into HTML markup + $attached = $response->getAttachments(); + $all_attached = ['#attached' => $attached]; + + $assets = AttachedAssets::createFromRenderArray($all_attached); + // Take Ajax page state into account, to allow for something like Turbolinks + // to be implemented without altering core. + // @see https://github.com/rails/turbolinks/ + $ajax_page_state = \Drupal::request()->request->get('ajax_page_state'); + $assets->setAlreadyLoadedLibraries(isset($ajax_page_state) ? explode(',', $ajax_page_state['libraries']) : []); + // Optimize CSS/JS if necessary, but only during normal site operation. + $optimize_css = !defined('MAINTENANCE_MODE') && \Drupal::config('system.performance')->get('css.preprocess'); + $optimize_js = !defined('MAINTENANCE_MODE') && \Drupal::config('system.performance')->get('js.preprocess'); + // Render the asset collections. + $asset_resolver = \Drupal::service('asset.resolver'); + + $variables = []; + $variables['styles'] = \Drupal::service('asset.css.collection_renderer')->render($asset_resolver->getCssAssets($assets, $optimize_css)); + list($js_assets_header, $js_assets_footer) = $asset_resolver->getJsAssets($assets, $optimize_js); + $js_collection_renderer = \Drupal::service('asset.js.collection_renderer'); + $variables['scripts'] = $js_collection_renderer->render($js_assets_header); + $variables['scripts_bottom'] = $js_collection_renderer->render($js_assets_footer); + + // Handle all non-asset attachments. + drupal_process_attached($all_attached); + $variables['head'] = drupal_get_html_head(FALSE); + + $content = $response->getContent(); + + $map = [ + 'styles' => '', + 'scripts' => '', + 'scripts_bottom' => '', + 'head' => '', + ]; + foreach ($map as $key => $placeholder) { + if (strpos($content, $placeholder) !== FALSE) { + $content = str_replace($placeholder, render($variables[$key]), $content); + } + } + + $response->setContent($content); + } + } + /** * Determine whether the given response has a custom Cache-Control header. * diff --git a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php index e43d00e..8adec60 100644 --- a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php @@ -11,6 +11,7 @@ use Drupal\Component\Utility\Xss; use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Render\BareHtmlPageRendererInterface; +use Drupal\Core\Render\CacheableHtmlResponse; use Drupal\Core\Routing\RouteMatch; use Drupal\Core\Routing\UrlGeneratorInterface; use Drupal\Core\Session\AccountInterface; @@ -18,7 +19,6 @@ use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\TranslationInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; -use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; @@ -108,7 +108,7 @@ public function onKernelRequestMaintenance(GetResponseEvent $event) { '@site' => $this->config->get('system.site')->get('name'), ))); $output = $this->bareHtmlPageRenderer->renderBarePage(['#markup' => $content], $this->t('Site under maintenance'), 'maintenance_page'); - $response = new Response($output, 503); + $response = new CacheableHtmlResponse($output, 503); $event->setResponse($response); } else { diff --git a/core/lib/Drupal/Core/Render/AttachmentsResponseInterface.php b/core/lib/Drupal/Core/Render/AttachmentsResponseInterface.php new file mode 100644 index 0000000..4c0b7b5 --- /dev/null +++ b/core/lib/Drupal/Core/Render/AttachmentsResponseInterface.php @@ -0,0 +1,39 @@ + [], + 'drupalSettings' => [], + 'html_head' => [], + 'feed' => [], + 'html_head_link' => [], + 'http_header' => [], + ]; + + /** + * {@inheritdoc} + */ + public function setAttachments(array $attachments) { + $this->attachments = $attachments; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getAttachments() { + return $this->attachments; + } + +} diff --git a/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php b/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php index c8012c6..ae2fb42 100644 --- a/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php +++ b/core/lib/Drupal/Core/Render/BareHtmlPageRenderer.php @@ -7,6 +7,8 @@ namespace Drupal\Core\Render; +use Drupal\Core\EventSubscriber\FinishResponseSubscriber; + /** * Default bare HTML page renderer. */ @@ -20,13 +22,21 @@ class BareHtmlPageRenderer implements BareHtmlPageRendererInterface { protected $renderer; /** + * The RenderCache service. + * + * @var \Drupal\Core\Render\RenderCache + */ + protected $renderCache; + + /** * Constructs a new BareHtmlPageRenderer. * * @param \Drupal\Core\Render\RendererInterface $renderer * The renderer service. */ - public function __construct(RendererInterface $renderer) { + public function __construct(RendererInterface $renderer, RenderCacheInterface $render_cache) { $this->renderer = $renderer; + $this->renderCache = $render_cache; } /** @@ -55,16 +65,32 @@ public function renderBarePage(array $content, $title, $page_theme_property, arr $html['page']['highlighted'] = ['#type' => 'status_messages']; } - // We must first render the contents of the html.html.twig template, see - // \Drupal\Core\Render\MainContent\HtmlRenderer::renderResponse() for more - // information about this; the exact same pattern is used there and - // explained in detail there. - $this->renderer->render($html['page'], TRUE); - // Add the bare minimum of attachments from the system module and the // current maintenance theme. system_page_attachments($html['page']); - return $this->renderer->render($html); + $this->renderer->renderRoot($html); + $content = $this->renderCache->getCacheableRenderArray($html); + + // Never cache exception pages. + $content['#cache']['max-age'] = 0; + return $content; + } + + /** + * {@inheritdoc} + */ + public function filterResponse(AttachmentsResponseInterface $response) { + FinishResponseSubscriber::processAttachments($response); } + /** + * {@inheritdoc} + */ + public function filterRenderArray(array $build) { + $response = new CacheableHtmlResponse($build); + FinishResponseSubscriber::processAttachments($response); + return $response->getContent(); + } + + } diff --git a/core/lib/Drupal/Core/Render/CacheableHtmlResponse.php b/core/lib/Drupal/Core/Render/CacheableHtmlResponse.php new file mode 100644 index 0000000..32f3b96 --- /dev/null +++ b/core/lib/Drupal/Core/Render/CacheableHtmlResponse.php @@ -0,0 +1,47 @@ +addCacheableDependency(CacheableMetadata::createFromRenderArray($content)); + $this->setAttachments($content['#attached']); + $content = $content['#markup']; + } + + parent::setContent($content); + } +} diff --git a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php index addd13c..4f34f02 100644 --- a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php +++ b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php @@ -13,6 +13,7 @@ use Drupal\Core\Controller\TitleResolverInterface; use Drupal\Core\Display\PageVariantInterface; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Render\CacheableHtmlResponse; use Drupal\Core\Render\PageDisplayVariantSelectionEvent; use Drupal\Core\Render\RenderCacheInterface; use Drupal\Core\Render\RendererInterface; @@ -119,45 +120,22 @@ public function renderResponse(array $main_content, Request $request, RouteMatch // page.html.twig, hence add them here, just before rendering html.html.twig. $this->buildPageTopAndBottom($html); - // The three parts of rendered markup in html.html.twig (page_top, page and - // page_bottom) must be rendered with drupal_render_root(), so that their - // #post_render_cache callbacks are executed (which may attach additional - // assets). - // html.html.twig must be able to render the final list of attached assets, - // and hence may not execute any #post_render_cache_callbacks (because they - // might add yet more assets to be attached), and therefore it must be - // rendered with drupal_render(), not drupal_render_root(). - $this->renderer->render($html['page'], TRUE); - if (isset($html['page_top'])) { - $this->renderer->render($html['page_top'], TRUE); - } - if (isset($html['page_bottom'])) { - $this->renderer->render($html['page_bottom'], TRUE); - } - $content = $this->renderer->render($html); + // @todo Make renderRoot return a cacheable render array directly. + $this->renderer->renderRoot($html); + $content = $this->renderCache->getCacheableRenderArray($html); + + // Also associate the "rendered" cache tag. This allows us to invalidate the + // entire render cache, regardless of the cache bin. + $content['#cache']['tags'][] = 'rendered'; // Set the generator in the HTTP header. list($version) = explode('.', \Drupal::VERSION, 2); - $response = new CacheableResponse($content, 200,[ + $response = new CacheableHtmlResponse($content, 200,[ 'Content-Type' => 'text/html; charset=UTF-8', 'X-Generator' => 'Drupal ' . $version . ' (https://www.drupal.org)' ]); - // Bubble the cacheability metadata associated with the rendered render - // arrays to the response. - foreach (['page_top', 'page', 'page_bottom'] as $region) { - if (isset($html[$region])) { - $response->addCacheableDependency(CacheableMetadata::createFromRenderArray($html[$region])); - } - } - - // Also associate the "rendered" cache tag. This allows us to invalidate the - // entire render cache, regardless of the cache bin. - $default = new CacheableMetadata(); - $default->setCacheTags(['rendered']); - $response->addCacheableDependency($default); - return $response; } diff --git a/core/modules/system/src/Controller/DbUpdateController.php b/core/modules/system/src/Controller/DbUpdateController.php index 3c10c8a..926da6b 100644 --- a/core/modules/system/src/Controller/DbUpdateController.php +++ b/core/modules/system/src/Controller/DbUpdateController.php @@ -8,17 +8,18 @@ namespace Drupal\system\Controller; use Drupal\Core\Cache\CacheBackendInterface; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface; use Drupal\Core\Extension\ModuleHandlerInterface; use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface; use Drupal\Core\Render\BareHtmlPageRendererInterface; +use Drupal\Core\Render\CacheableHtmlResponse; use Drupal\Core\Session\AccountInterface; use Drupal\Core\Site\Settings; use Drupal\Core\State\StateInterface; use Drupal\Core\Url; use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Request; /** @@ -198,7 +199,7 @@ public function handle($op, Request $request) { } $title = isset($output['#title']) ? $output['#title'] : $this->t('Drupal database update'); - return new Response($this->bareHtmlPageRenderer->renderBarePage($output, $title, 'maintenance_page', $regions)); + return new CacheableHtmlResponse($this->bareHtmlPageRenderer->renderBarePage($output, $title, 'maintenance_page', $regions)); } /**