Update to Drupal 8.0.0 beta 14. For more information, see https://drupal.org/node/2544542

This commit is contained in:
Pantheon Automation 2015-08-27 12:03:05 -07:00 committed by Greg Anderson
parent 3b2511d96d
commit 81ccda77eb
2155 changed files with 54307 additions and 46870 deletions

View file

@ -117,8 +117,8 @@ views.field.numeric:
type: boolean
label: 'Format plural'
format_plural_string:
type: label
label: 'Singular and one or more plurals'
type: plural_label
label: 'Plural variants'
prefix:
type: label
label: 'Prefix'

View file

@ -108,14 +108,15 @@
* @method
*/
Drupal.views.ajaxView.prototype.attachExposedFormAjax = function () {
var button = $('input[type=submit], input[type=image]', this.$exposed_form);
button = button[0];
var self_settings = $.extend({}, this.element_settings, {
base: $(button).attr('id'),
element: button
var that = this;
this.exposedFormAjax = [];
$('input[type=submit], input[type=image]', this.$exposed_form).each(function (index) {
var self_settings = $.extend({}, that.element_settings, {
base: $(this).attr('id'),
element: this
});
that.exposedFormAjax[index] = Drupal.ajax(self_settings);
});
this.exposedFormAjax = Drupal.ajax(self_settings);
};
/**

View file

@ -14,6 +14,8 @@ use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\EventSubscriber\AjaxResponseSubscriber;
use Drupal\Core\Path\CurrentPathStack;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\RenderContext;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\Routing\RedirectDestinationInterface;
use Drupal\views\Ajax\ScrollTopCommand;
@ -174,9 +176,18 @@ class ViewAjaxController implements ContainerInjectionInterface {
// Reuse the same DOM id so it matches that in drupalSettings.
$view->dom_id = $dom_id;
if ($preview = $view->preview($display_id, $args)) {
$response->addCommand(new ReplaceCommand(".js-view-dom-id-$dom_id", $preview));
$context = new RenderContext();
$preview = $this->renderer->executeInRenderContext($context, function() use ($view, $display_id, $args) {
return $view->preview($display_id, $args);
});
if (!$context->isEmpty()) {
$bubbleable_metadata = $context->pop();
BubbleableMetadata::createFromRenderArray($preview)
->merge($bubbleable_metadata)
->applyTo($preview);
}
$response->addCommand(new ReplaceCommand(".js-view-dom-id-$dom_id", $preview));
return $response;
}
else {

View file

@ -138,13 +138,18 @@ class EntityFieldRenderer extends RendererBase {
$build = $this->build[$row->index][$field_id];
unset($this->build[$row->index][$field_id]);
}
else {
elseif (isset($this->build[$row->index])) {
// In the uncommon case where a field gets rendered several times
// (typically through direct Views API calls), the pre-computed render
// array was removed by the unset() above. We have to manually rebuild
// the render array for the row.
$build = $this->buildFields([$row])[$row->index][$field_id];
}
else {
// In case the relationship is optional, there might not be any fields
// to render for this row.
$build = [];
}
}
else {
// Same logic as above, in the case where we are being called for a whole
@ -199,8 +204,9 @@ class EntityFieldRenderer extends RendererBase {
$entities_by_bundles = [];
$field = $this->view->field[current($field_ids)];
foreach ($values as $result_row) {
$entity = $field->getEntity($result_row);
$entities_by_bundles[$entity->bundle()][$result_row->index] = $this->getEntityTranslation($entity, $result_row);
if ($entity = $field->getEntity($result_row)) {
$entities_by_bundles[$entity->bundle()][$result_row->index] = $this->getEntityTranslation($entity, $result_row);
}
}
// Determine unique sets of fields that can be processed by the same

View file

@ -7,8 +7,9 @@
namespace Drupal\views\Plugin\Block;
use Drupal\Core\Config\Entity\Query\Query;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Config\Entity\Query\Query;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
@ -32,7 +33,8 @@ class ViewsBlock extends ViewsBlockBase {
if ($output = $this->view->buildRenderable($this->displayID, [], FALSE)) {
// Override the label to the dynamic title configured in the view.
if (empty($this->configuration['views_label']) && $this->view->getTitle()) {
$output['#title'] = Xss::filterAdmin($this->view->getTitle());
// @todo https://www.drupal.org/node/2527360 remove call to SafeMarkup.
$output['#title'] = SafeMarkup::xssFilter($this->view->getTitle(), Xss::getAdminTagList());
}
// Before returning the block output, convert it to a renderable array

View file

@ -9,6 +9,7 @@ namespace Drupal\views\Plugin\Derivative;
use Drupal\Component\Plugin\Derivative\DeriverBase;
use Drupal\Core\Plugin\Discovery\ContainerDeriverInterface;
use Drupal\views\Plugin\views\display\DisplayMenuInterface;
use Drupal\views\Views;
use Drupal\Core\Entity\EntityStorageInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
@ -57,8 +58,10 @@ class ViewsMenuLink extends DeriverBase implements ContainerDeriverInterface {
list($view_id, $display_id) = $data;
/** @var \Drupal\views\ViewExecutable $executable */
$executable = $this->viewStorage->load($view_id)->getExecutable();
$executable->initDisplay();
$display = $executable->displayHandlers->get($display_id);
if ($result = $executable->getMenuLinks($display_id)) {
if (($display instanceof DisplayMenuInterface) && ($result = $display->getMenuLinks())) {
foreach ($result as $link_id => $link) {
$links[$link_id] = $link + $base_plugin_definition;
}

View file

@ -721,7 +721,7 @@ abstract class HandlerBase extends PluginBase implements ViewsHandlerInterface {
return $views_data['table']['entity type'];
}
else {
throw new \Exception(SafeMarkup::format('No entity type for field @field on view @view', array('@field' => $this->options['id'], '@view' => $this->view->storage->id())));
throw new \Exception("No entity type for field {$this->options['id']} on view {$this->view->storage->id()}");
}
}

View file

@ -9,6 +9,7 @@ namespace Drupal\views\Plugin\views;
use Drupal\Component\Plugin\DependentPluginInterface;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
@ -333,23 +334,27 @@ abstract class PluginBase extends ComponentPluginBase implements ContainerFactor
}
/**
* Replaces Views' tokens in a given string. It is the responsibility of the
* calling function to ensure $text and $token replacements are sanitized.
* Replaces Views' tokens in a given string. The resulting string will be
* sanitized with Xss::filterAdmin.
*
* This used to be a simple strtr() scattered throughout the code. Some Views
* tokens, such as arguments (e.g.: %1 or !1), still use the old format so we
* handle those as well as the new Twig-based tokens (e.g.: {{ field_name }})
*
* @param $text
* String with possible tokens.
* Unsanitized string with possible tokens.
* @param $tokens
* Array of token => replacement_value items.
*
* @return String
*/
protected function viewsTokenReplace($text, $tokens) {
if (!strlen($text)) {
// No need to run filterAdmin on an empty string.
return '';
}
if (empty($tokens)) {
return $text;
return Xss::filterAdmin($text);
}
// Separate Twig tokens from other tokens (e.g.: contextual filter tokens in
@ -370,14 +375,22 @@ abstract class PluginBase extends ComponentPluginBase implements ContainerFactor
// Non-Twig tokens are a straight string replacement, Twig tokens get run
// through an inline template for rendering and replacement.
$text = strtr($text, $other_tokens);
if ($twig_tokens && !empty($text)) {
if ($twig_tokens) {
// Use the unfiltered text for the Twig template, then filter the output.
// Otherwise, Xss::filterAdmin could remove valid Twig syntax before the
// template is parsed.
$build = array(
'#type' => 'inline_template',
'#template' => $text,
'#context' => $twig_tokens,
'#post_render' => [
function ($children, $elements) {
return Xss::filterAdmin($children);
}
],
);
return $this->getRenderer()->render($build);
return (string) $this->getRenderer()->render($build);
}
else {
return $text;

View file

@ -153,7 +153,7 @@ abstract class CachePluginBase extends PluginBase {
* Clear out cached data for a view.
*/
public function cacheFlush() {
Cache::invalidateTags($this->view->storage->getCacheTags());
Cache::invalidateTags($this->view->storage->getCacheTagsToInvalidate());
}
/**
@ -211,7 +211,7 @@ abstract class CachePluginBase extends PluginBase {
'items_per_page' => $this->view->getItemsPerPage(),
'offset' => $this->view->getOffset(),
];
$key_data += \Drupal::service('cache_contexts_manager')->convertTokensToKeys($this->displayHandler->getCacheMetadata()['contexts']);
$key_data += \Drupal::service('cache_contexts_manager')->convertTokensToKeys($this->displayHandler->getCacheMetadata()['contexts'])->getKeys();
$this->resultsKey = $this->view->storage->id() . ':' . $this->displayHandler->display['id'] . ':results:' . hash('sha256', serialize($key_data));
}

View file

@ -0,0 +1,25 @@
<?php
/**
* @file
* Contains \Drupal\views\Plugin\views\display\DisplayMenuInterface.
*/
namespace Drupal\views\Plugin\views\display;
/**
* Defines an interface for displays that provide menu links.
*/
interface DisplayMenuInterface {
/**
* Gets menu links, if this display provides some.
*
* @return array
* The menu links registers for this display.
*
* @see \Drupal\views\Plugin\Derivative\ViewsMenuLink
*/
public function getMenuLinks();
}

View file

@ -2112,13 +2112,6 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte
}
}
/**
* {@inheritdoc}
*/
public function getMenuLinks() {
return array();
}
/**
* {@inheritdoc}
*/
@ -2472,18 +2465,6 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte
public function newDisplay() {
}
/**
* {@inheritdoc}
*/
public function remove() {
$menu_links = $this->getMenuLinks();
/** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
foreach ($menu_links as $menu_link_id => $menu_link) {
$menu_link_manager->removeDefinition("views_view:$menu_link_id");
}
}
/**
* {@inheritdoc}
*/
@ -2611,6 +2592,13 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte
}
}
/**
* {@inheritdoc}
*/
public function remove() {
}
/**
* Merges plugins default values.
*

View file

@ -381,16 +381,6 @@ interface DisplayPluginInterface {
*/
public function renderMoreLink();
/**
* Gets menu links, if this display provides some.
*
* @return array
* The menu links registers for this display.
*
* @see \Drupal\views\Plugin\Derivative\ViewsMenuLink
*/
public function getMenuLinks();
/**
* Renders this display.
*/

View file

@ -71,4 +71,5 @@ interface DisplayRouterInterface extends DisplayPluginInterface {
* @see \Drupal\views\Plugin\views\display\DisplayRouterInterface::alterRoutes()
*/
public function getAlteredRouteNames();
}

View file

@ -67,7 +67,7 @@ class Feed extends PathPluginBase implements ResponseDisplayPluginInterface {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
$output = $renderer->renderRoot($build);
$output = (string) $renderer->renderRoot($build);
if (empty($output)) {
throw new NotFoundHttpException();

View file

@ -7,6 +7,7 @@
namespace Drupal\views\Plugin\views\display;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Form\FormStateInterface;
@ -181,7 +182,8 @@ class Page extends PathPluginBase {
// it should be dropped.
if (is_array($render)) {
$render += array(
'#title' => Xss::filterAdmin($this->view->getTitle()),
// @todo https://www.drupal.org/node/2527360 remove call to SafeMarkup.
'#title' => SafeMarkup::xssFilter($this->view->getTitle(), Xss::getAdminTagList()),
);
}
return $render;

View file

@ -29,7 +29,7 @@ use Symfony\Component\Routing\RouteCollection;
*
* @see \Drupal\views\EventSubscriber\RouteSubscriber
*/
abstract class PathPluginBase extends DisplayPluginBase implements DisplayRouterInterface {
abstract class PathPluginBase extends DisplayPluginBase implements DisplayRouterInterface, DisplayMenuInterface {
use UrlGeneratorTrait;
@ -521,4 +521,16 @@ abstract class PathPluginBase extends DisplayPluginBase implements DisplayRouter
return $this->state->get('views.view_route_names') ?: array();
}
/**
* {@inheritdoc}
*/
public function remove() {
$menu_links = $this->getMenuLinks();
/** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
foreach ($menu_links as $menu_link_id => $menu_link) {
$menu_link_manager->removeDefinition("views_view:$menu_link_id");
}
}
}

View file

@ -321,7 +321,7 @@ abstract class ExposedFormPluginBase extends PluginBase implements CacheablePlug
}
// Set the form to allow redirect.
if (empty($this->view->live_preview)) {
if (empty($this->view->live_preview) && !\Drupal::request()->isXmlHttpRequest()) {
$form_state->disableRedirect(FALSE);
}
else {

View file

@ -7,7 +7,6 @@
namespace Drupal\views\Plugin\views\field;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Xss as CoreXss;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManagerInterface;
@ -671,7 +670,7 @@ class Field extends FieldPluginBase implements CacheablePluginInterface, MultiIt
if (!empty($items)) {
$items = $this->prepareItemsByDelta($items);
if ($this->options['multi_type'] == 'separator' || !$this->options['group_rows']) {
$separator = $this->options['multi_type'] == 'separator' ? SafeMarkup::checkAdminXss($this->options['separator']) : '';
$separator = $this->options['multi_type'] == 'separator' ? CoreXss::filterAdmin($this->options['separator']) : '';
$build = [
'#type' => 'inline_template',
'#template' => '{{ items | safe_join(separator) }}',

View file

@ -16,6 +16,7 @@ use Drupal\Component\Utility\Xss;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Renderer;
use Drupal\Core\Render\SafeString;
use Drupal\Core\Url as CoreUrl;
use Drupal\views\Plugin\views\HandlerBase;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
@ -394,7 +395,7 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf
if ($relationship_id == 'none') {
return $values->_entity;
}
else {
elseif (isset($values->_relationship_entities[$relationship_id])) {
return $values->_relationship_entities[$relationship_id];
}
}
@ -875,10 +876,16 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf
$this->documentSelfTokens($options[t('Fields')]);
// Default text.
$output = '<p>' . $this->t('You must add some additional fields to this display before using this field. These fields may be marked as <em>Exclude from display</em> if you prefer. Note that due to rendering order, you cannot use fields that come after this field; if you need a field not listed here, rearrange your fields.') . '</p>';
$output = [];
$output[] = [
'#markup' => '<p>' . $this->t('You must add some additional fields to this display before using this field. These fields may be marked as <em>Exclude from display</em> if you prefer. Note that due to rendering order, you cannot use fields that come after this field; if you need a field not listed here, rearrange your fields.') . '</p>',
];
// We have some options, so make a list.
if (!empty($options)) {
$output = '<p>' . $this->t("The following replacement tokens are available for this field. Note that due to rendering order, you cannot use fields that come after this field; if you need a field not listed here, rearrange your fields.") . '</p>';
$output[] = [
'#markup' => '<p>' . $this->t("The following replacement tokens are available for this field. Note that due to rendering order, you cannot use fields that come after this field; if you need a field not listed here, rearrange your fields.") . '</p>',
];
foreach (array_keys($options) as $type) {
if (!empty($options[$type])) {
$items = array();
@ -890,7 +897,7 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf
'#items' => $items,
'#list_type' => $type,
);
$output .= $this->getRenderer()->render($item_list);
$output[] = $item_list;
}
}
}
@ -901,7 +908,7 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf
$form['alter']['help'] = array(
'#type' => 'details',
'#title' => $this->t('Replacement patterns'),
'#value' => SafeMarkup::set($output),
'#value' => $output,
'#states' => array(
'visible' => array(
array(
@ -1131,7 +1138,7 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf
else {
$value = $this->render($values);
if (is_array($value)) {
$value = $this->getRenderer()->render($value);
$value = (string) $this->getRenderer()->render($value);
}
$this->last_render = $value;
$this->original_value = $value;
@ -1144,7 +1151,7 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf
foreach ($raw_items as $count => $item) {
$value = $this->render_item($count, $item);
if (is_array($value)) {
$value = $this->getRenderer()->render($value);
$value = (string) $this->getRenderer()->render($value);
}
$this->last_render = $value;
$this->original_value = $this->last_render;
@ -1162,7 +1169,7 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf
}
if (is_array($value)) {
$value = $this->getRenderer()->render($value);
$value = (string) $this->getRenderer()->render($value);
}
// This happens here so that renderAsLink can get the unaltered value of
// this field as a token rather than the altered value.
@ -1284,9 +1291,7 @@ abstract class FieldPluginBase extends HandlerBase implements FieldHandlerInterf
* Render this field as user-defined altered text.
*/
protected function renderAltered($alter, $tokens) {
// Filter this right away as our substitutions are already sanitized.
$template = Xss::filterAdmin($alter['text']);
return $this->viewsTokenReplace($template, $tokens);
return SafeString::create($this->viewsTokenReplace($alter['text'], $tokens));
}
/**

View file

@ -102,6 +102,7 @@ class NumericField extends FieldPluginBase {
for ($i = 0; $i < $plurals; $i++) {
$form['format_plural_values'][$i] = array(
'#type' => 'textfield',
// @todo Should use better labels https://www.drupal.org/node/2499639
'#title' => ($i == 0 ? $this->t('Singular form') : $this->formatPlural($i, 'First plural form', '@count. plural form')),
'#default_value' => isset($plural_array[$i]) ? $plural_array[$i] : '',
'#description' => $this->t('Text to use for this variant, @count will be replaced with the value.'),

View file

@ -96,6 +96,7 @@ class Full extends SqlBase {
'#element' => $this->options['id'],
'#parameters' => $input,
'#quantity' => $this->options['quantity'],
'#route_name' => !empty($this->view->live_preview) ? '<current>' : '<none>',
);
}

View file

@ -103,6 +103,7 @@ class Mini extends SqlBase {
'#tags' => $tags,
'#element' => $this->options['id'],
'#parameters' => $input,
'#route_name' => !empty($this->view->live_preview) ? '<current>' : '<none>',
);
}

View file

@ -382,14 +382,9 @@ abstract class SqlBase extends PagerPluginBase implements CacheablePluginInterfa
* {@inheritdoc}
*/
public function getCacheContexts() {
$contexts = ['url.query_args.pagers:' . $this->options['id']];
if ($this->options['expose']['items_per_page']) {
$contexts[] = 'url.query_args:items_per_page';
}
if ($this->options['expose']['offset']) {
$contexts[] = 'url.query_args:offset';
}
return $contexts;
// The rendered link needs to play well with any other query parameter used
// on the page, like other pagers and exposed filter.
return ['url.query_args'];
}
}

View file

@ -237,12 +237,15 @@ abstract class QueryPluginBase extends PluginBase implements CacheablePluginInte
* An appropriate query expression pointing to the date field.
* @param string $format
* A format string for the result, like 'Y-m-d H:i:s'.
* @param boolean $string_date
* For certain databases, date format functions vary depending on string or
* numeric storage.
*
* @return string
* A string representing the field formatted as a date in the format
* specified by $format.
*/
public function getDateFormat($field, $format) {
public function getDateFormat($field, $format, $string_date = FALSE) {
return $field;
}

View file

@ -1454,7 +1454,7 @@ class Sql extends QueryPluginBase {
drupal_set_message($e->getMessage(), 'error');
}
else {
throw new DatabaseExceptionWrapper(format_string('Exception in @label[@view_name]: @message', array('@label' => $view->storage->label(), '@view_name' => $view->storage->id(), '@message' => $e->getMessage())));
throw new DatabaseExceptionWrapper("Exception in {$view->storage->label()}[$view->storage->id()]: {$e->getMessage()}");
}
}
@ -1748,9 +1748,9 @@ class Sql extends QueryPluginBase {
}
/**
* Overrides \Drupal\views\Plugin\views\query\QueryPluginBase::getDateFormat().
* {@inheritdoc}
*/
public function getDateFormat($field, $format) {
public function getDateFormat($field, $format, $string_date = FALSE) {
$db_type = Database::getConnection()->databaseType();
switch ($db_type) {
case 'mysql':
@ -1797,7 +1797,12 @@ class Sql extends QueryPluginBase {
'A' => 'AM',
);
$format = strtr($format, $replace);
return "TO_CHAR($field, '$format')";
if (!$string_date) {
return "TO_CHAR($field, '$format')";
}
// In order to allow for partials (eg, only the year), transform to a
// date, back to a string again.
return "TO_CHAR(TO_TIMESTAMP($field, 'YYYY-MM-DD HH24:MI:SS'), '$format')";
case 'sqlite':
$replace = array(
'Y' => '%Y',
@ -1827,15 +1832,19 @@ class Sql extends QueryPluginBase {
'A' => '',
);
$format = strtr($format, $replace);
// Don't use the 'unixepoch' flag for string date comparisons.
$unixepoch = $string_date ? '' : ", 'unixepoch'";
// SQLite does not have a ISO week substitution string, so it needs
// special handling.
// @see http://en.wikipedia.org/wiki/ISO_week_date#Calculation
// @see http://stackoverflow.com/a/15511864/1499564
if ($format === '%W') {
$expression = "((strftime('%j', date(strftime('%Y-%m-%d', $field, 'unixepoch'), '-3 days', 'weekday 4')) - 1) / 7 + 1)";
$expression = "((strftime('%j', date(strftime('%Y-%m-%d', $field" . $unixepoch . "), '-3 days', 'weekday 4')) - 1) / 7 + 1)";
}
else {
$expression = "strftime('$format', $field, 'unixepoch')";
$expression = "strftime('$format', $field" . $unixepoch . ")";
}
// The expression yields a string, but the comparison value is an
// integer in case the comparison value is a float, integer, or numeric.

View file

@ -217,7 +217,7 @@ class OpmlFields extends RowPluginBase {
if (empty($this->view->style_plugin) || !is_object($this->view->style_plugin) || empty($field_id)) {
return '';
}
return $this->view->style_plugin->getField($index, $field_id);
return (string) $this->view->style_plugin->getField($index, $field_id);
}
}

View file

@ -8,9 +8,10 @@
namespace Drupal\views\Plugin\views\style;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Element;
use Drupal\Core\Render\SafeString;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\views\Plugin\views\PluginBase;
use Drupal\views\Plugin\views\wizard\WizardInterface;
@ -239,6 +240,11 @@ abstract class StylePluginBase extends PluginBase {
$value = $this->viewsTokenReplace($value, $tokens);
}
else {
// ::viewsTokenReplace() will run Xss::filterAdmin on the
// resulting string. We do the same here for consistency.
$value = Xss::filterAdmin($value);
}
return $value;
}
@ -700,8 +706,9 @@ abstract class StylePluginBase extends PluginBase {
$placeholders = array_keys($post_render_tokens);
$values = array_values($post_render_tokens);
foreach ($this->rendered_fields[$index] as &$rendered_field) {
$rendered_field = str_replace($placeholders, $values, $rendered_field);
SafeMarkup::set($rendered_field);
// Placeholders and rendered fields have been processed by the
// render system and are therefore safe.
$rendered_field = SafeString::create(str_replace($placeholders, $values, $rendered_field));
}
}
}
@ -738,7 +745,7 @@ abstract class StylePluginBase extends PluginBase {
* @param string $field
* The ID of the field.
*
* @return string|null
* @return \Drupal\Core\Render\SafeString|null
* The output of the field, or NULL if it was empty.
*/
public function getField($index, $field) {

View file

@ -444,8 +444,9 @@ class Table extends StylePluginBase implements CacheablePluginInterface {
foreach ($this->options['info'] as $field_id => $info) {
if (!empty($info['sortable'])) {
$contexts[] = 'url.query_args:order';
$contexts[] = 'url.query_args:sort';
// The rendered link needs to play well with any other query parameter
// used on the page, like pager and exposed filter.
$contexts[] = 'url.query_args';
break;
}
}

View file

@ -7,54 +7,13 @@
namespace Drupal\views\Routing;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\views\Plugin\views\display\Page;
use Drupal\views\ViewExecutableFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Defines a page controller to execute and render a view.
*/
class ViewPageController implements ContainerInjectionInterface {
/**
* The entity storage.
*
* @var \Drupal\Core\Entity\EntityStorageInterface
*/
protected $storage;
/**
* The view executable factory.
*
* @var \Drupal\views\ViewExecutableFactory
*/
protected $executableFactory;
/**
* Constructs a ViewPageController object.
*
* @param \Drupal\Core\Entity\EntityStorageInterface $storage
* The entity storage.
* @param \Drupal\views\ViewExecutableFactory $executable_factory
* The view executable factory
*/
public function __construct(EntityStorageInterface $storage, ViewExecutableFactory $executable_factory) {
$this->storage = $storage;
$this->executableFactory = $executable_factory;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('entity.manager')->getStorage('view'),
$container->get('views.executable')
);
}
class ViewPageController {
/**
* Handler a response for a given view and display.

View file

@ -71,17 +71,29 @@ class GlossaryTest extends ViewTestBase {
$url = Url::fromRoute('view.glossary.page_1');
// Verify cache tags.
$this->assertPageCacheContextsAndTags($url, ['languages:' . LanguageInterface::TYPE_CONTENT, 'languages:' . LanguageInterface::TYPE_INTERFACE, 'theme', 'url', 'user.node_grants:view', 'user.permissions'], [
'config:views.view.glossary',
'node:' . $nodes_by_char['a'][0]->id(), 'node:' . $nodes_by_char['a'][1]->id(), 'node:' . $nodes_by_char['a'][2]->id(),
'node_list',
'user:0',
'user_list',
'rendered',
// FinishResponseSubscriber adds this cache tag to responses that have the
// 'user.permissions' cache context for anonymous users.
'config:user.role.anonymous',
]);
$this->assertPageCacheContextsAndTags(
$url,
[
'languages:' . LanguageInterface::TYPE_CONTENT,
'languages:' . LanguageInterface::TYPE_INTERFACE,
'theme',
'url',
'user.node_grants:view',
'user.permissions',
'route',
],
[
'config:views.view.glossary',
'node:' . $nodes_by_char['a'][0]->id(), 'node:' . $nodes_by_char['a'][1]->id(), 'node:' . $nodes_by_char['a'][2]->id(),
'node_list',
'user:0',
'user_list',
'rendered',
// FinishResponseSubscriber adds this cache tag to responses that have the
// 'user.permissions' cache context for anonymous users.
'config:user.role.anonymous',
]
);
// Check the actual page response.
$this->drupalGet($url);

View file

@ -51,11 +51,11 @@ class FieldCounterTest extends ViewUnitTestBase {
$view->preview();
$counter = $view->style_plugin->getField(0, 'counter');
$this->assertEqual($counter, 1, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 1, '@counter' => $counter)));
$this->assertEqual($counter, '1', format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 1, '@counter' => $counter)));
$counter = $view->style_plugin->getField(1, 'counter');
$this->assertEqual($counter, 2, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 2, '@counter' => $counter)));
$this->assertEqual($counter, '2', format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 2, '@counter' => $counter)));
$counter = $view->style_plugin->getField(2, 'counter');
$this->assertEqual($counter, 3, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 3, '@counter' => $counter)));
$this->assertEqual($counter, '3', format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => 3, '@counter' => $counter)));
$view->destroy();
$view->storage->invalidateCaches();
@ -80,13 +80,13 @@ class FieldCounterTest extends ViewUnitTestBase {
$counter = $view->style_plugin->getField(0, 'counter');
$expected_number = 0 + $rand_start;
$this->assertEqual($counter, $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter)));
$this->assertEqual($counter, (string) $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter)));
$counter = $view->style_plugin->getField(1, 'counter');
$expected_number = 1 + $rand_start;
$this->assertEqual($counter, $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter)));
$this->assertEqual($counter, (string) $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter)));
$counter = $view->style_plugin->getField(2, 'counter');
$expected_number = 2 + $rand_start;
$this->assertEqual($counter, $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter)));
$this->assertEqual($counter, (string) $expected_number, format_string('Make sure the expected number (@expected) patches with the rendered number (@counter)', array('@expected' => $expected_number, '@counter' => $counter)));
}
// @TODO: Write tests for pager.

View file

@ -251,18 +251,18 @@ class FieldFieldTest extends ViewUnitTestBase {
$executable = Views::getView('test_field_field_test');
$executable->execute();
$this->assertEqual(1, $executable->getStyle()->getField(0, 'id'));
$this->assertEqual(3, $executable->getStyle()->getField(0, 'field_test'));
$this->assertEqual(2, $executable->getStyle()->getField(1, 'id'));
$this->assertEqual('1', $executable->getStyle()->getField(0, 'id'));
$this->assertEqual('3', $executable->getStyle()->getField(0, 'field_test'));
$this->assertEqual('2', $executable->getStyle()->getField(1, 'id'));
// @todo Switch this assertion to assertIdentical('', ...) when
// https://www.drupal.org/node/2488006 gets fixed.
$this->assertEqual(0, $executable->getStyle()->getField(1, 'field_test'));
$this->assertEqual(3, $executable->getStyle()->getField(2, 'id'));
$this->assertEqual(8, $executable->getStyle()->getField(2, 'field_test'));
$this->assertEqual(4, $executable->getStyle()->getField(3, 'id'));
$this->assertEqual(5, $executable->getStyle()->getField(3, 'field_test'));
$this->assertEqual(5, $executable->getStyle()->getField(4, 'id'));
$this->assertEqual(6, $executable->getStyle()->getField(4, 'field_test'));
$this->assertEqual('0', $executable->getStyle()->getField(1, 'field_test'));
$this->assertEqual('3', $executable->getStyle()->getField(2, 'id'));
$this->assertEqual('8', $executable->getStyle()->getField(2, 'field_test'));
$this->assertEqual('4', $executable->getStyle()->getField(3, 'id'));
$this->assertEqual('5', $executable->getStyle()->getField(3, 'field_test'));
$this->assertEqual('5', $executable->getStyle()->getField(4, 'id'));
$this->assertEqual('6', $executable->getStyle()->getField(4, 'field_test'));
}
/**
@ -326,10 +326,10 @@ class FieldFieldTest extends ViewUnitTestBase {
$executable->execute();
for ($i = 0; $i < 5; $i++) {
$this->assertEqual($i + 1, $executable->getStyle()->getField($i, 'id'));
$this->assertEqual((string) ($i + 1), $executable->getStyle()->getField($i, 'id'));
$this->assertEqual('test ' . $i, $executable->getStyle()->getField($i, 'name'));
$entity = EntityTest::load($i + 1);
$this->assertEqual('<a href="' . $entity->url() . '" hreflang="' . $entity->language()->getId() . '">test ' . $i . '</a>', $executable->getStyle()->getField($i, 'name_alias'));
$this->assertEqual('<a href="' . $entity->url() . '" hreflang="' . $entity->language()->getId() . '">test ' . $i . '</a>', (string) $executable->getStyle()->getField($i, 'name_alias'));
}
}
@ -426,24 +426,24 @@ class FieldFieldTest extends ViewUnitTestBase {
$executable = Views::getView('test_field_field_revision_test');
$executable->execute();
$this->assertEqual(1, $executable->getStyle()->getField(0, 'id'));
$this->assertEqual(1, $executable->getStyle()->getField(0, 'revision_id'));
$this->assertEqual(1, $executable->getStyle()->getField(0, 'field_test'));
$this->assertEqual('1', $executable->getStyle()->getField(0, 'id'));
$this->assertEqual('1', $executable->getStyle()->getField(0, 'revision_id'));
$this->assertEqual('1', $executable->getStyle()->getField(0, 'field_test'));
$this->assertEqual('base value', $executable->getStyle()->getField(0, 'name'));
$this->assertEqual(1, $executable->getStyle()->getField(1, 'id'));
$this->assertEqual(2, $executable->getStyle()->getField(1, 'revision_id'));
$this->assertEqual(2, $executable->getStyle()->getField(1, 'field_test'));
$this->assertEqual('1', $executable->getStyle()->getField(1, 'id'));
$this->assertEqual('2', $executable->getStyle()->getField(1, 'revision_id'));
$this->assertEqual('2', $executable->getStyle()->getField(1, 'field_test'));
$this->assertEqual('revision value1', $executable->getStyle()->getField(1, 'name'));
$this->assertEqual(1, $executable->getStyle()->getField(2, 'id'));
$this->assertEqual(3, $executable->getStyle()->getField(2, 'revision_id'));
$this->assertEqual(3, $executable->getStyle()->getField(2, 'field_test'));
$this->assertEqual('1', $executable->getStyle()->getField(2, 'id'));
$this->assertEqual('3', $executable->getStyle()->getField(2, 'revision_id'));
$this->assertEqual('3', $executable->getStyle()->getField(2, 'field_test'));
$this->assertEqual('revision value2', $executable->getStyle()->getField(2, 'name'));
$this->assertEqual(2, $executable->getStyle()->getField(3, 'id'));
$this->assertEqual(4, $executable->getStyle()->getField(3, 'revision_id'));
$this->assertEqual(4, $executable->getStyle()->getField(3, 'field_test'));
$this->assertEqual('2', $executable->getStyle()->getField(3, 'id'));
$this->assertEqual('4', $executable->getStyle()->getField(3, 'revision_id'));
$this->assertEqual('4', $executable->getStyle()->getField(3, 'field_test'));
$this->assertEqual('next entity value', $executable->getStyle()->getField(3, 'name'));
}
@ -484,29 +484,29 @@ class FieldFieldTest extends ViewUnitTestBase {
$executable = Views::getView('test_field_field_revision_complex_test');
$executable->execute();
$this->assertEqual(1, $executable->getStyle()->getField(0, 'id'));
$this->assertEqual(1, $executable->getStyle()->getField(0, 'revision_id'));
$this->assertEqual('1', $executable->getStyle()->getField(0, 'id'));
$this->assertEqual('1', $executable->getStyle()->getField(0, 'revision_id'));
$this->assertEqual($this->testUsers[0]->getTimeZone(), $executable->getStyle()->getField(0, 'timezone'));
$this->assertEqual('1, 3, 7', $executable->getStyle()->getField(0, 'field_test_multiple'));
$this->assertEqual('1', $executable->getStyle()->getField(0, 'field_test_multiple_1'));
$this->assertEqual('3, 7', $executable->getStyle()->getField(0, 'field_test_multiple_2'));
$this->assertEqual(1, $executable->getStyle()->getField(1, 'id'));
$this->assertEqual(2, $executable->getStyle()->getField(1, 'revision_id'));
$this->assertEqual('1', $executable->getStyle()->getField(1, 'id'));
$this->assertEqual('2', $executable->getStyle()->getField(1, 'revision_id'));
$this->assertEqual($this->testUsers[1]->getTimeZone(), $executable->getStyle()->getField(1, 'timezone'));
$this->assertEqual('0, 3, 5', $executable->getStyle()->getField(1, 'field_test_multiple'));
$this->assertEqual('0', $executable->getStyle()->getField(1, 'field_test_multiple_1'));
$this->assertEqual('3, 5', $executable->getStyle()->getField(1, 'field_test_multiple_2'));
$this->assertEqual(1, $executable->getStyle()->getField(2, 'id'));
$this->assertEqual(3, $executable->getStyle()->getField(2, 'revision_id'));
$this->assertEqual('1', $executable->getStyle()->getField(2, 'id'));
$this->assertEqual('3', $executable->getStyle()->getField(2, 'revision_id'));
$this->assertEqual($this->testUsers[2]->getTimeZone(), $executable->getStyle()->getField(2, 'timezone'));
$this->assertEqual('9, 9, 9', $executable->getStyle()->getField(2, 'field_test_multiple'));
$this->assertEqual('9', $executable->getStyle()->getField(2, 'field_test_multiple_1'));
$this->assertEqual('9, 9', $executable->getStyle()->getField(2, 'field_test_multiple_2'));
$this->assertEqual(2, $executable->getStyle()->getField(3, 'id'));
$this->assertEqual(4, $executable->getStyle()->getField(3, 'revision_id'));
$this->assertEqual('2', $executable->getStyle()->getField(3, 'id'));
$this->assertEqual('4', $executable->getStyle()->getField(3, 'revision_id'));
$this->assertEqual($this->testUsers[3]->getTimeZone(), $executable->getStyle()->getField(3, 'timezone'));
$this->assertEqual('2, 9, 9', $executable->getStyle()->getField(3, 'field_test_multiple'));
$this->assertEqual('2', $executable->getStyle()->getField(3, 'field_test_multiple_1'));
@ -531,9 +531,7 @@ class FieldFieldTest extends ViewUnitTestBase {
$executable = Views::getView('test_field_field_test');
$executable->execute();
// @todo Switch this assertion to assertIdentical('', ...) when
// https://www.drupal.org/node/2488006 gets fixed.
$this->assertEqual(0, $executable->getStyle()->getField(1, 'field_test'));
$this->assertEqual('', $executable->getStyle()->getField(1, 'field_test'));
}
}

View file

@ -8,6 +8,7 @@
namespace Drupal\views\Tests\Handler;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\Core\Render\RenderContext;
use Drupal\views\Views;
/**
@ -67,6 +68,9 @@ class FieldGroupRowsTest extends HandlerTestBase {
* Testing the "Grouped rows" functionality.
*/
public function testGroupRows() {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
$edit = array(
'title' => $this->randomMachineName(),
$this->fieldName => array('a', 'b', 'c'),
@ -77,7 +81,10 @@ class FieldGroupRowsTest extends HandlerTestBase {
// Test grouped rows.
$this->executeView($view);
$this->assertEqual($view->field[$this->fieldName]->advancedRender($view->result[0]), 'a, b, c');
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field[$this->fieldName]->advancedRender($view->result[0]);
});
$this->assertEqual($output, 'a, b, c');
// Change the group_rows checkbox to false.
$view = Views::getView('test_group_rows');
@ -88,11 +95,20 @@ class FieldGroupRowsTest extends HandlerTestBase {
$view->render();
$view->row_index = 0;
$this->assertEqual($view->field[$this->fieldName]->advancedRender($view->result[0]), 'a');
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field[$this->fieldName]->advancedRender($view->result[0]);
});
$this->assertEqual($output, 'a');
$view->row_index = 1;
$this->assertEqual($view->field[$this->fieldName]->advancedRender($view->result[1]), 'b');
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field[$this->fieldName]->advancedRender($view->result[1]);
});
$this->assertEqual($output, 'b');
$view->row_index = 2;
$this->assertEqual($view->field[$this->fieldName]->advancedRender($view->result[2]), 'c');
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field[$this->fieldName]->advancedRender($view->result[2]);
});
$this->assertEqual($output, 'c');
}
}

View file

@ -7,6 +7,7 @@
namespace Drupal\views\Tests\Handler;
use Drupal\Core\Render\RenderContext;
use Drupal\views\Tests\ViewUnitTestBase;
use Drupal\views\Plugin\views\field\FieldPluginBase;
use Drupal\views\Views;
@ -52,12 +53,18 @@ class FieldUnitTest extends ViewUnitTestBase {
* Tests that the render function is called.
*/
public function testRender() {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
$view = Views::getView('test_field_tokens');
$this->executeView($view);
$random_text = $this->randomMachineName();
$view->field['job']->setTestValue($random_text);
$this->assertEqual($view->field['job']->theme($view->result[0]), $random_text, 'Make sure the render method rendered the manual set value.');
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['job']->theme($view->result[0]);
});
$this->assertEqual($output, $random_text, 'Make sure the render method rendered the manual set value.');
}
/**
@ -141,6 +148,9 @@ class FieldUnitTest extends ViewUnitTestBase {
* Tests general rewriting of the output.
*/
public function testRewrite() {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
$view = Views::getView('test_view');
$view->initHandlers();
$this->executeView($view);
@ -149,11 +159,15 @@ class FieldUnitTest extends ViewUnitTestBase {
// Don't check the rewrite checkbox, so the text shouldn't appear.
$id_field->options['alter']['text'] = $random_text = $this->randomMachineName();
$output = $id_field->theme($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$this->assertNotSubString($output, $random_text);
$id_field->options['alter']['alter_text'] = TRUE;
$output = $id_field->theme($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$this->assertSubString($output, $random_text);
}
@ -161,6 +175,9 @@ class FieldUnitTest extends ViewUnitTestBase {
* Tests the field tokens, row level and field level.
*/
public function testFieldTokens() {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
$view = Views::getView('test_field_tokens');
$this->executeView($view);
$name_field_0 = $view->field['name'];
@ -175,26 +192,32 @@ class FieldUnitTest extends ViewUnitTestBase {
$name_field_1->options['alter']['text'] = '{{ name_1 }} {{ name }}';
$name_field_2->options['alter']['alter_text'] = TRUE;
$name_field_2->options['alter']['text'] = '{{ name_2 }} {{ name_1 }}';
$name_field_2->options['alter']['text'] = '{% if name_2|length > 3 %}{{ name_2 }} {{ name_1 }}{% endif %}';
foreach ($view->result as $row) {
$expected_output_0 = $row->views_test_data_name;
$expected_output_1 = "$row->views_test_data_name $row->views_test_data_name";
$expected_output_2 = "$row->views_test_data_name $row->views_test_data_name $row->views_test_data_name";
$output = $name_field_0->advancedRender($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field_0, $row) {
return $name_field_0->advancedRender($row);
});
$this->assertEqual($output, $expected_output_0, format_string('Test token replacement: "!token" gave "!output"', [
'!token' => $name_field_0->options['alter']['text'],
'!output' => $output,
]));
$output = $name_field_1->advancedRender($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field_1, $row) {
return $name_field_1->advancedRender($row);
});
$this->assertEqual($output, $expected_output_1, format_string('Test token replacement: "!token" gave "!output"', [
'!token' => $name_field_1->options['alter']['text'],
'!output' => $output,
]));
$output = $name_field_2->advancedRender($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field_2, $row) {
return $name_field_2->advancedRender($row);
});
$this->assertEqual($output, $expected_output_2, format_string('Test token replacement: "!token" gave "!output"', [
'!token' => $name_field_2->options['alter']['text'],
'!output' => $output,
@ -207,7 +230,9 @@ class FieldUnitTest extends ViewUnitTestBase {
$random_text = $this->randomMachineName();
$job_field->setTestValue($random_text);
$output = $job_field->advancedRender($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($job_field, $row) {
return $job_field->advancedRender($row);
});
$this->assertSubString($output, $random_text, format_string('Make sure the self token (!token => !value) appears in the output (!output)', [
'!value' => $random_text,
'!output' => $output,
@ -219,12 +244,46 @@ class FieldUnitTest extends ViewUnitTestBase {
$job_field->options['alter']['text'] = $old_token;
$random_text = $this->randomMachineName();
$job_field->setTestValue($random_text);
$output = $job_field->advancedRender($row);
$this->assertSubString($output, $old_token, format_string('Make sure the old token style (!token => !value) is not changed in the output (!output)', [
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($job_field, $row) {
return $job_field->advancedRender($row);
});
$this->assertEqual($output, $old_token, format_string('Make sure the old token style (!token => !value) is not changed in the output (!output)', [
'!value' => $random_text,
'!output' => $output,
'!token' => $job_field->options['alter']['text'],
]));
// Verify HTML tags are allowed in rewrite templates while token
// replacements are escaped.
$job_field->options['alter']['text'] = '<h1>{{ job }}</h1>';
$random_text = $this->randomMachineName();
$job_field->setTestValue('<span>' . $random_text . '</span>');
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($job_field, $row) {
return $job_field->advancedRender($row);
});
$this->assertEqual($output, '<h1>&lt;span&gt;' . $random_text . '&lt;/span&gt;</h1>', 'Valid tags are allowed in rewrite templates and token replacements.');
// Verify <script> tags are correctly removed from rewritten text.
$rewrite_template = '<script>alert("malicious");</script>';
$job_field->options['alter']['text'] = $rewrite_template;
$random_text = $this->randomMachineName();
$job_field->setTestValue($random_text);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($job_field, $row) {
return $job_field->advancedRender($row);
});
$this->assertNotSubString($output, '<script>', 'Ensure a script tag in the rewrite template is removed.');
$rewrite_template = '<script>{{ job }}</script>';
$job_field->options['alter']['text'] = $rewrite_template;
$random_text = $this->randomMachineName();
$job_field->setTestValue($random_text);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($job_field, $row) {
return $job_field->advancedRender($row);
});
$this->assertEqual($output, $random_text, format_string('Make sure a script tag in the template (!template) is removed, leaving only the replaced token in the output (!output)', [
'!output' => $output,
'!template' => $rewrite_template,
]));
}
/**
@ -268,6 +327,9 @@ class FieldUnitTest extends ViewUnitTestBase {
* This tests alters the result to get easier and less coupled results.
*/
function _testHideIfEmpty() {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
$view = Views::getView('test_view');
$view->initDisplay();
$this->executeView($view);
@ -284,22 +346,30 @@ class FieldUnitTest extends ViewUnitTestBase {
// Test a valid string.
$view->result[0]->{$column_map_reversed['name']} = $random_name;
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, $random_name, 'By default, a string should not be treated as empty.');
// Test an empty string.
$view->result[0]->{$column_map_reversed['name']} = "";
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, "", 'By default, "" should not be treated as empty.');
// Test zero as an integer.
$view->result[0]->{$column_map_reversed['name']} = 0;
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, '0', 'By default, 0 should not be treated as empty.');
// Test zero as a string.
$view->result[0]->{$column_map_reversed['name']} = "0";
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, "0", 'By default, "0" should not be treated as empty.');
// Test when results are not rewritten and non-zero empty values are hidden.
@ -309,22 +379,30 @@ class FieldUnitTest extends ViewUnitTestBase {
// Test a valid string.
$view->result[0]->{$column_map_reversed['name']} = $random_name;
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, $random_name, 'If hide_empty is checked, a string should not be treated as empty.');
// Test an empty string.
$view->result[0]->{$column_map_reversed['name']} = "";
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, "", 'If hide_empty is checked, "" should be treated as empty.');
// Test zero as an integer.
$view->result[0]->{$column_map_reversed['name']} = 0;
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, '0', 'If hide_empty is checked, but not empty_zero, 0 should not be treated as empty.');
// Test zero as a string.
$view->result[0]->{$column_map_reversed['name']} = "0";
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, "0", 'If hide_empty is checked, but not empty_zero, "0" should not be treated as empty.');
// Test when results are not rewritten and all empty values are hidden.
@ -334,12 +412,16 @@ class FieldUnitTest extends ViewUnitTestBase {
// Test zero as an integer.
$view->result[0]->{$column_map_reversed['name']} = 0;
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, "", 'If hide_empty and empty_zero are checked, 0 should be treated as empty.');
// Test zero as a string.
$view->result[0]->{$column_map_reversed['name']} = "0";
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, "", 'If hide_empty and empty_zero are checked, "0" should be treated as empty.');
// Test when results are rewritten to a valid string and non-zero empty
@ -352,22 +434,30 @@ class FieldUnitTest extends ViewUnitTestBase {
// Test a valid string.
$view->result[0]->{$column_map_reversed['name']} = $random_value;
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, $random_name, 'If the rewritten string is not empty, it should not be treated as empty.');
// Test an empty string.
$view->result[0]->{$column_map_reversed['name']} = "";
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, $random_name, 'If the rewritten string is not empty, "" should not be treated as empty.');
// Test zero as an integer.
$view->result[0]->{$column_map_reversed['name']} = 0;
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, $random_name, 'If the rewritten string is not empty, 0 should not be treated as empty.');
// Test zero as a string.
$view->result[0]->{$column_map_reversed['name']} = "0";
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, $random_name, 'If the rewritten string is not empty, "0" should not be treated as empty.');
// Test when results are rewritten to an empty string and non-zero empty results are hidden.
@ -379,22 +469,30 @@ class FieldUnitTest extends ViewUnitTestBase {
// Test a valid string.
$view->result[0]->{$column_map_reversed['name']} = $random_name;
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, $random_name, 'If the rewritten string is empty, it should not be treated as empty.');
// Test an empty string.
$view->result[0]->{$column_map_reversed['name']} = "";
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, "", 'If the rewritten string is empty, "" should be treated as empty.');
// Test zero as an integer.
$view->result[0]->{$column_map_reversed['name']} = 0;
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, '0', 'If the rewritten string is empty, 0 should not be treated as empty.');
// Test zero as a string.
$view->result[0]->{$column_map_reversed['name']} = "0";
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, "0", 'If the rewritten string is empty, "0" should not be treated as empty.');
// Test when results are rewritten to zero as a string and non-zero empty
@ -407,22 +505,30 @@ class FieldUnitTest extends ViewUnitTestBase {
// Test a valid string.
$view->result[0]->{$column_map_reversed['name']} = $random_name;
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, "0", 'If the rewritten string is zero and empty_zero is not checked, the string rewritten as 0 should not be treated as empty.');
// Test an empty string.
$view->result[0]->{$column_map_reversed['name']} = "";
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, "0", 'If the rewritten string is zero and empty_zero is not checked, "" rewritten as 0 should not be treated as empty.');
// Test zero as an integer.
$view->result[0]->{$column_map_reversed['name']} = 0;
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, "0", 'If the rewritten string is zero and empty_zero is not checked, 0 should not be treated as empty.');
// Test zero as a string.
$view->result[0]->{$column_map_reversed['name']} = "0";
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, "0", 'If the rewritten string is zero and empty_zero is not checked, "0" should not be treated as empty.');
// Test when results are rewritten to a valid string and non-zero empty
@ -435,22 +541,30 @@ class FieldUnitTest extends ViewUnitTestBase {
// Test a valid string.
$view->result[0]->{$column_map_reversed['name']} = $random_name;
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, $random_value, 'If the original and rewritten strings are valid, it should not be treated as empty.');
// Test an empty string.
$view->result[0]->{$column_map_reversed['name']} = "";
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, "", 'If either the original or rewritten string is invalid, "" should be treated as empty.');
// Test zero as an integer.
$view->result[0]->{$column_map_reversed['name']} = 0;
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, $random_value, 'If the original and rewritten strings are valid, 0 should not be treated as empty.');
// Test zero as a string.
$view->result[0]->{$column_map_reversed['name']} = "0";
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, $random_value, 'If the original and rewritten strings are valid, "0" should not be treated as empty.');
// Test when results are rewritten to zero as a string and all empty
@ -463,22 +577,30 @@ class FieldUnitTest extends ViewUnitTestBase {
// Test a valid string.
$view->result[0]->{$column_map_reversed['name']} = $random_name;
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, "", 'If the rewritten string is zero, it should be treated as empty.');
// Test an empty string.
$view->result[0]->{$column_map_reversed['name']} = "";
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, "", 'If the rewritten string is zero, "" should be treated as empty.');
// Test zero as an integer.
$view->result[0]->{$column_map_reversed['name']} = 0;
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, "", 'If the rewritten string is zero, 0 should not be treated as empty.');
// Test zero as a string.
$view->result[0]->{$column_map_reversed['name']} = "0";
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, "", 'If the rewritten string is zero, "0" should not be treated as empty.');
}
@ -486,6 +608,9 @@ class FieldUnitTest extends ViewUnitTestBase {
* Tests the usage of the empty text.
*/
function _testEmptyText() {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
$view = Views::getView('test_view');
$view->initDisplay();
$this->executeView($view);
@ -495,27 +620,37 @@ class FieldUnitTest extends ViewUnitTestBase {
$empty_text = $view->field['name']->options['empty'] = $this->randomMachineName();
$view->result[0]->{$column_map_reversed['name']} = "";
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, $empty_text, 'If a field is empty, the empty text should be used for the output.');
$view->result[0]->{$column_map_reversed['name']} = "0";
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, "0", 'If a field is 0 and empty_zero is not checked, the empty text should not be used for the output.');
$view->result[0]->{$column_map_reversed['name']} = "0";
$view->field['name']->options['empty_zero'] = TRUE;
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, $empty_text, 'If a field is 0 and empty_zero is checked, the empty text should be used for the output.');
$view->result[0]->{$column_map_reversed['name']} = "";
$view->field['name']->options['alter']['alter_text'] = TRUE;
$alter_text = $view->field['name']->options['alter']['text'] = $this->randomMachineName();
$view->field['name']->options['hide_alter_empty'] = FALSE;
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, $alter_text, 'If a field is empty, some rewrite text exists, but hide_alter_empty is not checked, render the rewrite text.');
$view->field['name']->options['hide_alter_empty'] = TRUE;
$render = $view->field['name']->advancedRender($view->result[0]);
$render = $renderer->executeInRenderContext(new RenderContext(), function () use ($view) {
return $view->field['name']->advancedRender($view->result[0]);
});
$this->assertIdentical($render, $empty_text, 'If a field is empty, some rewrite text exists, and hide_alter_empty is checked, use the empty text.');
}

View file

@ -10,6 +10,7 @@ namespace Drupal\views\Tests\Handler;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Unicode;
use Drupal\Component\Utility\UrlHelper;
use Drupal\Core\Render\RenderContext;
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
use Drupal\views\Views;
@ -67,23 +68,21 @@ class FieldWebTest extends HandlerTestBase {
$this->assertResponse(200);
// Only the id and name should be click sortable, but not the name.
$this->assertLinkByHref(\Drupal::url('view.test_click_sort.page_1', [], ['query' => ['order' => 'id', 'sort' => 'asc']]));
$this->assertLinkByHref(\Drupal::url('view.test_click_sort.page_1', [], ['query' => ['order' => 'name', 'sort' => 'desc']]));
$this->assertNoLinkByHref(\Drupal::url('view.test_click_sort.page_1', [], ['query' => ['order' => 'created']]));
$this->assertLinkByHref(\Drupal::url('<none>', [], ['query' => ['order' => 'id', 'sort' => 'asc']]));
$this->assertLinkByHref(\Drupal::url('<none>', [], ['query' => ['order' => 'name', 'sort' => 'desc']]));
$this->assertNoLinkByHref(\Drupal::url('<none>', [], ['query' => ['order' => 'created']]));
// Check that the view returns the click sorting cache contexts.
$expected_contexts = [
'languages:language_interface',
'theme',
'url.query_args.pagers:0',
'url.query_args:order',
'url.query_args:sort',
'url.query_args',
];
$this->assertCacheContexts($expected_contexts);
// Clicking a click sort should change the order.
$this->clickLink(t('ID'));
$this->assertLinkByHref(\Drupal::url('view.test_click_sort.page_1', [], ['query' => ['order' => 'id', 'sort' => 'desc']]));
$this->assertLinkByHref(\Drupal::url('<none>', [], ['query' => ['order' => 'id', 'sort' => 'desc']]));
// Check that the output has the expected order (asc).
$ids = $this->clickSortLoadIdsFromOutput();
$this->assertEqual($ids, range(1, 5));
@ -197,6 +196,9 @@ class FieldWebTest extends HandlerTestBase {
* Tests rewriting the output to a link.
*/
public function testAlterUrl() {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
$view = Views::getView('test_view');
$view->setDisplay();
$view->initHandlers();
@ -211,13 +213,17 @@ class FieldWebTest extends HandlerTestBase {
// Tests that the suffix/prefix appears on the output.
$id_field->options['alter']['prefix'] = $prefix = $this->randomMachineName();
$id_field->options['alter']['suffix'] = $suffix = $this->randomMachineName();
$output = $id_field->theme($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$this->assertSubString($output, $prefix);
$this->assertSubString($output, $suffix);
unset($id_field->options['alter']['prefix']);
unset($id_field->options['alter']['suffix']);
$output = $id_field->theme($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$this->assertSubString($output, $path, 'Make sure that the path is part of the output');
// Some generic test code adapted from the UrlTest class, which tests
@ -228,44 +234,60 @@ class FieldWebTest extends HandlerTestBase {
$expected_result = \Drupal::url('entity.node.canonical', ['node' => '123'], ['absolute' => $absolute]);
$alter['absolute'] = $absolute;
$result = $id_field->theme($row);
$result = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$this->assertSubString($result, $expected_result);
$expected_result = \Drupal::url('entity.node.canonical', ['node' => '123'], ['fragment' => 'foo', 'absolute' => $absolute]);
$alter['path'] = 'node/123#foo';
$result = $id_field->theme($row);
$result = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$this->assertSubString($result, $expected_result);
$expected_result = \Drupal::url('entity.node.canonical', ['node' => '123'], ['query' => ['foo' => NULL], 'absolute' => $absolute]);
$alter['path'] = 'node/123?foo';
$result = $id_field->theme($row);
$result = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$this->assertSubString($result, $expected_result);
$expected_result = \Drupal::url('entity.node.canonical', ['node' => '123'], ['query' => ['foo' => 'bar', 'bar' => 'baz'], 'absolute' => $absolute]);
$alter['path'] = 'node/123?foo=bar&bar=baz';
$result = $id_field->theme($row);
$result = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$this->assertSubString(Html::decodeEntities($result), Html::decodeEntities($expected_result));
// @todo The route-based URL generator strips out NULL attributes.
// $expected_result = \Drupal::url('entity.node.canonical', ['node' => '123'], ['query' => ['foo' => NULL], 'fragment' => 'bar', 'absolute' => $absolute]);
$expected_result = \Drupal::urlGenerator()->generateFromPath('node/123', array('query' => array('foo' => NULL), 'fragment' => 'bar', 'absolute' => $absolute));
$alter['path'] = 'node/123?foo#bar';
$result = $id_field->theme($row);
$result = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$this->assertSubString(Html::decodeEntities($result), Html::decodeEntities($expected_result));
$expected_result = \Drupal::url('<front>', [], ['absolute' => $absolute]);
$alter['path'] = '<front>';
$result = $id_field->theme($row);
$result = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$this->assertSubString($result, $expected_result);
}
// Tests the replace spaces with dashes feature.
$id_field->options['alter']['replace_spaces'] = TRUE;
$id_field->options['alter']['path'] = $path = $this->randomMachineName() . ' ' . $this->randomMachineName();
$output = $id_field->theme($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$this->assertSubString($output, str_replace(' ', '-', $path));
$id_field->options['alter']['replace_spaces'] = FALSE;
$output = $id_field->theme($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
// The url has a space in it, so to check we have to decode the url output.
$this->assertSubString(urldecode($output), $path);
@ -273,44 +295,60 @@ class FieldWebTest extends HandlerTestBase {
// Switch on the external flag should output an external url as well.
$id_field->options['alter']['external'] = TRUE;
$id_field->options['alter']['path'] = $path = 'www.drupal.org';
$output = $id_field->theme($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$this->assertSubString($output, 'http://www.drupal.org');
// Setup a not external url, which shouldn't lead to an external url.
$id_field->options['alter']['external'] = FALSE;
$id_field->options['alter']['path'] = $path = 'www.drupal.org';
$output = $id_field->theme($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$this->assertNotSubString($output, 'http://www.drupal.org');
// Tests the transforming of the case setting.
$id_field->options['alter']['path'] = $path = $this->randomMachineName();
$id_field->options['alter']['path_case'] = 'none';
$output = $id_field->theme($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$this->assertSubString($output, $path);
// Switch to uppercase and lowercase.
$id_field->options['alter']['path_case'] = 'upper';
$output = $id_field->theme($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$this->assertSubString($output, strtoupper($path));
$id_field->options['alter']['path_case'] = 'lower';
$output = $id_field->theme($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$this->assertSubString($output, strtolower($path));
// Switch to ucfirst and ucwords.
$id_field->options['alter']['path_case'] = 'ucfirst';
$id_field->options['alter']['path'] = 'drupal has a great community';
$output = $id_field->theme($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$this->assertSubString($output, UrlHelper::encodePath('Drupal has a great community'));
$id_field->options['alter']['path_case'] = 'ucwords';
$output = $id_field->theme($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$this->assertSubString($output, UrlHelper::encodePath('Drupal Has A Great Community'));
unset($id_field->options['alter']['path_case']);
// Tests the linkclass setting and see whether it actually exists in the
// output.
$id_field->options['alter']['link_class'] = $class = $this->randomMachineName();
$output = $id_field->theme($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$elements = $this->xpathContent($output, '//a[contains(@class, :class)]', array(':class' => $class));
$this->assertTrue($elements);
// @fixme link_class, alt, rel cannot be unset, which should be fixed.
@ -318,21 +356,27 @@ class FieldWebTest extends HandlerTestBase {
// Tests the alt setting.
$id_field->options['alter']['alt'] = $rel = $this->randomMachineName();
$output = $id_field->theme($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$elements = $this->xpathContent($output, '//a[contains(@title, :alt)]', array(':alt' => $rel));
$this->assertTrue($elements);
$id_field->options['alter']['alt'] = '';
// Tests the rel setting.
$id_field->options['alter']['rel'] = $rel = $this->randomMachineName();
$output = $id_field->theme($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$elements = $this->xpathContent($output, '//a[contains(@rel, :rel)]', array(':rel' => $rel));
$this->assertTrue($elements);
$id_field->options['alter']['rel'] = '';
// Tests the target setting.
$id_field->options['alter']['target'] = $target = $this->randomMachineName();
$output = $id_field->theme($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($id_field, $row) {
return $id_field->theme($row);
});
$elements = $this->xpathContent($output, '//a[contains(@target, :target)]', array(':target' => $target));
$this->assertTrue($elements);
unset($id_field->options['alter']['target']);
@ -453,6 +497,9 @@ class FieldWebTest extends HandlerTestBase {
* Tests trimming/read-more/ellipses.
*/
public function testTextRendering() {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
$view = Views::getView('test_field_output');
$view->initHandlers();
$name_field = $view->field['name'];
@ -465,18 +512,24 @@ class FieldWebTest extends HandlerTestBase {
$row = $view->result[0];
$name_field->options['alter']['strip_tags'] = TRUE;
$output = $name_field->advancedRender($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
return $name_field->advancedRender($row);
});
$this->assertSubString($output, $random_text, 'Find text without html if stripping of views field output is enabled.');
$this->assertNotSubString($output, $html_text, 'Find no text with the html if stripping of views field output is enabled.');
// Tests preserving of html tags.
$name_field->options['alter']['preserve_tags'] = '<div>';
$output = $name_field->advancedRender($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
return $name_field->advancedRender($row);
});
$this->assertSubString($output, $random_text, 'Find text without html if stripping of views field output is enabled but a div is allowed.');
$this->assertSubString($output, $html_text, 'Find text with the html if stripping of views field output is enabled but a div is allowed.');
$name_field->options['alter']['strip_tags'] = FALSE;
$output = $name_field->advancedRender($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
return $name_field->advancedRender($row);
});
$this->assertSubString($output, $random_text, 'Find text without html if stripping of views field output is disabled.');
$this->assertSubString($output, $html_text, 'Find text with the html if stripping of views field output is disabled.');
@ -485,13 +538,17 @@ class FieldWebTest extends HandlerTestBase {
$views_test_data_name = $row->views_test_data_name;
$row->views_test_data_name = ' ' . $views_test_data_name . ' ';
$name_field->options['alter']['trim_whitespace'] = TRUE;
$output = $name_field->advancedRender($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
return $name_field->advancedRender($row);
});
$this->assertSubString($output, $views_test_data_name, 'Make sure the trimmed text can be found if trimming is enabled.');
$this->assertNotSubString($output, $row->views_test_data_name, 'Make sure the untrimmed text can be found if trimming is enabled.');
$name_field->options['alter']['trim_whitespace'] = FALSE;
$output = $name_field->advancedRender($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
return $name_field->advancedRender($row);
});
$this->assertSubString($output, $views_test_data_name, 'Make sure the trimmed text can be found if trimming is disabled.');
$this->assertSubString($output, $row->views_test_data_name, 'Make sure the untrimmed text can be found if trimming is disabled.');
@ -504,12 +561,16 @@ class FieldWebTest extends HandlerTestBase {
$name_field->options['alter']['max_length'] = 5;
$trimmed_name = Unicode::substr($row->views_test_data_name, 0, 5);
$output = $name_field->advancedRender($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
return $name_field->advancedRender($row);
});
$this->assertSubString($output, $trimmed_name, format_string('Make sure the trimmed output (!trimmed) appears in the rendered output (!output).', array('!trimmed' => $trimmed_name, '!output' => $output)));
$this->assertNotSubString($output, $row->views_test_data_name, format_string("Make sure the untrimmed value (!untrimmed) shouldn't appear in the rendered output (!output).", array('!untrimmed' => $row->views_test_data_name, '!output' => $output)));
$name_field->options['alter']['max_length'] = 9;
$output = $name_field->advancedRender($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
return $name_field->advancedRender($row);
});
$this->assertSubString($output, $trimmed_name, format_string('Make sure the untrimmed (!untrimmed) output appears in the rendered output (!output).', array('!trimmed' => $trimmed_name, '!output' => $output)));
// Take word_boundary into account for the tests.
@ -549,7 +610,9 @@ class FieldWebTest extends HandlerTestBase {
foreach ($tuples as $tuple) {
$row->views_test_data_name = $tuple['value'];
$output = $name_field->advancedRender($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
return $name_field->advancedRender($row);
});
if ($tuple['trimmed']) {
$this->assertNotSubString($output, $tuple['value'], format_string('The untrimmed value (!untrimmed) should not appear in the trimmed output (!output).', array('!untrimmed' => $tuple['value'], '!output' => $output)));
@ -566,22 +629,30 @@ class FieldWebTest extends HandlerTestBase {
$name_field->options['alter']['more_link_text'] = $more_text = $this->randomMachineName();
$name_field->options['alter']['more_link_path'] = $more_path = $this->randomMachineName();
$output = $name_field->advancedRender($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
return $name_field->advancedRender($row);
});
$this->assertSubString($output, $more_text, 'Make sure a read more text is displayed if the output got trimmed');
$this->assertTrue($this->xpathContent($output, '//a[contains(@href, :path)]', array(':path' => $more_path)), 'Make sure the read more link points to the right destination.');
$name_field->options['alter']['more_link'] = FALSE;
$output = $name_field->advancedRender($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
return $name_field->advancedRender($row);
});
$this->assertNotSubString($output, $more_text, 'Make sure no read more text appears.');
$this->assertFalse($this->xpathContent($output, '//a[contains(@href, :path)]', array(':path' => $more_path)), 'Make sure no read more link appears.');
// Check for the ellipses.
$row->views_test_data_name = $this->randomMachineName(8);
$name_field->options['alter']['max_length'] = 5;
$output = $name_field->advancedRender($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
return $name_field->advancedRender($row);
});
$this->assertSubString($output, '…', 'An ellipsis should appear if the output is trimmed');
$name_field->options['alter']['max_length'] = 10;
$output = $name_field->advancedRender($row);
$output = $renderer->executeInRenderContext(new RenderContext(), function () use ($name_field, $row) {
return $name_field->advancedRender($row);
});
$this->assertNotSubString($output, '…', 'No ellipsis should appear if the output is not trimmed');
}

View file

@ -7,8 +7,8 @@
namespace Drupal\views\Tests\Handler;
use Drupal\simpletest\UserCreationTrait;
use Drupal\views\Views;
use Drupal\views\Tests\ViewUnitTestBase;
use Drupal\views\Tests\Plugin\RelationshipJoinTestBase;
/**
@ -18,6 +18,7 @@ use Drupal\views\Tests\Plugin\RelationshipJoinTestBase;
* @see \Drupal\views\Plugin\views\relationship\RelationshipPluginBase
*/
class RelationshipTest extends RelationshipJoinTestBase {
use UserCreationTrait;
/**
* Views used by this test.
@ -133,4 +134,51 @@ class RelationshipTest extends RelationshipJoinTestBase {
$this->assertIdenticalResultset($view, $expected_result, $this->columnMap);
}
/**
* Tests rendering of a view with a relationship.
*/
public function testRelationshipRender() {
$author1 = $this->createUser();
db_query("UPDATE {views_test_data} SET uid = :uid WHERE id = 1", [':uid' => $author1->id()]);
$author2 = $this->createUser();
db_query("UPDATE {views_test_data} SET uid = :uid WHERE id = 2", [':uid' => $author2->id()]);
// Set uid to non-existing author uid for row 3.
db_query("UPDATE {views_test_data} SET uid = :uid WHERE id = 3", [':uid' => $author2->id() + 123]);
$view = Views::getView('test_view');
// Add a relationship for authors.
$view->getDisplay()->overrideOption('relationships', [
'uid' => [
'id' => 'uid',
'table' => 'views_test_data',
'field' => 'uid',
],
]);
// Add fields for {views_test_data}.id and author name.
$view->getDisplay()->overrideOption('fields', [
'id' => [
'id' => 'id',
'table' => 'views_test_data',
'field' => 'id',
],
'author' => [
'id' => 'author',
'table' => 'users_field_data',
'field' => 'name',
'relationship' => 'uid',
],
]);
// Render the view.
$output = $view->preview();
$html = $this->container->get('renderer')->renderRoot($output);
$this->setRawContent($html);
// Check that the output contains correct values.
$xpath = '//div[@class="views-row" and div[@class="views-field views-field-id"]=:id and div[@class="views-field views-field-author"]=:author]';
$this->assertEqual(1, count($this->xpath($xpath, [':id' => 1, ':author' => $author1->getUsername()])));
$this->assertEqual(1, count($this->xpath($xpath, [':id' => 2, ':author' => $author2->getUsername()])));
$this->assertEqual(1, count($this->xpath($xpath, [':id' => 3, ':author' => ''])));
}
}

View file

@ -197,7 +197,7 @@ class CacheTagTest extends PluginTestBase {
$view->destroy();
// Invalidate the views cache tags in order to invalidate the render
// caching.
\Drupal::service('cache_tags.invalidator')->invalidateTags($view->storage->getCacheTags());
\Drupal::service('cache_tags.invalidator')->invalidateTags($view->storage->getCacheTagsToInvalidate());
$build = $view->buildRenderable();
$renderer->renderPlain($build);

View file

@ -7,6 +7,7 @@
namespace Drupal\views\Tests\Plugin;
use Drupal\Core\Render\RenderContext;
use Drupal\node\Entity\Node;
use Drupal\views\Tests\ViewUnitTestBase;
use Drupal\views\Views;
@ -282,14 +283,18 @@ class CacheTest extends ViewUnitTestBase {
$output = $view->buildRenderable();
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
$renderer->render($output);
$renderer->executeInRenderContext(new RenderContext(), function () use (&$output, $renderer) {
return $renderer->render($output);
});
unset($view->pre_render_called);
$view->destroy();
$view->setDisplay();
$output = $view->buildRenderable();
$renderer->render($output);
$renderer->executeInRenderContext(new RenderContext(), function () use (&$output, $renderer) {
return $renderer->render($output);
});
$this->assertTrue(in_array('views_test_data/test', $output['#attached']['library']), 'Make sure libraries are added for cached views.');
$this->assertEqual(['foo' => 'bar'], $output['#attached']['drupalSettings'], 'Make sure drupalSettings are added for cached views.');

View file

@ -206,11 +206,7 @@ class ExposedFormTest extends ViewTestBase {
'languages:language_interface',
'entity_test_view_grants',
'theme',
'url.query_args.pagers:0',
'url.query_args:items_per_page',
'url.query_args:offset',
'url.query_args:sort_order',
'url.query_args:sort_by',
'url.query_args',
'languages:language_content'
];

View file

@ -261,7 +261,7 @@ class PagerTest extends PluginTestBase {
// Test pager cache contexts.
$this->drupalGet('test_pager_full');
$this->assertCacheContexts(['languages:language_interface', 'theme', 'timezone', 'url.query_args.pagers:0', 'user.node_grants:view']);
$this->assertCacheContexts(['languages:language_interface', 'theme', 'timezone', 'url.query_args', 'user.node_grants:view']);
}
/**

View file

@ -253,9 +253,9 @@ class QueryGroupByTest extends ViewUnitTestBase {
$this->executeView($view);
$this->assertEqual(2, count($view->result));
$this->assertEqual(3, $view->getStyle()->getField(0, 'id'));
$this->assertEqual('3', $view->getStyle()->getField(0, 'id'));
$this->assertEqual('1', $view->getStyle()->getField(0, 'field_test'));
$this->assertEqual(6, $view->getStyle()->getField(1, 'id'));
$this->assertEqual('6', $view->getStyle()->getField(1, 'id'));
$this->assertEqual('2', $view->getStyle()->getField(1, 'field_test'));
$entities[2]->field_test[0]->value = 3;
@ -267,15 +267,15 @@ class QueryGroupByTest extends ViewUnitTestBase {
$this->executeView($view);
$this->assertEqual(5, count($view->result));
$this->assertEqual(3, $view->getStyle()->getField(0, 'id'));
$this->assertEqual('3', $view->getStyle()->getField(0, 'id'));
$this->assertEqual('1', $view->getStyle()->getField(0, 'field_test'));
$this->assertEqual(3, $view->getStyle()->getField(1, 'id'));
$this->assertEqual('3', $view->getStyle()->getField(1, 'id'));
$this->assertEqual('2', $view->getStyle()->getField(1, 'field_test'));
$this->assertEqual(1, $view->getStyle()->getField(2, 'id'));
$this->assertEqual('1', $view->getStyle()->getField(2, 'id'));
$this->assertEqual('3', $view->getStyle()->getField(2, 'field_test'));
$this->assertEqual(1, $view->getStyle()->getField(3, 'id'));
$this->assertEqual('1', $view->getStyle()->getField(3, 'id'));
$this->assertEqual('4', $view->getStyle()->getField(3, 'field_test'));
$this->assertEqual(1, $view->getStyle()->getField(4, 'id'));
$this->assertEqual('1', $view->getStyle()->getField(4, 'id'));
$this->assertEqual('5', $view->getStyle()->getField(4, 'field_test'));
// Check that translated values are correctly retrieved and are not grouped
@ -288,7 +288,7 @@ class QueryGroupByTest extends ViewUnitTestBase {
$this->executeView($view);
$this->assertEqual(6, count($view->result));
$this->assertEqual(3, $view->getStyle()->getField(5, 'id'));
$this->assertEqual('3', $view->getStyle()->getField(5, 'id'));
$this->assertEqual('6', $view->getStyle()->getField(5, 'field_test'));
}

View file

@ -124,7 +124,11 @@ class RenderCacheIntegrationTest extends ViewUnitTestBase {
$this->pass('Test pager');
$this->pass('Page 1');
\Drupal::request()->query->set('page', 0);
$tags_page_1 = Cache::mergeTags($base_tags, $entities[1]->getCacheTags(), $entities[2]->getCacheTags(), $entities[3]->getCacheTags(), $entities[4]->getCacheTags(), $entities[5]->getCacheTags());
$tags_page_1 = Cache::mergeTags($base_tags, $entities[1]->getCacheTags());
$tags_page_1 = Cache::mergeTags($tags_page_1, $entities[2]->getCacheTags());
$tags_page_1 = Cache::mergeTags($tags_page_1, $entities[3]->getCacheTags());
$tags_page_1 = Cache::mergeTags($tags_page_1, $entities[4]->getCacheTags());
$tags_page_1 = Cache::mergeTags($tags_page_1, $entities[5]->getCacheTags());
$this->assertViewsCacheTags($view, $tags_page_1, $do_assert_views_caches, $tags_page_1);
$this->assertViewsCacheTagsFromStaticRenderArray($view, $tags_page_1, $do_assert_views_caches);
$view->destroy();
@ -254,7 +258,8 @@ class RenderCacheIntegrationTest extends ViewUnitTestBase {
$entity->save();
$result_tags_with_entity = Cache::mergeTags($base_tags, $entities[0]->getCacheTags());
$render_tags_with_entity = Cache::mergeTags($base_render_tags, $entities[0]->getCacheTags(), ['entity_test_view']);
$render_tags_with_entity = Cache::mergeTags($base_render_tags, $entities[0]->getCacheTags());
$render_tags_with_entity = Cache::mergeTags($render_tags_with_entity, ['entity_test_view']);
$this->assertViewsCacheTags($view, $result_tags_with_entity, $do_assert_views_caches, $render_tags_with_entity);
$this->assertViewsCacheTagsFromStaticRenderArray($view, $render_tags_with_entity, $do_assert_views_caches);
@ -265,9 +270,13 @@ class RenderCacheIntegrationTest extends ViewUnitTestBase {
$entity->save();
}
$new_entities_cache_tags = Cache::mergeTags($entities[1]->getCacheTags(), $entities[2]->getCacheTags(), $entities[3]->getCacheTags(), $entities[4]->getCacheTags(), $entities[5]->getCacheTags());
$new_entities_cache_tags = Cache::mergeTags($entities[1]->getCacheTags(), $entities[2]->getCacheTags());
$new_entities_cache_tags = Cache::mergeTags($new_entities_cache_tags, $entities[3]->getCacheTags());
$new_entities_cache_tags = Cache::mergeTags($new_entities_cache_tags, $entities[4]->getCacheTags());
$new_entities_cache_tags = Cache::mergeTags($new_entities_cache_tags, $entities[5]->getCacheTags());
$result_tags_page_1 = Cache::mergeTags($base_tags, $new_entities_cache_tags);
$render_tags_page_1 = Cache::mergeTags($base_render_tags, $new_entities_cache_tags, ['entity_test_view']);
$render_tags_page_1 = Cache::mergeTags($base_render_tags, $new_entities_cache_tags);
$render_tags_page_1 = Cache::mergeTags($render_tags_page_1, ['entity_test_view']);
$this->assertViewsCacheTags($view, $result_tags_page_1, $do_assert_views_caches, $render_tags_page_1);
$this->assertViewsCacheTagsFromStaticRenderArray($view, $render_tags_page_1, $do_assert_views_caches);
}
@ -292,7 +301,7 @@ class RenderCacheIntegrationTest extends ViewUnitTestBase {
$view = View::load('test_display');
$view->save();
$this->assertEqual(['languages:' . LanguageInterface::TYPE_CONTENT, 'languages:' . LanguageInterface::TYPE_INTERFACE, 'url.query_args.pagers:0', 'user.node_grants:view', 'user.permissions'], $view->getDisplay('default')['cache_metadata']['contexts']);
$this->assertEqual(['languages:' . LanguageInterface::TYPE_CONTENT, 'languages:' . LanguageInterface::TYPE_INTERFACE, 'url.query_args', 'user.node_grants:view', 'user.permissions'], $view->getDisplay('default')['cache_metadata']['contexts']);
}
}

View file

@ -7,6 +7,7 @@
namespace Drupal\views\Tests;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\views\Views;
/**
@ -54,9 +55,25 @@ class TokenReplaceTest extends ViewUnitTestBase {
'[view:page-count]' => '1',
);
$base_bubbleable_metadata = BubbleableMetadata::createFromObject($view->storage);
$metadata_tests = [];
$metadata_tests['[view:label]'] = $base_bubbleable_metadata;
$metadata_tests['[view:description]'] = $base_bubbleable_metadata;
$metadata_tests['[view:id]'] = $base_bubbleable_metadata;
$metadata_tests['[view:title]'] = $base_bubbleable_metadata;
$metadata_tests['[view:url]'] = $base_bubbleable_metadata;
$metadata_tests['[view:total-rows]'] = $base_bubbleable_metadata;
$metadata_tests['[view:base-table]'] = $base_bubbleable_metadata;
$metadata_tests['[view:base-field]'] = $base_bubbleable_metadata;
$metadata_tests['[view:items-per-page]'] = $base_bubbleable_metadata;
$metadata_tests['[view:current-page]'] = $base_bubbleable_metadata;
$metadata_tests['[view:page-count]'] = $base_bubbleable_metadata;
foreach ($expected as $token => $expected_output) {
$output = $token_handler->replace($token, array('view' => $view));
$bubbleable_metadata = new BubbleableMetadata();
$output = $token_handler->replace($token, array('view' => $view), [], $bubbleable_metadata);
$this->assertIdentical($output, $expected_output, format_string('Token %token replaced correctly.', array('%token' => $token)));
$this->assertEqual($bubbleable_metadata, $metadata_tests[$token]);
}
}

View file

@ -8,6 +8,7 @@
namespace Drupal\views\Tests;
use Drupal\comment\Tests\CommentTestTrait;
use Drupal\Component\Utility\Xss;
use Drupal\views\Entity\View;
use Drupal\views\Views;
use Drupal\views\ViewExecutable;
@ -326,7 +327,7 @@ class ViewExecutableTest extends ViewUnitTestBase {
// Test the title methods.
$title = $this->randomString();
$view->setTitle($title);
$this->assertEqual($view->getTitle(), $title);
$this->assertEqual($view->getTitle(), Xss::filterAdmin($title));
}
/**

View file

@ -31,7 +31,7 @@ abstract class ViewUnitTestBase extends KernelTestBase {
*
* @var array
*/
public static $modules = array('system', 'views', 'views_test_config', 'views_test_data');
public static $modules = array('system', 'views', 'views_test_config', 'views_test_data', 'user');
/**
* {@inheritdoc}

View file

@ -133,6 +133,7 @@ class BasicTest extends WizardTestBase {
// Confirm that the block is available in the block administration UI.
$this->drupalGet('admin/structure/block/list/' . $this->config('system.theme')->get('default'));
$this->clickLinkPartialName('Place block');
$this->assertText($view3['label']);
// Place the block.

View file

@ -71,6 +71,7 @@ class ItemsPerPageTest extends WizardTestBase {
// Confirm that the block is listed in the block administration UI.
$this->drupalGet('admin/structure/block/list/' . $this->config('system.theme')->get('default'));
$this->clickLinkPartialName('Place block');
$this->assertText($view['label']);
// Place the block, visit a page that displays the block, and check that the

View file

@ -9,7 +9,6 @@ namespace Drupal\views;
use Drupal\Component\Utility\Html;
use Drupal\Core\Cache\Cache;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\DependencyInjection\DependencySerializationTrait;
use Drupal\Core\Form\FormState;
use Drupal\Core\Routing\RouteProviderInterface;
@ -1615,29 +1614,6 @@ class ViewExecutable implements \Serializable {
$this->is_attachment = FALSE;
}
/**
* Returns menu links from the view and the named display handler.
*
* @param string $display_id
* A display ID.
*
* @return array|bool
* The generated menu links for this view and display, FALSE if the call
* to ::setDisplay failed.
*/
public function getMenuLinks($display_id = NULL) {
// Prepare the view with the information we have. This was probably already
// called, but it's good to be safe.
if (!$this->setDisplay($display_id)) {
return FALSE;
}
// Execute the hook.
if (isset($this->display_handler)) {
return $this->display_handler->getMenuLinks();
}
}
/**
* Determine if the given user has access to the view. Note that
* this sets the display handler if it hasn't been.
@ -1897,7 +1873,7 @@ class ViewExecutable implements \Serializable {
public function getUrlInfo($display_id = '') {
$this->initDisplay();
if (!$this->display_handler instanceof DisplayRouterInterface) {
throw new \InvalidArgumentException(SafeMarkup::format('You cannot generate a URL for the display @display_id', ['@display_id' => $display_id]));
throw new \InvalidArgumentException("You cannot generate a URL for the display '$display_id'");
}
return $this->display_handler->getUrlInfo();
}

View file

@ -7,10 +7,13 @@
namespace Drupal\views_test_data\Cache;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\Context\CacheContextInterface;
/**
* Test cache context which uses a dynamic context coming from state.
*
* Cache context ID: 'views_test_cache_context'.
*/
class ViewsTestCacheContext implements CacheContextInterface {
@ -28,4 +31,11 @@ class ViewsTestCacheContext implements CacheContextInterface {
return \Drupal::state()->get('views_test_cache_context', 'George');
}
/**
* {@inheritdoc}
*/
public function getCacheableMetadata() {
return new CacheableMetadata();
}
}

View file

@ -7,11 +7,13 @@
namespace Drupal\Tests\views\Unit\Controller {
use Drupal\Core\Render\RenderContext;
use Drupal\Tests\UnitTestCase;
use Drupal\views\Ajax\ViewAjaxResponse;
use Drupal\views\Controller\ViewAjaxController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpFoundation\RequestStack;
/**
* @coversDefaultClass \Drupal\views\Controller\ViewAjaxController
@ -76,6 +78,11 @@ class ViewAjaxControllerTest extends UnitTestCase {
$elements['#attached'] = [];
return isset($elements['#markup']) ? $elements['#markup'] : '';
}));
$this->renderer->expects($this->any())
->method('executeInRenderContext')
->willReturnCallback(function (RenderContext $context, callable $callable) {
return $callable();
});
$this->currentPath = $this->getMockBuilder('Drupal\Core\Path\CurrentPathStack')
->disableOriginalConstructor()
->getMock();
@ -83,8 +90,23 @@ class ViewAjaxControllerTest extends UnitTestCase {
$this->viewAjaxController = new ViewAjaxController($this->viewStorage, $this->executableFactory, $this->renderer, $this->currentPath, $this->redirectDestination);
$request_stack = new RequestStack();
$request_stack->push(new Request());
$args = [
$this->getMock('\Drupal\Core\Controller\ControllerResolverInterface'),
$this->getMock('\Drupal\Core\Theme\ThemeManagerInterface'),
$this->getMock('\Drupal\Core\Render\ElementInfoManagerInterface'),
$this->getMock('\Drupal\Core\Render\RenderCacheInterface'),
$request_stack,
[
'required_cache_contexts' => [
'languages:language_interface',
'theme',
],
],
];
$this->renderer = $this->getMockBuilder('Drupal\Core\Render\Renderer')
->disableOriginalConstructor()
->setConstructorArgs($args)
->setMethods(NULL)
->getMock();
$container = new ContainerBuilder();

View file

@ -168,20 +168,12 @@ class FieldPluginBaseTest extends UnitTestCase {
* Sets up the unrouted url assembler and the link generator.
*/
protected function setUpUrlIntegrationServices() {
$config = $this->getMockBuilder('Drupal\Core\Config\ImmutableConfig')
->disableOriginalConstructor()
->getMock();
$config_factory = $this->getMock('\Drupal\Core\Config\ConfigFactoryInterface');
$config_factory->expects($this->any())
->method('get')
->willReturn($config);
$this->pathProcessor = $this->getMock('Drupal\Core\PathProcessor\OutboundPathProcessorInterface');
$this->unroutedUrlAssembler = new UnroutedUrlAssembler($this->requestStack, $config_factory, $this->pathProcessor);
$this->unroutedUrlAssembler = new UnroutedUrlAssembler($this->requestStack, $this->pathProcessor);
\Drupal::getContainer()->set('unrouted_url_assembler', $this->unroutedUrlAssembler);
$this->linkGenerator = new LinkGenerator($this->urlGenerator, $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'));
$this->linkGenerator = new LinkGenerator($this->urlGenerator, $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'), $this->renderer);
}
/**
@ -453,6 +445,7 @@ class FieldPluginBaseTest extends UnitTestCase {
'#type' => 'inline_template',
'#template' => 'base:test-path/' . explode('/', $path)[1],
'#context' => ['foo' => 123],
'#post_render' => [function() {}],
];
$this->renderer->expects($this->once())

View file

@ -13,7 +13,6 @@ use Drupal\views\Routing\ViewPageController;
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
use Symfony\Component\HttpFoundation\ParameterBag;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
use Symfony\Component\Routing\Route;
/**
@ -29,20 +28,6 @@ class ViewPageControllerTest extends UnitTestCase {
*/
public $pageController;
/**
* The mocked view storage.
*
* @var \Drupal\views\ViewStorage|\PHPUnit_Framework_MockObject_MockObject
*/
protected $storage;
/**
* The mocked view executable factory.
*
* @var \Drupal\views\ViewExecutableFactory|\PHPUnit_Framework_MockObject_MockObject
*/
protected $executableFactory;
/**
* A render array expected for every page controller render array result.
*
@ -56,23 +41,13 @@ class ViewPageControllerTest extends UnitTestCase {
];
protected function setUp() {
$this->storage = $this->getMockBuilder('Drupal\Core\Config\Entity\ConfigEntityStorage')
->disableOriginalConstructor()
->getMock();
$this->executableFactory = $this->getMockBuilder('Drupal\views\ViewExecutableFactory')
->disableOriginalConstructor()
->getMock();
$this->pageController = new ViewPageController($this->storage, $this->executableFactory);
$this->pageController = new ViewPageController();
}
/**
* Tests the page controller.
*/
public function testPageController() {
$this->storage->expects($this->never())
->method('load');
$build = [
'#type' => 'view',
'#name' => 'test_page_view',
@ -102,9 +77,6 @@ class ViewPageControllerTest extends UnitTestCase {
* Tests the page controller with arguments on a non overridden page view.
*/
public function testHandleWithArgumentsWithoutOverridden() {
$this->storage->expects($this->never())
->method('load');
$request = new Request();
$request->attributes->set('view_id', 'test_page_view');
$request->attributes->set('display_id', 'page_1');
@ -139,9 +111,6 @@ class ViewPageControllerTest extends UnitTestCase {
* Note: This test does not care about upcasting for now.
*/
public function testHandleWithArgumentsOnOverriddenRoute() {
$this->storage->expects($this->never())
->method('load');
$request = new Request();
$request->attributes->set('view_id', 'test_page_view');
$request->attributes->set('display_id', 'page_1');
@ -179,9 +148,6 @@ class ViewPageControllerTest extends UnitTestCase {
* are pulled in.
*/
public function testHandleWithArgumentsOnOverriddenRouteWithUpcasting() {
$this->storage->expects($this->never())
->method('load');
$request = new Request();
$request->attributes->set('view_id', 'test_page_view');
$request->attributes->set('display_id', 'page_1');

View file

@ -494,7 +494,9 @@ function template_preprocess_views_view_table(&$variables) {
'attributes' => array('title' => $title),
'query' => $query,
);
$variables['header'][$field]['content'] = \Drupal::l($label, new Url('<current>', [], $link_options));
// It is ok to specify no URL path here as we will always reload the
// current page.
$variables['header'][$field]['content'] = \Drupal::l($label, new Url('<none>', [], $link_options));
}
$variables['header'][$field]['default_classes'] = $fields[$field]->options['element_default_classes'];
@ -1050,6 +1052,10 @@ function template_preprocess_views_mini_pager(&$variables) {
}
$variables['items']['next']['attributes'] = new Attribute();
}
// This is is based on the entire current query string. We need to ensure
// cacheability is affected accordingly.
$variables['#cache']['contexts'][] = 'url.query_args';
}
/**

View file

@ -6,6 +6,7 @@
*/
use Drupal\Component\Utility\SafeMarkup;
use Drupal\Core\Render\BubbleableMetadata;
/**
* Implements hook_token_info().
@ -68,9 +69,7 @@ function views_token_info() {
/**
* Implements hook_tokens().
*/
function views_tokens($type, $tokens, array $data = array(), array $options = array()) {
$token_service = \Drupal::token();
function views_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
$url_options = array('absolute' => TRUE);
if (isset($options['language'])) {
$url_options['language'] = $options['language'];
@ -83,6 +82,8 @@ function views_tokens($type, $tokens, array $data = array(), array $options = ar
/** @var \Drupal\views\ViewExecutable $view */
$view = $data['view'];
$bubbleable_metadata->addCacheableDependency($view->storage);
foreach ($tokens as $name => $original) {
switch ($name) {
case 'label':
@ -104,7 +105,8 @@ function views_tokens($type, $tokens, array $data = array(), array $options = ar
case 'url':
if ($url = $view->getUrl()) {
$replacements[$original] = $url->setOptions($url_options)->toString();
$replacements[$original] = $url->setOptions($url_options)
->toString();
}
break;
case 'base-table':
@ -129,13 +131,6 @@ function views_tokens($type, $tokens, array $data = array(), array $options = ar
break;
}
}
// [view:url:*] nested tokens. This only works if Token module is installed.
if ($url_tokens = $token_service->findWithPrefix($tokens, 'url')) {
if ($path = $view->getUrl()) {
$replacements += $token_service->generate('url', $url_tokens, array('path' => $url->getInternalPath()), $options);
}
}
}
return $replacements;