public function testAccess(Request $request, EditorInterface $editor, EmbedButtonInterface $embed_button) { $text = $request->get('value'); $response = new HtmlResponse(['#markup' => $text, '#cache' => ['contexts' => ['url.query_args:value']]]); if ($text == '') { $response->setStatusCode(404); } return $response; }
/** * Sets headers on a response object. * * @param \Drupal\Core\Render\HtmlResponse $response * The HTML response to update. * @param array $headers * The headers to set, as an array. The items in this array should be as * follows: * - The header name. * - The header value. * - (optional) Whether to replace a current value with the new one, or add * it to the others. If the value is not replaced, it will be appended, * resulting in a header like this: 'Header: value1,value2' */ protected function setHeaders(HtmlResponse $response, array $headers) { foreach ($headers as $values) { $name = $values[0]; $value = $values[1]; $replace = !empty($values[2]); // Drupal treats the HTTP response status code like a header, even though // it really is not. if (strtolower($name) === 'status') { $response->setStatusCode($value); } else { $response->headers->set($name, $value, $replace); } } }
/** * Sends no-JS BigPipe placeholders' replacements as embedded HTML responses. * * @param string $html * HTML markup. * @param array $no_js_placeholders * Associative array; the no-JS BigPipe placeholders. Keys are the BigPipe * selectors. * @param \Drupal\Core\Asset\AttachedAssetsInterface $cumulative_assets * The cumulative assets sent so far; to be updated while rendering no-JS * BigPipe placeholders. * * @throws \Exception * If an exception is thrown during the rendering of a placeholder, it is * caught to allow the other placeholders to still be replaced. But when * error logging is configured to be verbose, the exception is rethrown to * simplify debugging. */ protected function sendNoJsPlaceholders($html, $no_js_placeholders, AttachedAssetsInterface $cumulative_assets) { // Split the HTML on every no-JS placeholder string. $prepare_for_preg_split = function ($placeholder_string) { return '(' . preg_quote($placeholder_string, '/') . ')'; }; $preg_placeholder_strings = array_map($prepare_for_preg_split, array_keys($no_js_placeholders)); $fragments = preg_split('/' . implode('|', $preg_placeholder_strings) . '/', $html, NULL, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); foreach ($fragments as $fragment) { // If the fragment isn't one of the no-JS placeholders, it is the HTML in // between placeholders and it must be printed & flushed immediately. The // rest of the logic in the loop handles the placeholders. if (!isset($no_js_placeholders[$fragment])) { print $fragment; flush(); continue; } $placeholder = $fragment; assert('isset($no_js_placeholders[$placeholder])'); $token = Crypt::randomBytesBase64(55); // Render the placeholder, but include the cumulative settings assets, so // we can calculate the overall settings for the entire page. $placeholder_plus_cumulative_settings = ['placeholder' => $no_js_placeholders[$placeholder], 'cumulative_settings_' . $token => ['#attached' => ['drupalSettings' => $cumulative_assets->getSettings()]]]; try { $elements = $this->renderPlaceholder($placeholder, $placeholder_plus_cumulative_settings); } catch (\Exception $e) { if (\Drupal::config('system.logging')->get('error_level') === ERROR_REPORTING_DISPLAY_VERBOSE) { throw $e; } else { trigger_error($e, E_USER_ERROR); continue; } } // Create a new HtmlResponse. Ensure the CSS and (non-bottom) JS is sent // before the HTML they're associated with. In other words: ensure the // critical assets for this placeholder's markup are loaded first. // @see \Drupal\Core\Render\HtmlResponseSubscriber // @see template_preprocess_html() $css_placeholder = '<nojs-bigpipe-placeholder-styles-placeholder token="' . $token . '">'; $js_placeholder = '<nojs-bigpipe-placeholder-scripts-placeholder token="' . $token . '">'; $elements['#markup'] = BigPipeMarkup::create($css_placeholder . $js_placeholder . (string) $elements['#markup']); $elements['#attached']['html_response_attachment_placeholders']['styles'] = $css_placeholder; $elements['#attached']['html_response_attachment_placeholders']['scripts'] = $js_placeholder; $html_response = new HtmlResponse(); $html_response->setContent($elements); $html_response->getCacheableMetadata()->setCacheMaxAge(0); // Push a fake request with the asset libraries loaded so far and dispatch // KernelEvents::RESPONSE event. This results in the attachments for the // HTML response being processed by HtmlResponseAttachmentsProcessor and // hence: // - the HTML to load the CSS can be rendered. // - the HTML to load the JS (at the top) can be rendered. $fake_request = $this->requestStack->getMasterRequest()->duplicate(); $fake_request->request->set('ajax_page_state', ['libraries' => implode(',', $cumulative_assets->getAlreadyLoadedLibraries())]); try { $html_response = $this->filterEmbeddedResponse($fake_request, $html_response); } catch (\Exception $e) { if (\Drupal::config('system.logging')->get('error_level') === ERROR_REPORTING_DISPLAY_VERBOSE) { throw $e; } else { trigger_error($e, E_USER_ERROR); continue; } } // Send this embedded HTML response. print $html_response->getContent(); flush(); // Another placeholder was rendered and sent, track the set of asset // libraries sent so far. Any new settings also need to be tracked, so // they can be sent in ::sendPreBody(). $cumulative_assets->setAlreadyLoadedLibraries(array_merge($cumulative_assets->getAlreadyLoadedLibraries(), $html_response->getAttachments()['library'])); $cumulative_assets->setSettings($html_response->getAttachments()['drupalSettings']); } }
/** * Sends no-JS BigPipe placeholders' replacements as embedded HTML responses. * * @param string $html * HTML markup. * @param array $no_js_placeholders * Associative array; the no-JS BigPipe placeholders. Keys are the BigPipe * selectors. * @param \Drupal\Core\Asset\AttachedAssetsInterface $cumulative_assets * The cumulative assets sent so far; to be updated while rendering no-JS * BigPipe placeholders. */ protected function sendNoJsPlaceholders($html, $no_js_placeholders, AttachedAssetsInterface $cumulative_assets) { $fragments = explode('<div data-big-pipe-selector-nojs="', $html); print array_shift($fragments); ob_end_flush(); flush(); foreach ($fragments as $fragment) { $t = explode('"></div>', $fragment, 2); $placeholder = $t[0]; if (!isset($no_js_placeholders[$placeholder])) { continue; } $token = Crypt::randomBytesBase64(55); // Render the placeholder, but include the cumulative settings assets, so // we can calculate the overall settings for the entire page. $placeholder_plus_cumulative_settings = ['placeholder' => $no_js_placeholders[$placeholder], 'cumulative_settings_' . $token => ['#attached' => ['drupalSettings' => $cumulative_assets->getSettings()]]]; $elements = $this->renderPlaceholder($placeholder, $placeholder_plus_cumulative_settings); // Create a new HtmlResponse. Ensure the CSS and (non-bottom) JS is sent // before the HTML they're associated with. In other words: ensure the // critical assets for this placeholder's markup are loaded first. // @see \Drupal\Core\Render\HtmlResponseSubscriber // @see template_preprocess_html() $css_placeholder = '<nojs-bigpipe-placeholder-styles-placeholder token="' . $token . '">'; $js_placeholder = '<nojs-bigpipe-placeholder-scripts-placeholder token="' . $token . '">'; $elements['#markup'] = Markup::create($css_placeholder . $js_placeholder . (string) $elements['#markup']); $elements['#attached']['html_response_attachment_placeholders']['styles'] = $css_placeholder; $elements['#attached']['html_response_attachment_placeholders']['scripts'] = $js_placeholder; $html_response = new HtmlResponse(); $html_response->setContent($elements); $html_response->getCacheableMetadata()->setCacheMaxAge(0); // Push a fake request with the asset libraries loaded so far and dispatch // KernelEvents::RESPONSE event. This results in the attachments for the // HTML response being processed by HtmlResponseAttachmentsProcessor and // hence: // - the HTML to load the CSS can be rendered. // - the HTML to load the JS (at the top) can be rendered. $fake_request = $this->requestStack->getMasterRequest()->duplicate(); $fake_request->request->set('ajax_page_state', ['libraries' => implode(',', $cumulative_assets->getAlreadyLoadedLibraries())] + $cumulative_assets->getSettings()['ajaxPageState']); $this->requestStack->push($fake_request); $event = new FilterResponseEvent($this->httpKernel, $fake_request, HttpKernelInterface::SUB_REQUEST, $html_response); $this->eventDispatcher->dispatch(KernelEvents::RESPONSE, $event); $html_response = $event->getResponse(); $this->requestStack->pop(); // Send this embedded HTML response. print $html_response->getContent(); print $t[1]; flush(); // Another placeholder was rendered and sent, track the set of asset // libraries sent so far. Any new settings also need to be tracked, so // they can be sent in ::sendPreBody(). // @todo What if drupalSettings already was printed in the HTML <head>? That case is not yet handled. In that case, no-JS BigPipe would cause broken (incomplete) drupalSettingsā¦ This would not matter if it were only used if JS is not enabled, but that's not the only use case. However, this $final_settings = $html_response->getAttachments()['drupalSettings']; $cumulative_assets->setAlreadyLoadedLibraries(explode(',', $final_settings['ajaxPageState']['libraries'])); $cumulative_assets->setSettings($final_settings); } }
/** * Sets headers on a response object. * * @param \Drupal\Core\Render\HtmlResponse $response * The HTML response to update. * @param array $headers * The headers to set. */ protected function setHeaders(HtmlResponse $response, array $headers) { foreach ($headers as $name => $value) { // Drupal treats the HTTP response status code like a header, even though // it really is not. if ($name === 'status') { $response->setStatusCode($value); } $response->headers->set($name, $value, FALSE); } }
/** * Sends no-JS BigPipe placeholders' replacements as embedded HTML responses. * * @param string $html * HTML markup. * @param array $no_js_placeholders * Associative array; the no-JS BigPipe placeholders. Keys are the BigPipe * selectors. * @param \Drupal\Core\Asset\AttachedAssetsInterface $cumulative_assets * The cumulative assets sent so far; to be updated while rendering no-JS * BigPipe placeholders. */ protected function sendNoJsPlaceholders($html, $no_js_placeholders, AttachedAssetsInterface $cumulative_assets) { // Split the HTML on every no-JS placeholder string. $prepare_for_preg_split = function ($placeholder_string) { return '(' . preg_quote($placeholder_string, '/') . ')'; }; $preg_placeholder_strings = array_map($prepare_for_preg_split, array_keys($no_js_placeholders)); $fragments = preg_split('/' . implode('|', $preg_placeholder_strings) . '/', $html, NULL, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); foreach ($fragments as $fragment) { // If the fragment isn't one of the no-JS placeholders, it is the HTML in // between placeholders and it must be printed & flushed immediately. The // rest of the logic in the loop handles the placeholders. if (!isset($no_js_placeholders[$fragment])) { print $fragment; flush(); continue; } $placeholder = $fragment; assert('isset($no_js_placeholders[$placeholder])'); $token = Crypt::randomBytesBase64(55); // Render the placeholder, but include the cumulative settings assets, so // we can calculate the overall settings for the entire page. $placeholder_plus_cumulative_settings = ['placeholder' => $no_js_placeholders[$placeholder], 'cumulative_settings_' . $token => ['#attached' => ['drupalSettings' => $cumulative_assets->getSettings()]]]; $elements = $this->renderPlaceholder($placeholder, $placeholder_plus_cumulative_settings); // Create a new HtmlResponse. Ensure the CSS and (non-bottom) JS is sent // before the HTML they're associated with. In other words: ensure the // critical assets for this placeholder's markup are loaded first. // @see \Drupal\Core\Render\HtmlResponseSubscriber // @see template_preprocess_html() $css_placeholder = '<nojs-bigpipe-placeholder-styles-placeholder token="' . $token . '">'; $js_placeholder = '<nojs-bigpipe-placeholder-scripts-placeholder token="' . $token . '">'; $elements['#markup'] = Markup::create($css_placeholder . $js_placeholder . (string) $elements['#markup']); $elements['#attached']['html_response_attachment_placeholders']['styles'] = $css_placeholder; $elements['#attached']['html_response_attachment_placeholders']['scripts'] = $js_placeholder; $html_response = new HtmlResponse(); $html_response->setContent($elements); $html_response->getCacheableMetadata()->setCacheMaxAge(0); // Push a fake request with the asset libraries loaded so far and dispatch // KernelEvents::RESPONSE event. This results in the attachments for the // HTML response being processed by HtmlResponseAttachmentsProcessor and // hence: // - the HTML to load the CSS can be rendered. // - the HTML to load the JS (at the top) can be rendered. $fake_request = $this->requestStack->getMasterRequest()->duplicate(); $fake_request->request->set('ajax_page_state', ['libraries' => implode(',', $cumulative_assets->getAlreadyLoadedLibraries())]); $this->requestStack->push($fake_request); $event = new FilterResponseEvent($this->httpKernel, $fake_request, HttpKernelInterface::SUB_REQUEST, $html_response); $this->eventDispatcher->dispatch(KernelEvents::RESPONSE, $event); $html_response = $event->getResponse(); $this->requestStack->pop(); // Send this embedded HTML response. print $html_response->getContent(); flush(); // Another placeholder was rendered and sent, track the set of asset // libraries sent so far. Any new settings also need to be tracked, so // they can be sent in ::sendPreBody(). // @todo What if drupalSettings already was printed in the HTML <head>? That case is not yet handled. In that case, no-JS BigPipe would cause broken (incomplete) drupalSettingsā¦ This would not matter if it were only used if JS is not enabled, but that's not the only use case. However, this $cumulative_assets->setAlreadyLoadedLibraries(array_merge($cumulative_assets->getAlreadyLoadedLibraries(), $html_response->getAttachments()['library'])); $cumulative_assets->setSettings($html_response->getAttachments()['drupalSettings']); } }