Update to Drupal 8.1.5. For more information, see https://www.drupal.org/project/drupal/releases/8.1.5

This commit is contained in:
Pantheon Automation 2016-07-07 09:44:38 -07:00 committed by Greg Anderson
parent 13b6ca7cc2
commit 38ba7c357d
342 changed files with 7814 additions and 1534 deletions

View file

@ -34,6 +34,12 @@ views.display.page:
weight:
type: integer
label: 'Weight'
enabled:
type: boolean
label: 'Enabled'
expanded:
type: boolean
label: 'Expanded'
menu_name:
type: string
label: 'Menu name'

View file

@ -104,7 +104,7 @@ class ViewAjaxController implements ContainerInjectionInterface {
* The current request object.
*
* @return \Drupal\views\Ajax\ViewAjaxResponse
* The view response as ajax response.
* The view response as ajax response.
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
* Thrown when the view was not found.

View file

@ -57,6 +57,13 @@ class EntityViewsData implements EntityHandlerInterface, EntityViewsDataInterfac
*/
protected $fieldStorageDefinitions;
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManager;
/**
* Constructs an EntityViewsData object.
*
@ -112,6 +119,7 @@ class EntityViewsData implements EntityHandlerInterface, EntityViewsDataInterfac
$data = [];
$base_table = $this->entityType->getBaseTable() ?: $this->entityType->id();
$views_revision_base_table = NULL;
$revisionable = $this->entityType->isRevisionable();
$base_field = $this->entityType->getKey('id');
@ -235,6 +243,7 @@ class EntityViewsData implements EntityHandlerInterface, EntityViewsDataInterfac
// Load all typed data definitions of all fields. This should cover each of
// the entity base, revision, data tables.
$field_definitions = $this->entityManager->getBaseFieldDefinitions($this->entityType->id());
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
if ($table_mapping = $this->storage->getTableMapping($field_definitions)) {
// Fetch all fields that can appear in both the base table and the data
// table.
@ -257,6 +266,36 @@ class EntityViewsData implements EntityHandlerInterface, EntityViewsDataInterfac
$this->mapFieldDefinition($table, $field_name, $field_definitions[$field_name], $table_mapping, $data[$table]);
}
}
foreach ($field_definitions as $field_definition) {
if ($table_mapping->requiresDedicatedTableStorage($field_definition->getFieldStorageDefinition())) {
$table = $table_mapping->getDedicatedDataTableName($field_definition->getFieldStorageDefinition());
$data[$table]['table']['group'] = $this->entityType->getLabel();
$data[$table]['table']['provider'] = $this->entityType->getProvider();
$data[$table]['table']['join'][$views_base_table] = [
'left_field' => $base_field,
'field' => 'entity_id',
'extra' => [
['field' => 'deleted', 'value' => 0, 'numeric' => TRUE],
],
];
if ($revisionable) {
$revision_table = $table_mapping->getDedicatedRevisionTableName($field_definition->getFieldStorageDefinition());
$data[$revision_table]['table']['group'] = $this->t('@entity_type revision', ['@entity_type' => $this->entityType->getLabel()]);
$data[$revision_table]['table']['provider'] = $this->entityType->getProvider();
$data[$revision_table]['table']['join'][$views_revision_base_table] = [
'left_field' => $revision_field,
'field' => 'entity_id',
'extra' => [
['field' => 'deleted', 'value' => 0, 'numeric' => TRUE],
],
];
}
}
}
}
// Add the entity type key to each table generated.

View file

@ -26,7 +26,6 @@ class ViewsMenuLink extends MenuLinkBase implements ContainerFactoryPluginInterf
'enabled' => 1,
'title' => 1,
'description' => 1,
'metadata' => 1,
);
/**
@ -140,9 +139,9 @@ class ViewsMenuLink extends MenuLinkBase implements ContainerFactoryPluginInterf
$display = &$view->storage->getDisplay($view->current_display);
// Just save the title to the original view.
$changed = FALSE;
foreach ($new_definition_values as $key => $new_definition_value) {
if (isset($display['display_options']['menu'][$key]) && $display['display_options']['menu'][$key] != $new_definition_values[$key]) {
$display['display_options']['menu'][$key] = $new_definition_values[$key];
foreach ($overrides as $key => $new_definition_value) {
if (empty($display['display_options']['menu'][$key]) || $display['display_options']['menu'][$key] != $new_definition_value) {
$display['display_options']['menu'][$key] = $new_definition_value;
$changed = TRUE;
}
}

View file

@ -86,7 +86,7 @@ class ViewsHandlerManager extends DefaultPluginManager implements FallbackPlugin
if (isset($data[$field][$this->handlerType])) {
$definition = $data[$field][$this->handlerType];
foreach (array('group', 'title', 'title short', 'help', 'real field', 'real table', 'entity type', 'entity field') as $key) {
foreach (array('group', 'title', 'title short', 'label', 'help', 'real field', 'real table', 'entity type', 'entity field') as $key) {
if (!isset($definition[$key])) {
// First check the field level.
if (!empty($data[$field][$key])) {

View file

@ -816,4 +816,19 @@ abstract class HandlerBase extends PluginBase implements ViewsHandlerInterface {
$view->cacheSet();
}
/**
* Calculates options stored on the handler
*
* @param array $options
* The options stored in the handler
* @param array $form_state_options
* The newly submitted form state options.
*
* @return array
* The new options
*/
public function submitFormCalculateOptions(array $options, array $form_state_options) {
return $form_state_options + $options;
}
}

View file

@ -336,7 +336,7 @@ abstract class PluginBase extends ComponentPluginBase implements ContainerFactor
* @param $tokens
* Array of token => replacement_value items.
*
* @return String
* @return string
*/
protected function viewsTokenReplace($text, $tokens) {
if (!strlen($text)) {
@ -373,7 +373,8 @@ abstract class PluginBase extends ComponentPluginBase implements ContainerFactor
assert('preg_match(\'/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/\', $top) === 1', 'Tokens need to be valid Twig variables.');
$token_array = array(array_pop($parts) => $replacement);
foreach (array_reverse($parts) as $key) {
assert('preg_match(\'/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/\', $key) === 1', 'Tokens need to be valid Twig variables.');
// The key could also be numeric (array index) so allow that.
assert('is_numeric($key) || (preg_match(\'/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/\', $key) === 1)', 'Tokens need to be valid Twig variables.');
$token_array = array($key => $token_array);
}
if (!isset($twig_tokens[$top])) {

View file

@ -568,7 +568,7 @@ interface DisplayPluginInterface {
* Renders the exposed form as block.
*
* @return string|null
* The rendered exposed form as string or NULL otherwise.
* The rendered exposed form as string or NULL otherwise.
*/
public function viewExposedFormBlocks();

View file

@ -124,6 +124,7 @@ class Page extends PathPluginBase {
'title' => array('default' => ''),
'description' => array('default' => ''),
'weight' => array('default' => 0),
'enabled' => array('default' => TRUE),
'menu_name' => array('default' => 'main'),
'parent' => array('default' => ''),
'context' => array('default' => ''),

View file

@ -322,6 +322,8 @@ abstract class PathPluginBase extends DisplayPluginBase implements DisplayRouter
$links[$menu_link_id]['title'] = $menu['title'];
$links[$menu_link_id]['description'] = $menu['description'];
$links[$menu_link_id]['parent'] = $menu['parent'];
$links[$menu_link_id]['enabled'] = $menu['enabled'];
$links[$menu_link_id]['expanded'] = $menu['expanded'];
if (isset($menu['weight'])) {
$links[$menu_link_id]['weight'] = intval($menu['weight']);

View file

@ -2,7 +2,9 @@
namespace Drupal\views\Plugin\views\field;
use Drupal\Core\Entity\EntityMalformedException;
use Drupal\Core\Entity\EntityManagerInterface;
use Drupal\Core\Entity\Exception\UndefinedLinkTemplateException;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\ResultRow;
use Drupal\views\ViewExecutable;
@ -105,8 +107,16 @@ class EntityLabel extends FieldPluginBase {
$entity = $this->loadedReferencers[$type][$value];
if (!empty($this->options['link_to_entity'])) {
$this->options['alter']['make_link'] = TRUE;
$this->options['alter']['url'] = $entity->urlInfo();
try {
$this->options['alter']['url'] = $entity->toUrl();
$this->options['alter']['make_link'] = TRUE;
}
catch (UndefinedLinkTemplateException $e) {
$this->options['alter']['make_link'] = FALSE;
}
catch (EntityMalformedException $e) {
$this->options['alter']['make_link'] = FALSE;
}
}
return $this->sanitizeValue($entity->label());

View file

@ -472,7 +472,8 @@ class Field extends FieldPluginBase implements CacheableDependencyInterface, Mul
// Get the settings form.
$settings_form = array('#value' => array());
if ($formatter = $this->getFormatterInstance()) {
$format = isset($form_state->getUserInput()['options']['type']) ? $form_state->getUserInput()['options']['type'] : $this->options['type'];
if ($formatter = $this->getFormatterInstance($format)) {
$settings_form = $formatter->settingsForm($form, $form_state);
// Convert field UI selector states to work in the Views field form.
FormHelper::rewriteStatesSelector($settings_form, "fields[{$field->getName()}][settings_edit_form]", 'options');
@ -480,6 +481,21 @@ class Field extends FieldPluginBase implements CacheableDependencyInterface, Mul
$form['settings'] = $settings_form;
}
/**
* {@inheritdoc}
*/
public function submitFormCalculateOptions(array $options, array $form_state_options) {
// When we change the formatter type we don't want to keep any of the
// previous configured formatter settings, as there might be schema
// conflict.
unset($options['settings']);
$options = $form_state_options + $options;
if (!isset($options['settings'])) {
$options['settings'] = [];
}
return $options;
}
/**
* Provide options for multiple value fields.
*/
@ -937,13 +953,16 @@ class Field extends FieldPluginBase implements CacheableDependencyInterface, Mul
* @return \Drupal\Core\Field\FormatterInterface|null
* The field formatter instance.
*/
protected function getFormatterInstance() {
$settings = $this->options['settings'] + $this->formatterPluginManager->getDefaultSettings($this->options['type']);
protected function getFormatterInstance($format = NULL) {
if (!isset($format)) {
$format = $this->options['type'];
}
$settings = $this->options['settings'] + $this->formatterPluginManager->getDefaultSettings($format);
$options = [
'field_definition' => $this->getFieldDefinition(),
'configuration' => [
'type' => $this->options['type'],
'type' => $format,
'settings' => $settings,
'label' => '',
'weight' => 0,

View file

@ -140,7 +140,7 @@ interface FieldHandlerInterface extends ViewsHandlerInterface {
* by in the style settings.
*
* @return bool
* TRUE if this field handler is groupable, otherwise FALSE.
* TRUE if this field handler is groupable, otherwise FALSE.
*/
public function useStringGroupBy();
@ -215,7 +215,7 @@ interface FieldHandlerInterface extends ViewsHandlerInterface {
* Whether or not to use empty() to check the value.
*
* @return bool
* TRUE if the value is considered empty, FALSE otherwise.
* TRUE if the value is considered empty, FALSE otherwise.
*/
public function isValueEmpty($value, $empty_zero, $no_skip_empty = TRUE);

View file

@ -93,9 +93,14 @@ class BooleanOperator extends FilterPluginBase {
parent::init($view, $display, $options);
$this->value_value = $this->t('True');
if (isset($this->definition['label'])) {
$this->value_value = $this->definition['label'];
}
elseif (isset($this->definition['title'])) {
$this->value_value = $this->definition['title'];
}
if (isset($this->definition['accept null'])) {
$this->accept_null = (bool) $this->definition['accept null'];
}

View file

@ -303,24 +303,18 @@ class JoinPluginBase extends PluginBase implements JoinPluginInterface {
if (is_array($info['value']) && count($info['value']) == 1) {
$info['value'] = array_shift($info['value']);
}
if (is_array($info['value'])) {
// With an array of values, we need multiple placeholders and the
// 'IN' operator is implicit.
$local_arguments = array();
foreach ($info['value'] as $value) {
$placeholder_i = ':views_join_condition_' . $select_query->nextPlaceholder();
$local_arguments[$placeholder_i] = $value;
}
// We use an SA-CORE-2014-005 conformant placeholder for our array
// of values. Also, note that the 'IN' operator is implicit.
// @see https://www.drupal.org/node/2401615.
$operator = !empty($info['operator']) ? $info['operator'] : 'IN';
$placeholder = '( ' . implode(', ', array_keys($local_arguments)) . ' )';
$arguments += $local_arguments;
$placeholder = ':views_join_condition_' . $select_query->nextPlaceholder() . '[]';
$placeholder_sql = "( $placeholder )";
}
else {
// With a single value, the '=' operator is implicit.
$operator = !empty($info['operator']) ? $info['operator'] : '=';
$placeholder = ':views_join_condition_' . $select_query->nextPlaceholder();
$placeholder = $placeholder_sql = ':views_join_condition_' . $select_query->nextPlaceholder();
}
// Set 'field' as join table field if available or set 'left field' as
// join table field is not set.
@ -329,7 +323,7 @@ class JoinPluginBase extends PluginBase implements JoinPluginInterface {
// Allow the value to be set either with the 'value' element or
// with 'left_field'.
if (isset($info['left_field'])) {
$placeholder = "$left[alias].$info[left_field]";
$placeholder_sql = "$left[alias].$info[left_field]";
}
else {
$arguments[$placeholder] = $info['value'];
@ -340,7 +334,8 @@ class JoinPluginBase extends PluginBase implements JoinPluginInterface {
$join_table_field = "$left[alias].$info[left_field]";
$arguments[$placeholder] = $info['value'];
}
$extras[] = "$join_table_field $operator $placeholder";
// Render out the SQL fragment with parameters.
$extras[] = "$join_table_field $operator $placeholder_sql";
}
if ($extras) {

View file

@ -5,6 +5,7 @@ namespace Drupal\views\Plugin\views\query;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Database\Database;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\views\Plugin\views\display\DisplayPluginBase;
use Drupal\Core\Database\DatabaseExceptionWrapper;
@ -13,6 +14,7 @@ use Drupal\views\Plugin\views\HandlerBase;
use Drupal\views\ResultRow;
use Drupal\views\ViewExecutable;
use Drupal\views\Views;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Views query plugin for an SQL query.
@ -106,6 +108,40 @@ class Sql extends QueryPluginBase {
*/
protected $noDistinct;
/**
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* Constructs a Sql object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin_id for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->entityTypeManager = $entity_type_manager;
}
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('entity_type.manager')
);
}
/**
* {@inheritdoc}
*/
@ -1482,63 +1518,97 @@ class Sql extends QueryPluginBase {
foreach ($entity_information as $info) {
$entity_type = $info['entity_type'];
if (!isset($entity_types[$entity_type])) {
$entity_types[$entity_type] = \Drupal::entityManager()->getDefinition($entity_type);
$entity_types[$entity_type] = $this->entityTypeManager->getDefinition($entity_type);
}
}
// Assemble a list of entities to load.
$ids_by_type = array();
$entity_ids_by_type = [];
$revision_ids_by_type = [];
foreach ($entity_information as $info) {
$relationship_id = $info['relationship_id'];
$entity_type = $info['entity_type'];
/** @var \Drupal\Core\Entity\EntityTypeInterface $entity_info */
$entity_info = $entity_types[$entity_type];
$id_key = !$info['revision'] ? $entity_info->getKey('id') : $entity_info->getKey('revision');
$revision = $info['revision'];
$id_key = !$revision ? $entity_info->getKey('id') : $entity_info->getKey('revision');
$id_alias = $this->getFieldAlias($info['alias'], $id_key);
foreach ($results as $index => $result) {
// Store the entity id if it was found.
if (isset($result->{$id_alias}) && $result->{$id_alias} != '') {
$ids_by_type[$entity_type][$index][$relationship_id] = $result->$id_alias;
if ($revision) {
$revision_ids_by_type[$entity_type][$index][$relationship_id] = $result->$id_alias;
}
else {
$entity_ids_by_type[$entity_type][$index][$relationship_id] = $result->$id_alias;
}
}
}
}
// Load all entities and assign them to the correct result row.
foreach ($ids_by_type as $entity_type => $ids) {
foreach ($entity_ids_by_type as $entity_type => $ids) {
$entity_storage = $this->entityTypeManager->getStorage($entity_type);
$flat_ids = iterator_to_array(new \RecursiveIteratorIterator(new \RecursiveArrayIterator($ids)), FALSE);
// Drupal core currently has no way to load multiple revisions. Sad.
if (isset($entity_information[$entity_type]['revision']) && $entity_information[$entity_type]['revision'] === TRUE) {
$entities = array();
foreach ($flat_ids as $revision_id) {
$entity = entity_revision_load($entity_type, $revision_id);
if ($entity) {
$entities[$revision_id] = $entity;
}
$entities = $entity_storage->loadMultiple(array_unique($flat_ids));
$results = $this->_assignEntitiesToResult($ids, $entities, $results);
}
// Now load all revisions.
foreach ($revision_ids_by_type as $entity_type => $revision_ids) {
$entity_storage = $this->entityTypeManager->getStorage($entity_type);
$entities = [];
foreach ($revision_ids as $index => $revision_id_by_relationship) {
foreach ($revision_id_by_relationship as $revision => $revision_id) {
// Drupal core currently has no way to load multiple revisions.
$entity = $entity_storage->loadRevision($revision_id);
$entities[$revision_id] = $entity;
}
}
else {
$entities = entity_load_multiple($entity_type, $flat_ids);
}
foreach ($ids as $index => $relationships) {
foreach ($relationships as $relationship_id => $entity_id) {
if (isset($entities[$entity_id])) {
$entity = $entities[$entity_id];
}
else {
$entity = NULL;
}
$results = $this->_assignEntitiesToResult($revision_ids, $entities, $results);
}
}
if ($relationship_id == 'none') {
$results[$index]->_entity = $entity;
}
else {
$results[$index]->_relationship_entities[$relationship_id] = $entity;
}
/**
* Sets entities onto the view result row objects.
*
* This method takes into account the relationship in which the entity was
* needed in the first place.
*
* @param mixed[][] $ids
* A two dimensional array of identifiers (entity ID / revision ID) keyed by
* relationship.
* @param \Drupal\Core\Entity\EntityInterface[] $entities
* An array of entities keyed by their identified (entity ID / revision ID).
* @param \Drupal\views\ResultRow[] $results
* The entire views result.
*
* @return \Drupal\views\ResultRow[]
* The changed views results.
*/
protected function _assignEntitiesToResult($ids, array $entities, array $results) {
foreach ($ids as $index => $relationships) {
foreach ($relationships as $relationship_id => $id) {
if (isset($entities[$id])) {
$entity = $entities[$id];
}
else {
$entity = NULL;
}
if ($relationship_id == 'none') {
$results[$index]->_entity = $entity;
}
else {
$results[$index]->_relationship_entities[$relationship_id] = $entity;
}
}
}
return $results;
}
/**

View file

@ -152,7 +152,7 @@ class DisplayPageWebTest extends PluginTestBase {
* @param string $path
* Path that will be set as the view page display path.
*
* @return boolean
* @return bool
* Assertion result.
*/
public function assertPagePath($path) {

View file

@ -136,7 +136,7 @@ class NumericFormatPluralTest extends ViewTestBase {
* Creates and saves a test file.
*
* @return \Drupal\Core\Entity\EntityInterface
* A file entity.
* A file entity.
*/
protected function createFile() {
// Create a new file entity.

View file

@ -2374,7 +2374,7 @@ class ViewExecutable implements \Serializable {
* Returns whether admin links should be rendered on the view.
*
* @return bool
* TRUE if admin links should be rendered, else FALSE.
* TRUE if admin links should be rendered, else FALSE.
*/
public function getShowAdminLinks() {
if (!isset($this->showAdminLinks)) {

View file

@ -295,8 +295,8 @@ class Views {
* If TRUE, the list of views is sorted ascending.
*
* @return array
* an associative array for use in select.
* - key: view name and display id separated by ':', or the view name only
* An associative array for use in select.
* - key: view name and display id separated by ':', or the view name only.
*/
public static function getViewsAsOptions($views_only = FALSE, $filter = 'all', $exclude_view = NULL, $optgroup = FALSE, $sort = FALSE) {

View file

@ -0,0 +1,346 @@
langcode: en
status: true
dependencies:
module:
- node
- user
id: base_and_revision
label: base_and_revision
module: views
description: ''
tag: ''
base_table: node_field_revision
base_field: vid
core: 8.x
display:
default:
display_plugin: default
id: default
display_title: Master
position: 0
display_options:
access:
type: perm
options:
perm: 'view all revisions'
cache:
type: none
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: mini
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous:
next:
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
fields:
nid:
id: nid
table: node_field_revision
field: nid
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: number_integer
settings:
thousand_separator: ''
prefix_suffix: true
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
entity_type: node
entity_field: nid
plugin_id: field
vid:
id: vid
table: node_field_revision
field: vid
relationship: none
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: number_integer
settings:
thousand_separator: ''
prefix_suffix: true
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
entity_type: node
entity_field: vid
plugin_id: field
vid_1:
id: vid_1
table: node_field_data
field: vid
relationship: nid
group_type: group
admin_label: ''
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: number_integer
settings:
thousand_separator: ''
prefix_suffix: true
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
entity_type: node
entity_field: vid
plugin_id: field
filters:
vid:
id: vid
table: node_field_revision
field: vid
relationship: none
group_type: group
admin_label: ''
operator: '='
value:
min: ''
max: ''
value: '3'
group: 1
exposed: false
expose:
operator_id: ''
label: ''
description: ''
use_operator: false
operator: ''
identifier: ''
required: false
remember: false
multiple: false
remember_roles:
authenticated: authenticated
is_grouped: false
group_info:
label: ''
description: ''
identifier: ''
optional: true
widget: select
multiple: false
remember: false
default_group: All
default_group_multiple: { }
group_items: { }
entity_type: node
entity_field: vid
plugin_id: numeric
sorts: { }
header: { }
footer: { }
empty: { }
relationships:
nid:
id: nid
table: node_field_revision
field: nid
relationship: none
group_type: group
admin_label: Node
required: false
entity_type: node
entity_field: nid
plugin_id: standard
arguments: { }
display_extenders: { }
rendering_language: en
cache_metadata:
max-age: -1
contexts:
- 'languages:language_interface'
- url.query_args
- 'user.node_grants:view'
- user.permissions
tags: { }

View file

@ -0,0 +1,45 @@
langcode: en
status: true
dependencies:
module:
- entity_test
id: test_entity_multivalue_basefield
label: ''
module: views
description: ''
tag: ''
base_table: entity_test_multivalue_basefield
base_field: id
core: '8'
display:
default:
display_plugin: default
id: default
display_title: Master
position: 0
display_options:
fields:
id:
id: id
table: entity_test_multivalue_basefield
field: nid
relationship: none
plugin_id: field
entity_type: entity_test_multivalue_basefield
entity_field: id
name:
id: name
table: entity_test_multivalue_basefield__name
field: name
plugin_id: field
entity_type: entity_test_multivalue_basefield
entity_field: name
defaults:
fields: false
filters: false
sorts:
id:
id: id
table: entity_test_multivalue_basefield
field: id
order: asc

View file

@ -0,0 +1,188 @@
langcode: en
status: true
dependencies:
config:
- field.storage.node.body
module:
- node
- text
- user
id: test_field_body
label: test_field_body
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
core: 8.x
display:
default:
display_plugin: default
id: default
display_title: Master
position: 0
display_options:
access:
type: perm
options:
perm: 'access content'
cache:
type: tag
options: { }
query:
type: views_query
options:
disable_sql_rewrite: false
distinct: false
replica: false
query_comment: ''
query_tags: { }
exposed_form:
type: basic
options:
submit_button: Apply
reset_button: false
reset_button_label: Reset
exposed_sorts_label: 'Sort by'
expose_sort_order: true
sort_asc_label: Asc
sort_desc_label: Desc
pager:
type: mini
options:
items_per_page: 10
offset: 0
id: 0
total_pages: null
expose:
items_per_page: false
items_per_page_label: 'Items per page'
items_per_page_options: '5, 10, 25, 50'
items_per_page_options_all: false
items_per_page_options_all_label: '- All -'
offset: false
offset_label: Offset
tags:
previous:
next:
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
inline: { }
separator: ''
hide_empty: false
default_field_elements: true
fields:
body:
id: body
table: node__body
field: body
relationship: none
group_type: group
admin_label: 'Body field'
label: ''
exclude: false
alter:
alter_text: false
text: ''
make_link: false
path: ''
absolute: false
external: false
replace_spaces: false
path_case: none
trim_whitespace: false
alt: ''
rel: ''
link_class: ''
prefix: ''
suffix: ''
target: ''
nl2br: false
max_length: 0
word_boundary: true
ellipsis: true
more_link: false
more_link_text: ''
more_link_path: ''
strip_tags: false
trim: false
preserve_tags: ''
html: false
element_type: ''
element_class: ''
element_label_type: ''
element_label_class: ''
element_label_colon: false
element_wrapper_type: ''
element_wrapper_class: ''
element_default_classes: true
empty: ''
hide_empty: false
empty_zero: false
hide_alter_empty: true
click_sort_column: value
type: text_default
settings: { }
group_column: value
group_columns: { }
group_rows: true
delta_limit: 0
delta_offset: 0
delta_reversed: false
delta_first_last: false
multi_type: separator
separator: ', '
field_api_classes: false
plugin_id: field
filters:
status:
value: true
table: node_field_data
field: status
plugin_id: boolean
entity_type: node
entity_field: status
id: status
expose:
operator: ''
group: 1
sorts:
created:
id: created
table: node_field_data
field: created
order: DESC
entity_type: node
entity_field: created
plugin_id: date
relationship: none
group_type: group
admin_label: ''
exposed: false
expose:
label: ''
granularity: second
header: { }
footer: { }
empty: { }
relationships: { }
arguments: { }
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url.query_args
- 'user.node_grants:view'
- user.permissions
tags:
- 'config:field.storage.node.body'

View file

@ -0,0 +1,105 @@
<?php
namespace Drupal\Tests\views\FunctionalJavascript\Plugin\views\Handler;
use Drupal\config\Tests\SchemaCheckTestTrait;
use Drupal\field\Entity\FieldConfig;
use Drupal\FunctionalJavascriptTests\JavascriptTestBase;
use Drupal\node\Entity\NodeType;
use Drupal\views\Tests\ViewTestData;
/**
* Tests the field field handler UI.
*
* @group views
*/
class FieldTest extends JavascriptTestBase {
use SchemaCheckTestTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['node', 'views', 'views_ui', 'views_test_config'];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_field_body'];
/**
* The account.
*
* @var \Drupal\user\UserInterface
*/
protected $account;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
ViewTestData::createTestViews(get_class($this), ['views_test_config']);
// Disable automatic live preview to make the sequence of calls clearer.
\Drupal::configFactory()->getEditable('views.settings')->set('ui.always_live_preview', FALSE)->save();
$this->account = $this->drupalCreateUser(['administer views']);
$this->drupalLogin($this->account);
NodeType::create([
'type' => 'page',
])->save();
FieldConfig::create([
'entity_type' => 'node',
'field_name' => 'body',
'bundle' => 'page',
])->save();
}
public function testFormatterChanging() {
$web_assert = $this->assertSession();
$url = '/admin/structure/views/view/test_field_body';
$this->drupalGet($url);
$page = $this->getSession()->getPage();
$page->clickLink('Body field');
$web_assert->assertWaitOnAjaxRequest();
$page->fillField('options[type]', 'text_trimmed');
// Add a value to the trim_length setting.
$web_assert->assertWaitOnAjaxRequest();
$page->fillField('options[settings][trim_length]', '700');
$apply_button = $page->find('css', '.views-ui-dialog button.button--primary');
$this->assertTrue(!empty($apply_button));
$apply_button->press();
$web_assert->assertWaitOnAjaxRequest();
// Save the page.
$save_button = $page->find('css', '#edit-actions-submit');
$save_button->press();
// Set the body field back to 'default' and test that the trim_length
// settings are not in the config.
$this->drupalGet($url);
$page->clickLink('Body field');
$web_assert->assertWaitOnAjaxRequest();
$page->fillField('options[type]', 'text_default');
$web_assert->assertWaitOnAjaxRequest();
$apply_button = $page->find('css', '.views-ui-dialog button.button--primary');
$apply_button->press();
$web_assert->assertWaitOnAjaxRequest();
// Save the page.
$save_button = $page->find('css', '#edit-actions-submit');
$save_button->press();
$this->assertConfigSchemaByName('views.view.test_field_body');
}
}

View file

@ -0,0 +1,54 @@
<?php
namespace Drupal\Tests\views\Kernel\Entity;
use Drupal\entity_test\Entity\EntityTestMultiValueBasefield;
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
use Drupal\views\Views;
/**
* Tests entity views with multivalue base fields.
*
* @group views
*/
class EntityViewsWithMultivalueBasefieldTest extends ViewsKernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['entity_test'];
/**
* {@inheritdoc}
*/
public static $testViews = ['test_entity_multivalue_basefield'];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
$this->installEntitySchema('entity_test_multivalue_basefield');
}
/**
* Tests entity views with multivalue base fields.
*/
public function testView() {
EntityTestMultiValueBasefield::create([
'name' => 'test',
])->save();
EntityTestMultiValueBasefield::create([
'name' => ['test2', 'test3'],
])->save();
$view = Views::getView('test_entity_multivalue_basefield');
$view->execute();
$this->assertIdenticalResultset($view, [
['name' => ['test']],
['name' => ['test2', 'test3']],
], ['name' => 'name']);
}
}

View file

@ -0,0 +1,98 @@
<?php
namespace Drupal\Tests\views\Kernel\Plugin\Display;
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
/**
* Menu link test.
*
* @group views
*/
class ViewsMenuLinkTest extends ViewsKernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'menu_ui',
'user',
'views'
];
/**
* {@inheritdoc}
*/
public static $testViews = ['test_page_display_menu'];
/**
* The entity manager.
*
* @var \Drupal\Core\Entity\EntityManagerInterface
*/
protected $entityManger;
/**
* The menu link manager.
*
* @var \Drupal\Core\Menu\MenuLinkManagerInterface
*/
protected $menuLinkManager;
/**
* The menu link overrides.
*
* @var \Drupal\Core\Menu\StaticMenuLinkOverridesInterface
*/
protected $menuLinkOverrides;
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
$this->entityManger = $this->container->get('entity.manager');
$this->menuLinkManager = $this->container->get('plugin.manager.menu.link');
$this->menuLinkOverrides = $this->container->get('menu_link.static.overrides');
}
/**
* Test views internal menu link options.
*/
public function testMenuLinkOverrides() {
// Link from views module.
$views_link = $this->menuLinkManager->getDefinition('views_view:views.test_page_display_menu.page_3');
$this->assertTrue($views_link['enabled'], 'Menu link is enabled.');
$this->assertFalse($views_link['expanded'], 'Menu link is not expanded.');
$views_link['enabled'] = 0;
$views_link['expanded'] = 1;
$this->menuLinkManager->updateDefinition($views_link['id'], $views_link);
$views_link = $this->menuLinkManager->getDefinition($views_link['id']);
$this->assertFalse($views_link['enabled'], 'Menu link is disabled.');
$this->assertTrue($views_link['expanded'], 'Menu link is expanded.');
$this->menuLinkManager->rebuild();
$this->assertFalse($views_link['enabled'], 'Menu link is disabled.');
$this->assertTrue($views_link['expanded'], 'Menu link is expanded.');
// Link from user module.
$user_link = $this->menuLinkManager->getDefinition('user.page');
$this->assertTrue($user_link['enabled'], 'Menu link is enabled.');
$user_link['enabled'] = 0;
$views_link['expanded'] = 1;
$this->menuLinkManager->updateDefinition($user_link['id'], $user_link);
$this->assertFalse($user_link['enabled'], 'Menu link is disabled.');
$this->menuLinkManager->rebuild();
$this->assertFalse($user_link['enabled'], 'Menu link is disabled.');
$this->menuLinkOverrides->reload();
$views_link = $this->menuLinkManager->getDefinition('views_view:views.test_page_display_menu.page_3');
$this->assertFalse($views_link['enabled'], 'Menu link is disabled.');
$this->assertTrue($views_link['expanded'], 'Menu link is expanded.');
$user_link = $this->menuLinkManager->getDefinition('user.page');
$this->assertFalse($user_link['enabled'], 'Menu link is disabled.');
}
}

View file

@ -172,7 +172,8 @@ class JoinTest extends RelationshipJoinTestBase {
$join_info = $tables['users4'];
$this->assertTrue(strpos($join_info['condition'], "views_test_data.uid = users4.uid") !== FALSE, 'Make sure the join condition appears in the query.');
$this->assertTrue(strpos($join_info['condition'], "users4.name = :views_join_condition_2") !== FALSE, 'Make sure the first extra join condition appears in the query.');
$this->assertTrue(strpos($join_info['condition'], "users4.name IN ( :views_join_condition_3, :views_join_condition_4, :views_join_condition_5 )") !== FALSE, 'The IN condition for the join is properly formed.');
$this->assertTrue(strpos($join_info['condition'], "users4.name IN ( :views_join_condition_3[] )") !== FALSE, 'The IN condition for the join is properly formed.');
$this->assertEqual($join_info['arguments'][':views_join_condition_3[]'], array($random_name_2, $random_name_3, $random_name_4), 'Make sure the IN arguments are still part of an array.');
// Test that all the conditions are properly built.
$configuration['extra'] = array(
@ -197,8 +198,8 @@ class JoinTest extends RelationshipJoinTestBase {
$tables = $query->getTables();
$join_info = $tables['users5'];
$this->assertTrue(strpos($join_info['condition'], "views_test_data.uid = users5.uid") !== FALSE, 'Make sure the join condition appears in the query.');
$this->assertTrue(strpos($join_info['condition'], "users5.langcode = :views_join_condition_6") !== FALSE, 'Make sure the first extra join condition appears in the query.');
$this->assertTrue(strpos($join_info['condition'], "views_test_data.status = :views_join_condition_7") !== FALSE, 'Make sure the second extra join condition appears in the query.');
$this->assertTrue(strpos($join_info['condition'], "users5.langcode = :views_join_condition_4") !== FALSE, 'Make sure the first extra join condition appears in the query.');
$this->assertTrue(strpos($join_info['condition'], "views_test_data.status = :views_join_condition_5") !== FALSE, 'Make sure the second extra join condition appears in the query.');
$this->assertTrue(strpos($join_info['condition'], "users5.name = views_test_data.name") !== FALSE, 'Make sure the third extra join condition appears in the query.');
$this->assertEqual(array_values($join_info['arguments']), array('en', 0), 'Make sure the arguments are in the right order');
}

View file

@ -5,11 +5,11 @@
* Contains \Drupal\views\Tests\Plugin\PluginBaseTest.
*/
namespace Drupal\views\Tests\Plugin;
namespace Drupal\Tests\views\Kernel\Plugin;
use Drupal\Core\Render\RenderContext;
use Drupal\Core\Render\Markup;
use Drupal\simpletest\KernelTestBase;
use Drupal\KernelTests\KernelTestBase;
use Drupal\views\Plugin\views\PluginBase;
/**
@ -55,6 +55,16 @@ class PluginBaseTest extends KernelTestBase {
});
$this->assertIdentical($result, 'first comes before second');
// Test tokens with numeric indexes.
$text = '{{ argument.0.first }} comes before {{ argument.1.second }}';
$tokens = ['{{ argument.0.first }}' => 'first', '{{ argument.1.second }}' => 'second'];
$result = \Drupal::service('renderer')->executeInRenderContext(new RenderContext(), function () use ($text, $tokens) {
return $this->testPluginBase->viewsTokenReplace($text, $tokens);
});
$this->assertIdentical($result, 'first comes before second');
}
/**

View file

@ -0,0 +1,83 @@
<?php
namespace Drupal\Tests\views\Kernel\Plugin;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
use Drupal\views\Views;
/**
* Tests the loading of entities and entity revisions.
*
* @group views
*
* @see \Drupal\views\Plugin\views\query\Sql
*/
class SqlEntityLoadingTest extends ViewsKernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['node', 'user'];
/**
* {@inheritdoc}
*/
public static $testViews = ['base_and_revision'];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
$this->installEntitySchema('node');
$this->installEntitySchema('user');
$this->installSchema('node', 'node_access');
}
public function testViewWithNonDefaultForwardRevision() {
$node_type = NodeType::create([
'type' => 'page',
]);
$node_type->save();
$node = Node::create([
'type' => 'page',
'title' => 'test title',
]);
$node->save();
// Creates the first revision, which is set as default.
$revision = clone $node;
$revision->setNewRevision(TRUE);
$revision->isDefaultRevision(TRUE);
$revision->save();
// Creates the second revision, which is not set as default.
$revision2 = clone $node;
$revision2->setNewRevision(TRUE);
$revision2->isDefaultRevision(FALSE);
$revision2->save();
$view = Views::getView('base_and_revision');
$view->execute();
$expected = [
[
'nid' => $node->id(),
// The default revision ID.
'vid_1' => $revision->getRevisionId(),
// THe latest revision ID.
'vid' => $revision2->getRevisionId(),
],
];
$this->assertIdenticalResultset($view, $expected, [
'node_field_data_node_field_revision_nid' => 'nid',
'vid_1' => 'vid_1',
'vid' => 'vid',
]);
}
}

View file

@ -11,7 +11,7 @@ use Drupal\views\Tests\ViewTestData;
/**
* Defines a base class for Views kernel testing.
*/
class ViewsKernelTestBase extends KernelTestBase {
abstract class ViewsKernelTestBase extends KernelTestBase {
use ViewResultAssertionTrait;

View file

@ -11,6 +11,7 @@ use Drupal\Core\Config\Entity\ConfigEntityType;
use Drupal\Core\Entity\ContentEntityType;
use Drupal\Core\Entity\EntityType;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Entity\Sql\DefaultTableMapping;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
use Drupal\Core\Field\Plugin\Field\FieldType\IntegerItem;
@ -160,6 +161,12 @@ class EntityViewsDataTest extends UnitTestCase {
->setTranslatable(TRUE)
->setSetting('max_length', 255);
// A base field with cardinality > 1
$base_fields['string'] = BaseFieldDefinition::create('string')
->setLabel('Strong')
->setTranslatable(TRUE)
->setCardinality(2);
foreach ($base_fields as $name => $base_field) {
$base_field->setName($name);
}
@ -376,6 +383,10 @@ class EntityViewsDataTest extends UnitTestCase {
$homepage_field_storage_definition->expects($this->any())
->method('getSchema')
->willReturn(UriItem::schema($homepage_field_storage_definition));
$string_field_storage_definition = $this->getMock('Drupal\Core\Field\FieldStorageDefinitionInterface');
$string_field_storage_definition->expects($this->any())
->method('getSchema')
->willReturn(StringItem::schema($string_field_storage_definition));
// Setup the user_id entity reference field.
$this->entityManager->expects($this->any())
@ -411,6 +422,7 @@ class EntityViewsDataTest extends UnitTestCase {
'name' => $name_field_storage_definition,
'description' => $description_field_storage_definition,
'homepage' => $homepage_field_storage_definition,
'string' => $string_field_storage_definition,
'user_id' => $user_id_field_storage_definition,
'revision_id' => $revision_id_field_storage_definition,
]);
@ -435,10 +447,12 @@ class EntityViewsDataTest extends UnitTestCase {
['entity_test', $base_field_definitions],
]));
// Setup the table mapping.
$table_mapping = $this->getMock('Drupal\Core\Entity\Sql\TableMappingInterface');
$table_mapping = $this->getMockBuilder(DefaultTableMapping::class)
->disableOriginalConstructor()
->getMock();
$table_mapping->expects($this->any())
->method('getTableNames')
->willReturn(['entity_test']);
->willReturn(['entity_test', 'entity_test__string']);
$table_mapping->expects($this->any())
->method('getColumnNames')
->willReturnMap([
@ -450,12 +464,26 @@ class EntityViewsDataTest extends UnitTestCase {
['description', ['value' => 'description__value', 'format' => 'description__format']],
['homepage', ['value' => 'homepage']],
['user_id', ['target_id' => 'user_id']],
['string', ['value' => 'value']],
]);
$table_mapping->expects($this->any())
->method('getFieldNames')
->willReturnMap([
['entity_test', ['id', 'uuid', 'type', 'langcode', 'name', 'description', 'homepage', 'user_id']]
['entity_test', ['id', 'uuid', 'type', 'langcode', 'name', 'description', 'homepage', 'user_id']],
['entity_test__string', ['string']],
]);
$table_mapping->expects($this->any())
->method('requiresDedicatedTableStorage')
->willReturnCallback(function (BaseFieldDefinition $base_field) {
return $base_field->getName() === 'string';
});
$table_mapping->expects($this->any())
->method('getDedicatedDataTableName')
->willReturnCallback(function (BaseFieldDefinition $base_field) {
if ($base_field->getName() === 'string') {
return 'entity_test__string';
}
});
$this->entityStorage->expects($this->once())
->method('getTableMapping')
@ -492,6 +520,18 @@ class EntityViewsDataTest extends UnitTestCase {
$relationship = $data['entity_test']['user_id']['relationship'];
$this->assertEquals('users_field_data', $relationship['base']);
$this->assertEquals('uid', $relationship['base field']);
$this->assertStringField($data['entity_test__string']['string']);
$this->assertField($data['entity_test__string']['string'], 'string');
$this->assertEquals([
'left_field' => 'id',
'field' => 'entity_id',
'extra' => [[
'field' => 'deleted',
'value' => 0,
'numeric' => TRUE,
]],
], $data['entity_test__string']['table']['join']['entity_test']);
}
/**
@ -529,10 +569,12 @@ class EntityViewsDataTest extends UnitTestCase {
$this->viewsData->setEntityType($entity_type);
// Setup the table mapping.
$table_mapping = $this->getMock('Drupal\Core\Entity\Sql\TableMappingInterface');
$table_mapping = $this->getMockBuilder(DefaultTableMapping::class)
->disableOriginalConstructor()
->getMock();
$table_mapping->expects($this->any())
->method('getTableNames')
->willReturn(['entity_test_mul', 'entity_test_mul_property_data']);
->willReturn(['entity_test_mul', 'entity_test_mul_property_data', 'entity_test_mul__string']);
$table_mapping->expects($this->any())
->method('getColumnNames')
->willReturnMap([
@ -544,12 +586,14 @@ class EntityViewsDataTest extends UnitTestCase {
['description', ['value' => 'description__value', 'format' => 'description__format']],
['homepage', ['value' => 'homepage']],
['user_id', ['target_id' => 'user_id']],
['string', ['value' => 'value']],
]);
$table_mapping->expects($this->any())
->method('getFieldNames')
->willReturnMap([
['entity_test_mul', ['uuid']],
['entity_test_mul_property_data', ['id', 'type', 'langcode', 'name', 'description', 'homepage', 'user_id']],
['entity_test_mul__string', ['string']],
]);
$table_mapping->expects($this->any())
@ -560,6 +604,18 @@ class EntityViewsDataTest extends UnitTestCase {
}
return 'entity_test_mul_property_data';
});
$table_mapping->expects($this->any())
->method('requiresDedicatedTableStorage')
->willReturnCallback(function (BaseFieldDefinition $base_field) {
return $base_field->getName() === 'string';
});
$table_mapping->expects($this->any())
->method('getDedicatedDataTableName')
->willReturnCallback(function (BaseFieldDefinition $base_field) {
if ($base_field->getName() === 'string') {
return 'entity_test_mul__string';
}
});
$this->entityStorage->expects($this->once())
->method('getTableMapping')
@ -619,6 +675,18 @@ class EntityViewsDataTest extends UnitTestCase {
$relationship = $data['entity_test_mul_property_data']['user_id']['relationship'];
$this->assertEquals('users_field_data', $relationship['base']);
$this->assertEquals('uid', $relationship['base field']);
$this->assertStringField($data['entity_test_mul__string']['string']);
$this->assertField($data['entity_test_mul__string']['string'], 'string');
$this->assertEquals([
'left_field' => 'id',
'field' => 'entity_id',
'extra' => [[
'field' => 'deleted',
'value' => 0,
'numeric' => TRUE,
]],
], $data['entity_test_mul__string']['table']['join']['entity_test_mul']);
}
/**
@ -650,10 +718,12 @@ class EntityViewsDataTest extends UnitTestCase {
$this->viewsData->setEntityType($entity_type);
// Setup the table mapping.
$table_mapping = $this->getMock('Drupal\Core\Entity\Sql\TableMappingInterface');
$table_mapping = $this->getMockBuilder(DefaultTableMapping::class)
->disableOriginalConstructor()
->getMock();
$table_mapping->expects($this->any())
->method('getTableNames')
->willReturn(['entity_test_mulrev', 'entity_test_mulrev_revision', 'entity_test_mulrev_property_data', 'entity_test_mulrev_property_revision']);
->willReturn(['entity_test_mulrev', 'entity_test_mulrev_revision', 'entity_test_mulrev_property_data', 'entity_test_mulrev_property_revision', 'entity_test_mulrev__string', 'entity_test_mulrev_revision__string']);
$table_mapping->expects($this->any())
->method('getColumnNames')
->willReturnMap([
@ -666,6 +736,7 @@ class EntityViewsDataTest extends UnitTestCase {
['homepage', ['value' => 'homepage']],
['user_id', ['target_id' => 'user_id']],
['revision_id', ['value' => 'id']],
['string', ['value' => 'value']],
]);
$table_mapping->expects($this->any())
->method('getFieldNames')
@ -674,7 +745,29 @@ class EntityViewsDataTest extends UnitTestCase {
['entity_test_mulrev_revision', ['id', 'revision_id', 'langcode']],
['entity_test_mulrev_property_data', ['id', 'revision_id', 'langcode', 'name', 'description', 'homepage', 'user_id']],
['entity_test_mulrev_property_revision', ['id', 'revision_id', 'langcode', 'name', 'description', 'homepage', 'user_id']],
['entity_test_mulrev__string', ['string']],
['entity_test_mulrev_revision__string', ['string']],
]);
$table_mapping->expects($this->any())
->method('requiresDedicatedTableStorage')
->willReturnCallback(function (BaseFieldDefinition $base_field) {
return $base_field->getName() === 'string';
});
$table_mapping->expects($this->any())
->method('getDedicatedDataTableName')
->willReturnCallback(function (BaseFieldDefinition $base_field) {
if ($base_field->getName() === 'string') {
return 'entity_test_mulrev__string';
}
});
$table_mapping->expects($this->any())
->method('getDedicatedRevisionTableName')
->willReturnCallback(function (BaseFieldDefinition $base_field) {
if ($base_field->getName() === 'string') {
return 'entity_test_mulrev_revision__string';
}
});
$table_mapping->expects($this->any())
->method('getFieldTableName')
@ -767,6 +860,30 @@ class EntityViewsDataTest extends UnitTestCase {
$relationship = $data['entity_test_mulrev_property_revision']['user_id']['relationship'];
$this->assertEquals('users_field_data', $relationship['base']);
$this->assertEquals('uid', $relationship['base field']);
$this->assertStringField($data['entity_test_mulrev__string']['string']);
$this->assertField($data['entity_test_mulrev__string']['string'], 'string');
$this->assertEquals([
'left_field' => 'id',
'field' => 'entity_id',
'extra' => [[
'field' => 'deleted',
'value' => 0,
'numeric' => TRUE,
]],
], $data['entity_test_mulrev__string']['table']['join']['entity_test_mulrev_property_data']);
$this->assertStringField($data['entity_test_mulrev_revision__string']['string']);
$this->assertField($data['entity_test_mulrev_revision__string']['string'], 'string');
$this->assertEquals([
'left_field' => 'revision_id',
'field' => 'entity_id',
'extra' => [[
'field' => 'deleted',
'value' => 0,
'numeric' => TRUE,
]],
], $data['entity_test_mulrev_revision__string']['table']['join']['entity_test_mulrev_property_revision']);
}
/**

View file

@ -2,9 +2,18 @@
namespace Drupal\Tests\views\Unit\Plugin\query;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityType;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Tests\UnitTestCase;
use Drupal\views\Plugin\views\query\Sql;
use Drupal\views\Plugin\views\relationship\RelationshipPluginBase;
use Drupal\views\ResultRow;
use Drupal\views\ViewEntityInterface;
use Drupal\views\ViewExecutable;
use Drupal\views\ViewsData;
use Symfony\Component\DependencyInjection\ContainerBuilder;
/**
* @coversDefaultClass \Drupal\views\Plugin\views\query\Sql
@ -19,8 +28,9 @@ class SqlTest extends UnitTestCase {
*/
public function testGetCacheTags() {
$view = $this->prophesize('Drupal\views\ViewExecutable')->reveal();
$entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
$query = new Sql([], 'sql', []);
$query = new Sql([], 'sql', [], $entity_type_manager->reveal());
$query->view = $view;
$result = [];
@ -64,8 +74,9 @@ class SqlTest extends UnitTestCase {
*/
public function testGetCacheMaxAge() {
$view = $this->prophesize('Drupal\views\ViewExecutable')->reveal();
$entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
$query = new Sql([], 'sql', []);
$query = new Sql([], 'sql', [], $entity_type_manager->reveal());
$query->view = $view;
$view->result = [];
@ -98,4 +109,427 @@ class SqlTest extends UnitTestCase {
$this->assertEquals(10, $query->getCacheMaxAge());
}
/**
* Sets up the views data in the container.
*
* @param \Drupal\views\ViewsData $views_data
* The views data.
*/
protected function setupViewsData(ViewsData $views_data) {
$container = \Drupal::hasContainer() ? \Drupal::getContainer() : new ContainerBuilder();
$container->set('views.views_data', $views_data);
\Drupal::setContainer($container);
}
/**
* Sets up the entity type manager in the container.
*
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
*/
protected function setupEntityTypeManager(EntityTypeManagerInterface $entity_type_manager) {
$container = \Drupal::hasContainer() ? \Drupal::getContainer() : new ContainerBuilder();
$container->set('entity_type.manager', $entity_type_manager);
$container->set('entity.manager', $entity_type_manager);
\Drupal::setContainer($container);
}
/**
* Sets up some test entity types and corresponding views data.
*
* @param \Drupal\Core\Entity\EntityInterface[][] $entities_by_type
* Test entities keyed by entity type and entity ID.
* @param \Drupal\Core\Entity\EntityInterface[][] $entities_by_type
* Test entities keyed by entity type and revision ID.
*
* @return \Prophecy\Prophecy\ObjectProphecy
*/
protected function setupEntityTypes($entities_by_type = [], $entity_revisions_by_type = []) {
$entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
$entity_type0 = new EntityType([
'label' => 'First',
'id' => 'first',
'base_table' => 'entity_first',
'revision_table' => 'entity_first__revision',
'entity_keys' => [
'id' => 'id',
'revision' => 'vid',
],
]);
$entity_type1 = new EntityType([
'label' => 'second',
'id' => 'second',
'base_table' => 'entity_second',
'revision_table' => 'entity_second__revision',
'entity_keys' => [
'id' => 'id',
'revision' => 'vid',
],
]);
$entity_type_manager->getDefinitions()->willReturn([
'first' => $entity_type0,
'second' => $entity_type1,
'base_table' => 'entity_second',
]);
$entity_type_manager->getDefinition('first')->willReturn($entity_type0);
$entity_type_manager->getDefinition('second')->willReturn($entity_type1);
// Setup the views data corresponding to the entity types.
$views_data = $this->prophesize(ViewsData::class);
$views_data->get('entity_first')->willReturn([
'table' => [
'entity type' => 'first',
'entity revision' => FALSE,
],
]);
$views_data->get('entity_first__revision')->willReturn([
'table' => [
'entity type' => 'first',
'entity revision' => TRUE,
],
]);
$views_data->get('entity_second')->willReturn([
'table' => [
'entity type' => 'second',
'entity revision' => FALSE,
],
]);
$views_data->get('entity_second__revision')->willReturn([
'table' => [
'entity type' => 'second',
'entity revision' => TRUE,
],
]);
$this->setupViewsData($views_data->reveal());
// Setup the loading of entities and entity revisions.
$entity_storages = [
'first' => $this->prophesize(EntityStorageInterface::class),
'second' => $this->prophesize(EntityStorageInterface::class),
];
foreach ($entities_by_type as $entity_type_id => $entities) {
foreach ($entities as $entity_id => $entity) {
$entity_storages[$entity_type_id]->load($entity_id)->willReturn($entity);
}
$entity_storages[$entity_type_id]->loadMultiple(array_keys($entities))->willReturn($entities);
}
foreach ($entity_revisions_by_type as $entity_type_id => $entity_revisions) {
foreach ($entity_revisions as $revision_id => $revision) {
$entity_storages[$entity_type_id]->loadRevision($revision_id)->willReturn($revision);
}
}
$entity_type_manager->getStorage('first')->willReturn($entity_storages['first']);
$entity_type_manager->getStorage('second')->willReturn($entity_storages['second']);
$this->setupEntityTypeManager($entity_type_manager->reveal());
return $entity_type_manager;
}
/**
* @covers ::loadEntities
* @covers ::_assignEntitiesToResult
*/
public function testLoadEntitiesWithEmptyResult() {
$view = $this->prophesize('Drupal\views\ViewExecutable')->reveal();
$view_entity = $this->prophesize(ViewEntityInterface::class);
$view_entity->get('base_table')->willReturn('entity_first');
$view_entity->get('base_field')->willReturn('id');
$view->storage = $view_entity->reveal();
$entity_type_manager = $this->setupEntityTypes();
$query = new Sql([], 'sql', [], $entity_type_manager->reveal());
$query->view = $view;
$result = [];
$query->addField('entity_first', 'id', 'id');
$query->loadEntities($result);
$this->assertEmpty($result);
}
/**
* @covers ::loadEntities
* @covers ::_assignEntitiesToResult
*/
public function testLoadEntitiesWithNoRelationshipAndNoRevision() {
$view = $this->prophesize('Drupal\views\ViewExecutable')->reveal();
$view_entity = $this->prophesize(ViewEntityInterface::class);
$view_entity->get('base_table')->willReturn('entity_first');
$view_entity->get('base_field')->willReturn('id');
$view->storage = $view_entity->reveal();
$entities = [
'first' => [
1 => $this->prophesize(EntityInterface::class)->reveal(),
2 => $this->prophesize(EntityInterface::class)->reveal(),
],
];
$entity_type_manager = $this->setupEntityTypes($entities);
$query = new Sql([], 'sql', [], $entity_type_manager->reveal());
$query->view = $view;
$result = [];
$result[] = new ResultRow([
'id' => 1,
]);
// Note: Let the same entity be returned multiple times, for example to
// support the translation usecase.
$result[] = new ResultRow([
'id' => 2,
]);
$result[] = new ResultRow([
'id' => 2,
]);
$query->addField('entity_first', 'id', 'id');
$query->loadEntities($result);
$this->assertSame($entities['first'][1], $result[0]->_entity);
$this->assertSame($entities['first'][2], $result[1]->_entity);
$this->assertSame($entities['first'][2], $result[2]->_entity);
}
/**
* Create a view with a relationship.
*/
protected function setupViewWithRelationships(ViewExecutable $view, $base = 'entity_second') {
// We don't use prophecy, because prophecy enforces methods.
$relationship = $this->getMockBuilder(RelationshipPluginBase::class)->disableOriginalConstructor()->getMock();
$relationship->definition['base'] = $base;
$relationship->tableAlias = $base;
$relationship->alias = $base;
$view->relationship[$base] = $relationship;
}
/**
* @covers ::loadEntities
* @covers ::_assignEntitiesToResult
*/
public function testLoadEntitiesWithRelationship() {
// We don't use prophecy, because prophecy enforces methods.
$view = $this->getMockBuilder(ViewExecutable::class)->disableOriginalConstructor()->getMock();
$this->setupViewWithRelationships($view);
$view_entity = $this->prophesize(ViewEntityInterface::class);
$view_entity->get('base_table')->willReturn('entity_first');
$view_entity->get('base_field')->willReturn('id');
$view->storage = $view_entity->reveal();
$entities = [
'first' => [
1 => $this->prophesize(EntityInterface::class)->reveal(),
2 => $this->prophesize(EntityInterface::class)->reveal(),
],
'second' => [
11 => $this->prophesize(EntityInterface::class)->reveal(),
12 => $this->prophesize(EntityInterface::class)->reveal(),
],
];
$entity_type_manager = $this->setupEntityTypes($entities);
$query = new Sql([], 'sql', [], $entity_type_manager->reveal());
$query->view = $view;
$result = [];
$result[] = new ResultRow([
'id' => 1,
'entity_second__id' => 11,
]);
// Provide an explicit NULL value, to test the case of a non required
// relationship.
$result[] = new ResultRow([
'id' => 2,
'entity_second__id' => NULL,
]);
$result[] = new ResultRow([
'id' => 2,
'entity_second__id' => 12,
]);
$query->addField('entity_first', 'id', 'id');
$query->addField('entity_second', 'id', 'entity_second__id');
$query->loadEntities($result);
$this->assertSame($entities['first'][1], $result[0]->_entity);
$this->assertSame($entities['first'][2], $result[1]->_entity);
$this->assertSame($entities['first'][2], $result[2]->_entity);
$this->assertSame($entities['second'][11], $result[0]->_relationship_entities['entity_second']);
$this->assertEquals([], $result[1]->_relationship_entities);
$this->assertSame($entities['second'][12], $result[2]->_relationship_entities['entity_second']);
}
/**
* @covers ::loadEntities
* @covers ::_assignEntitiesToResult
*/
public function testLoadEntitiesWithRevision() {
// We don't use prophecy, because prophecy enforces methods.
$view = $this->getMockBuilder(ViewExecutable::class)
->disableOriginalConstructor()
->getMock();
$view_entity = $this->prophesize(ViewEntityInterface::class);
$view_entity->get('base_table')->willReturn('entity_first__revision');
$view_entity->get('base_field')->willReturn('vid');
$view->storage = $view_entity->reveal();
$entity_revisions = [
'first' => [
1 => $this->prophesize(EntityInterface::class)->reveal(),
3 => $this->prophesize(EntityInterface::class)->reveal(),
],
];
$entity_type_manager = $this->setupEntityTypes([], $entity_revisions);
$query = new Sql([], 'sql', [], $entity_type_manager->reveal());
$query->view = $view;
$result = [];
$result[] = new ResultRow([
'vid' => 1,
]);
$result[] = new ResultRow([
'vid' => 1,
]);
$result[] = new ResultRow([
'vid' => 3,
]);
$query->addField('entity_first__revision', 'vid', 'vid');
$query->loadEntities($result);
$this->assertSame($entity_revisions['first'][1], $result[0]->_entity);
$this->assertSame($entity_revisions['first'][1], $result[1]->_entity);
$this->assertSame($entity_revisions['first'][3], $result[2]->_entity);
}
/**
* @covers ::loadEntities
* @covers ::_assignEntitiesToResult
*/
public function testLoadEntitiesWithRevisionOfSameEntityType() {
// We don't use prophecy, because prophecy enforces methods.
$view = $this->getMockBuilder(ViewExecutable::class)
->disableOriginalConstructor()
->getMock();
$this->setupViewWithRelationships($view, 'entity_first__revision');
$view_entity = $this->prophesize(ViewEntityInterface::class);
$view_entity->get('base_table')->willReturn('entity_first');
$view_entity->get('base_field')->willReturn('id');
$view->storage = $view_entity->reveal();
$entity = [
'first' => [
1 => $this->prophesize(EntityInterface::class)->reveal(),
2 => $this->prophesize(EntityInterface::class)->reveal(),
],
];
$entity_revisions = [
'first' => [
1 => $this->prophesize(EntityInterface::class)->reveal(),
2 => $this->prophesize(EntityInterface::class)->reveal(),
3 => $this->prophesize(EntityInterface::class)->reveal(),
],
];
$entity_type_manager = $this->setupEntityTypes($entity, $entity_revisions);
$query = new Sql([], 'sql', [], $entity_type_manager->reveal());
$query->view = $view;
$result = [];
$result[] = new ResultRow([
'id' => 1,
'entity_first__revision__vid' => 1,
]);
$result[] = new ResultRow([
'id' => 2,
'entity_first__revision__vid' => 2,
]);
$result[] = new ResultRow([
'id' => 2,
'entity_first__revision__vid' => 3,
]);
$query->addField('entity_first', 'id', 'id');
$query->addField('entity_first__revision', 'vid', 'entity_first__revision__vid');
$query->loadEntities($result);
$this->assertSame($entity['first'][1], $result[0]->_entity);
$this->assertSame($entity['first'][2], $result[1]->_entity);
$this->assertSame($entity['first'][2], $result[2]->_entity);
$this->assertSame($entity_revisions['first'][1], $result[0]->_relationship_entities['entity_first__revision']);
$this->assertSame($entity_revisions['first'][2], $result[1]->_relationship_entities['entity_first__revision']);
$this->assertSame($entity_revisions['first'][3], $result[2]->_relationship_entities['entity_first__revision']);
}
/**
* @covers ::loadEntities
* @covers ::_assignEntitiesToResult
*/
public function testLoadEntitiesWithRelationshipAndRevision() {
// We don't use prophecy, because prophecy enforces methods.
$view = $this->getMockBuilder(ViewExecutable::class)->disableOriginalConstructor()->getMock();
$this->setupViewWithRelationships($view);
$view_entity = $this->prophesize(ViewEntityInterface::class);
$view_entity->get('base_table')->willReturn('entity_first__revision');
$view_entity->get('base_field')->willReturn('vid');
$view->storage = $view_entity->reveal();
$entities = [
'second' => [
11 => $this->prophesize(EntityInterface::class)->reveal(),
12 => $this->prophesize(EntityInterface::class)->reveal(),
],
];
$entity_revisions = [
'first' => [
1 => $this->prophesize(EntityInterface::class)->reveal(),
3 => $this->prophesize(EntityInterface::class)->reveal(),
],
];
$entity_type_manager = $this->setupEntityTypes($entities, $entity_revisions);
$query = new Sql([], 'sql', [], $entity_type_manager->reveal());
$query->view = $view;
$result = [];
$result[] = new ResultRow([
'vid' => 1,
'entity_second__id' => 11,
]);
// Provide an explicit NULL value, to test the case of a non required
// relationship.
$result[] = new ResultRow([
'vid' => 1,
'entity_second__id' => NULL,
]);
$result[] = new ResultRow([
'vid' => 3,
'entity_second__id' => 12,
]);
$query->addField('entity_first__revision', 'vid', 'vid');
$query->addField('entity_second', 'id', 'entity_second__id');
$query->loadEntities($result);
$this->assertSame($entity_revisions['first'][1], $result[0]->_entity);
$this->assertSame($entity_revisions['first'][1], $result[1]->_entity);
$this->assertSame($entity_revisions['first'][3], $result[2]->_entity);
$this->assertSame($entities['second'][11], $result[0]->_relationship_entities['entity_second']);
$this->assertEquals([], $result[1]->_relationship_entities);
$this->assertSame($entities['second'][12], $result[2]->_relationship_entities['entity_second']);
}
}

View file

@ -360,6 +360,35 @@ function views_update_8005() {
// Empty update function to rebuild the views data.
}
/**
* Clear caches due to updated entity views data.
*/
function views_update_8100() {
// Empty update to cause a cache flush so that views data is rebuilt.
}
/**
* Set default values for enabled/expanded flag on page displays.
*/
function views_update_8101() {
$config_factory = \Drupal::configFactory();
foreach ($config_factory->listAll('views.view.') as $view_config_name) {
$view = $config_factory->getEditable($view_config_name);
$save = FALSE;
foreach ($view->get('display') as $display_id => $display) {
if ($display['display_plugin'] == 'page') {
$display['display_options']['menu']['enabled'] = TRUE;
$display['display_options']['menu']['expanded'] = FALSE;
$view->set("display.$display_id", $display);
$save = TRUE;
}
}
if ($save) {
$view->save();
}
}
}
/**
* @} End of "addtogroup updates-8.1.0".
*/