/** * {@inheritdoc} */ public function render(ResultRow $values) { $value = $this->getValue($values); if (!empty($this->options['not'])) { $value = !$value; } if ($this->options['type'] == 'custom') { $custom_value = $value ? $this->options['type_custom_true'] : $this->options['type_custom_false']; return ViewsRenderPipelineMarkup::create(UtilityXss::filterAdmin($custom_value)); } elseif (isset($this->formats[$this->options['type']])) { return $value ? $this->formats[$this->options['type']][0] : $this->formats[$this->options['type']][1]; } else { return $value ? $this->formats['yes-no'][0] : $this->formats['yes-no'][1]; } }
/** * Renders all of the fields for a given style and store them on the object. * * @param array $result * The result array from $view->result */ protected function renderFields(array $result) { if (!$this->usesFields()) { return; } if (!isset($this->rendered_fields)) { $this->rendered_fields = []; $this->view->row_index = 0; $field_ids = array_keys($this->view->field); // Only tokens relating to field handlers preceding the one we invoke // ::getRenderTokens() on are returned, so here we need to pick the last // available field handler. $render_tokens_field_id = end($field_ids); // If all fields have a field::access FALSE there might be no fields, so // there is no reason to execute this code. if (!empty($field_ids)) { $renderer = $this->getRenderer(); /** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache_plugin */ $cache_plugin = $this->view->display_handler->getPlugin('cache'); /** @var \Drupal\views\ResultRow $row */ foreach ($result as $index => $row) { $this->view->row_index = $index; // Here we implement render caching for result rows. Since we never // build a render array for single rows, given that style templates // need individual field markup to support proper theming, we build // a raw render array containing all field render arrays and cache it. // This allows us to cache the markup of the various children, that is // individual fields, which is then available for style template // preprocess functions, later in the rendering workflow. // @todo Fetch all the available cached row items in one single cache // get operation, once https://www.drupal.org/node/2453945 is fixed. $data = ['#pre_render' => [[$this, 'elementPreRenderRow']], '#row' => $row, '#cache' => ['keys' => $cache_plugin->getRowCacheKeys($row), 'tags' => $cache_plugin->getRowCacheTags($row)], '#cache_properties' => $field_ids]; $renderer->addCacheableDependency($data, $this->view->storage); // Views may be rendered both inside and outside a render context: // - HTML views are rendered inside a render context: then we want to // use ::render(), so that attachments and cacheability are bubbled. // - non-HTML views are rendered outside a render context: then we // want to use ::renderPlain(), so that no bubbling happens if ($renderer->hasRenderContext()) { $renderer->render($data); } else { $renderer->renderPlain($data); } // Extract field output from the render array and post process it. $fields = $this->view->field; $rendered_fields =& $this->rendered_fields[$index]; $post_render_tokens = []; foreach ($field_ids as $id) { $rendered_fields[$id] = $data[$id]['#markup']; $tokens = $fields[$id]->postRender($row, $rendered_fields[$id]); if ($tokens) { $post_render_tokens += $tokens; } } // Populate row tokens. $this->rowTokens[$index] = $this->view->field[$render_tokens_field_id]->getRenderTokens([]); // Replace post-render tokens. if ($post_render_tokens) { $placeholders = array_keys($post_render_tokens); $values = array_values($post_render_tokens); foreach ($this->rendered_fields[$index] as &$rendered_field) { // Placeholders and rendered fields have been processed by the // render system and are therefore safe. $rendered_field = ViewsRenderPipelineMarkup::create(str_replace($placeholders, $values, $rendered_field)); } } } } unset($this->view->row_index); } }
/** * {@inheritdoc} */ public function sanitizeValue($value, $type = NULL) { switch ($type) { case 'xss': $value = Xss::filter($value); break; case 'xss_admin': $value = Xss::filterAdmin($value); break; case 'url': $value = Html::escape(UrlHelper::stripDangerousProtocols($value)); break; default: $value = Html::escape($value); break; } return ViewsRenderPipelineMarkup::create($value); }
/** * {@inheritdoc} */ public function renderText($alter) { // We need to preserve the safeness of the value regardless of the // alterations made by this method. Any alterations or replacements made // within this method need to ensure that at the minimum the result is // XSS admin filtered. See self::renderAltered() as an example that does. $value_is_safe = $this->last_render instanceof MarkupInterface; // Cast to a string so that empty checks and string functions work as // expected. $value = (string) $this->last_render; if (!empty($alter['alter_text']) && $alter['text'] !== '') { $tokens = $this->getRenderTokens($alter); $value = $this->renderAltered($alter, $tokens); } if (!empty($this->options['alter']['trim_whitespace'])) { $value = trim($value); } // Check if there should be no further rewrite for empty values. $no_rewrite_for_empty = $this->options['hide_alter_empty'] && $this->isValueEmpty($this->original_value, $this->options['empty_zero']); // Check whether the value is empty and return nothing, so the field isn't rendered. // First check whether the field should be hidden if the value(hide_alter_empty = TRUE) /the rewrite is empty (hide_alter_empty = FALSE). // For numeric values you can specify whether "0"/0 should be empty. if ((($this->options['hide_empty'] && empty($value)) || ($alter['phase'] != static::RENDER_TEXT_PHASE_EMPTY && $no_rewrite_for_empty)) && $this->isValueEmpty($value, $this->options['empty_zero'], FALSE)) { return ''; } // Only in empty phase. if ($alter['phase'] == static::RENDER_TEXT_PHASE_EMPTY && $no_rewrite_for_empty) { // If we got here then $alter contains the value of "No results text" // and so there is nothing left to do. return ViewsRenderPipelineMarkup::create($value); } if (!empty($alter['strip_tags'])) { $value = strip_tags($value, $alter['preserve_tags']); } $more_link = ''; if (!empty($alter['trim']) && !empty($alter['max_length'])) { $length = strlen($value); $value = $this->renderTrimText($alter, $value); if ($this->options['alter']['more_link'] && strlen($value) < $length) { $tokens = $this->getRenderTokens($alter); $more_link_text = $this->options['alter']['more_link_text'] ? $this->options['alter']['more_link_text'] : $this->t('more'); $more_link_text = strtr(Xss::filterAdmin($more_link_text), $tokens); $more_link_path = $this->options['alter']['more_link_path']; $more_link_path = strip_tags(Html::decodeEntities($this->viewsTokenReplace($more_link_path, $tokens))); // Make sure that paths which were run through URL generation work as // well. $base_path = base_path(); // Checks whether the path starts with the base_path. if (strpos($more_link_path, $base_path) === 0) { $more_link_path = Unicode::substr($more_link_path, Unicode::strlen($base_path)); } // @todo Views should expect and store a leading /. See // https://www.drupal.org/node/2423913. $options = array( 'attributes' => array( 'class' => array( 'views-more-link', ), ), ); if (UrlHelper::isExternal($more_link_path)) { $more_link_url = CoreUrl::fromUri($more_link_path, $options); } else { $more_link_url = CoreUrl::fromUserInput('/' . $more_link_path, $options); } $more_link = ' ' . $this->linkGenerator()->generate($more_link_text, $more_link_url); } } if (!empty($alter['nl2br'])) { $value = nl2br($value); } if ($value_is_safe) { $value = ViewsRenderPipelineMarkup::create($value); } $this->last_render_text = $value; if (!empty($alter['make_link']) && (!empty($alter['path']) || !empty($alter['url']))) { if (!isset($tokens)) { $tokens = $this->getRenderTokens($alter); } $value = $this->renderAsLink($alter, $value, $tokens); } // Preserve whether or not the string is safe. Since $more_link comes from // \Drupal::l(), it is safe to append. Check if the value is an instance of // \Drupal\Component\Render\MarkupInterface here because renderAsLink() // can return both safe and unsafe values. if ($value instanceof MarkupInterface) { return ViewsRenderPipelineMarkup::create($value . $more_link); } else { // If the string is not already marked safe, it is still OK to return it // because it will be sanitized by Twig. return $value . $more_link; } }
/** * {@inheritdoc} */ public function render() { $build = array(); $build['#markup'] = $this->renderer->executeInRenderContext(new RenderContext(), function () { return $this->view->style_plugin->render(); }); $this->view->element['#content_type'] = $this->getMimeType(); $this->view->element['#cache_properties'][] = '#content_type'; // Encode and wrap the output in a pre tag if this is for a live preview. if (!empty($this->view->live_preview)) { $build['#prefix'] = '<pre>'; $build['#plain_text'] = $build['#markup']; $build['#suffix'] = '</pre>'; unset($build['#markup']); } elseif ($this->view->getRequest()->getFormat($this->view->element['#content_type']) !== 'html') { // This display plugin is primarily for returning non-HTML formats. // However, we still invoke the renderer to collect cacheability metadata. // Because the renderer is designed for HTML rendering, it filters // #markup for XSS unless it is already known to be safe, but that filter // only works for HTML. Therefore, we mark the contents as safe to bypass // the filter. So long as we are returning this in a non-HTML response // (checked above), this is safe, because an XSS attack only works when // executed by an HTML agent. // @todo Decide how to support non-HTML in the render API in // https://www.drupal.org/node/2501313. $build['#markup'] = ViewsRenderPipelineMarkup::create($build['#markup']); } parent::applyDisplayCachablityMetadata($build); return $build; }
/** * {@inheritdoc} */ public function render(ResultRow $values) { // Return the text, so the code never thinks the value is empty. return ViewsRenderPipelineMarkup::create(Xss::filterAdmin($this->options['alter']['text'])); }
/** * {@inheritdoc} */ public function render(ResultRow $values) { return ViewsRenderPipelineMarkup::create('<!--form-item-' . $this->options['id'] . '--' . $values->index . '-->'); }