/**
  * {@inheritdoc}
  */
 public function createPlaceholder(array $element)
 {
     $placeholder_render_array = array_intersect_key($element, ['#lazy_builder' => TRUE, '#cache' => TRUE]);
     // Generate placeholder markup. Note that the only requirement is that this
     // is unique markup that isn't easily guessable. The #lazy_builder callback
     // and its arguments are put in the placeholder markup solely to simplify<<<
     // debugging.
     $callback = $placeholder_render_array['#lazy_builder'][0];
     $arguments = UrlHelper::buildQuery($placeholder_render_array['#lazy_builder'][1]);
     $token = hash('crc32b', serialize($placeholder_render_array));
     $placeholder_markup = '<drupal-render-placeholder callback="' . Html::escape($callback) . '" arguments="' . Html::escape($arguments) . '" token="' . Html::escape($token) . '"></drupal-render-placeholder>';
     // Build the placeholder element to return.
     $placeholder_element = [];
     $placeholder_element['#markup'] = Markup::create($placeholder_markup);
     $placeholder_element['#attached']['placeholders'][$placeholder_markup] = $placeholder_render_array;
     return $placeholder_element;
 }
 /**
  * Renders placeholders (#attached['placeholders']).
  *
  * First, the HTML response object is converted to an equivalent render array,
  * with #markup being set to the response's content and #attached being set to
  * the response's attachments. Among these attachments, there may be
  * placeholders that need to be rendered (replaced).
  *
  * Next, RendererInterface::renderRoot() is called, which renders the
  * placeholders into their final markup.
  *
  * The markup that results from RendererInterface::renderRoot() is now the
  * original HTML response's content, but with the placeholders rendered. We
  * overwrite the existing content in the original HTML response object with
  * this markup. The markup that was rendered for the placeholders may also
  * have attachments (e.g. for CSS/JS assets) itself, and cacheability metadata
  * that indicates what that markup depends on. That metadata is also added to
  * the HTML response object.
  *
  * @param \Drupal\Core\Render\HtmlResponse $response
  *   The HTML response whose placeholders are being replaced.
  *
  * @return \Drupal\Core\Render\HtmlResponse
  *   The updated HTML response, with replaced placeholders.
  *
  * @see \Drupal\Core\Render\Renderer::replacePlaceholders()
  * @see \Drupal\Core\Render\Renderer::renderPlaceholder()
  */
 protected function renderPlaceholders(HtmlResponse $response)
 {
     $build = ['#markup' => Markup::create($response->getContent()), '#attached' => $response->getAttachments()];
     // RendererInterface::renderRoot() renders the $build render array and
     // updates it in place. We don't care about the return value (which is just
     // $build['#markup']), but about the resulting render array.
     // @todo Simplify this when https://www.drupal.org/node/2495001 lands.
     $this->renderer->renderRoot($build);
     // Update the Response object now that the placeholders have been rendered.
     $placeholders_bubbleable_metadata = BubbleableMetadata::createFromRenderArray($build);
     $response->setContent($build['#markup'])->addCacheableDependency($placeholders_bubbleable_metadata)->setAttachments($placeholders_bubbleable_metadata->getAttachments());
     return $response;
 }
 /**
  * {@inheritdoc}
  */
 public function getCacheableRenderArray(array $elements)
 {
     $data = ['#markup' => $elements['#markup'], '#attached' => $elements['#attached'], '#cache' => ['contexts' => $elements['#cache']['contexts'], 'tags' => $elements['#cache']['tags'], 'max-age' => $elements['#cache']['max-age']]];
     // Preserve cacheable items if specified. If we are preserving any cacheable
     // children of the element, we assume we are only interested in their
     // individual markup and not the parent's one, thus we empty it to minimize
     // the cache entry size.
     if (!empty($elements['#cache_properties']) && is_array($elements['#cache_properties'])) {
         $data['#cache_properties'] = $elements['#cache_properties'];
         // Extract all the cacheable items from the element using cache
         // properties.
         $cacheable_items = array_intersect_key($elements, array_flip($elements['#cache_properties']));
         $cacheable_children = Element::children($cacheable_items);
         if ($cacheable_children) {
             $data['#markup'] = '';
             // Cache only cacheable children's markup.
             foreach ($cacheable_children as $key) {
                 // We can assume that #markup is safe at this point.
                 $cacheable_items[$key] = ['#markup' => Markup::create($cacheable_items[$key]['#markup'])];
             }
         }
         $data += $cacheable_items;
     }
     $data['#markup'] = Markup::create($data['#markup']);
     return $data;
 }
Exemple #4
0
 /**
  * Escapes #plain_text or filters #markup as required.
  *
  * Drupal uses Twig's auto-escape feature to improve security. This feature
  * automatically escapes any HTML that is not known to be safe. Due to this
  * the render system needs to ensure that all markup it generates is marked
  * safe so that Twig does not do any additional escaping.
  *
  * By default all #markup is filtered to protect against XSS using the admin
  * tag list. Render arrays can alter the list of tags allowed by the filter
  * using the #allowed_tags property. This value should be an array of tags
  * that Xss::filter() would accept. Render arrays can escape text instead
  * of XSS filtering by setting the #plain_text property instead of #markup. If
  * #plain_text is used #allowed_tags is ignored.
  *
  * @param array $elements
  *   A render array with #markup set.
  *
  * @return \Drupal\Component\Render\MarkupInterface|string
  *   The escaped markup wrapped in a Markup object. If
  *   SafeMarkup::isSafe($elements['#markup']) returns TRUE, it won't be
  *   escaped or filtered again.
  *
  * @see \Drupal\Component\Utility\Html::escape()
  * @see \Drupal\Component\Utility\Xss::filter()
  * @see \Drupal\Component\Utility\Xss::adminFilter()
  */
 protected function ensureMarkupIsSafe(array $elements)
 {
     if (empty($elements['#markup']) && empty($elements['#plain_text'])) {
         return $elements;
     }
     if (!empty($elements['#plain_text'])) {
         $elements['#markup'] = Markup::create(Html::escape($elements['#plain_text']));
     } elseif (!SafeMarkup::isSafe($elements['#markup'])) {
         // The default behaviour is to XSS filter using the admin tag list.
         $tags = isset($elements['#allowed_tags']) ? $elements['#allowed_tags'] : Xss::getAdminTagList();
         $elements['#markup'] = Markup::create(Xss::filter($elements['#markup'], $tags));
     }
     return $elements;
 }