Issue #2649914 by claudiu.cristea, lokapujya, dawehner: Enforce formatter dependencies on views fields
parent
d0ccb7457e
commit
bf28e3d006
22
core/modules/system/tests/fixtures/update/drupal8.views-image-style-dependency-2649914.php
vendored
Normal file
22
core/modules/system/tests/fixtures/update/drupal8.views-image-style-dependency-2649914.php
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains database additions to drupal-8-rc1.bare.standard.php.gz for testing
|
||||
* the upgrade path of https://www.drupal.org/node/2649914.
|
||||
*/
|
||||
|
||||
use Drupal\Component\Serialization\Yaml;
|
||||
use Drupal\Core\Database\Database;
|
||||
|
||||
$connection = Database::getConnection();
|
||||
|
||||
$views_config = Yaml::decode(file_get_contents(__DIR__ . '/drupal8.views-image-style-dependency-2649914.yml'));
|
||||
|
||||
$connection->insert('config')
|
||||
->fields(['collection', 'name', 'data'])
|
||||
->values([
|
||||
'collection' => '',
|
||||
'name' => 'views.view.' . $views_config['id'],
|
||||
'data' => serialize($views_config),
|
||||
])->execute();
|
43
core/modules/system/tests/fixtures/update/drupal8.views-image-style-dependency-2649914.yml
vendored
Normal file
43
core/modules/system/tests/fixtures/update/drupal8.views-image-style-dependency-2649914.yml
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.node.field_image
|
||||
module:
|
||||
- image
|
||||
- node
|
||||
id: foo
|
||||
label: Foo
|
||||
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:
|
||||
fields:
|
||||
field_image:
|
||||
id: field_image
|
||||
table: node__field_image
|
||||
field: field_image
|
||||
type: image
|
||||
settings:
|
||||
image_style: thumbnail
|
||||
image_link: ''
|
||||
plugin_id: field
|
||||
field_image_1:
|
||||
id: field_image_1
|
||||
table: node__field_image
|
||||
field: field_image
|
||||
type: image
|
||||
settings:
|
||||
# This field's formatter is using a non-existent image style.
|
||||
image_style: nonexistent
|
||||
image_link: ''
|
||||
plugin_id: field
|
|
@ -951,7 +951,7 @@ abstract class DisplayPluginBase extends PluginBase implements DisplayPluginInte
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
$this->addDependencies(parent::calculateDependencies());
|
||||
$this->dependencies = parent::calculateDependencies();
|
||||
// Collect all the dependencies of handlers and plugins. Only calculate
|
||||
// their dependencies if they are configured by this display.
|
||||
$plugins = array_merge($this->getAllHandlers(TRUE), $this->getAllPlugins(TRUE));
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\views\Plugin\views\field;
|
||||
|
||||
use Drupal\Component\Plugin\DependentPluginInterface;
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\Core\Cache\Cache;
|
||||
use Drupal\Core\Cache\CacheableDependencyInterface;
|
||||
|
@ -18,6 +19,7 @@ use Drupal\Core\Field\FormatterPluginManager;
|
|||
use Drupal\Core\Form\FormHelper;
|
||||
use Drupal\Core\Form\FormStateInterface;
|
||||
use Drupal\Core\Language\LanguageManagerInterface;
|
||||
use Drupal\Core\Plugin\PluginDependencyTrait;
|
||||
use Drupal\Core\Render\BubbleableMetadata;
|
||||
use Drupal\Core\Render\Element;
|
||||
use Drupal\Core\Render\RendererInterface;
|
||||
|
@ -40,7 +42,9 @@ use Symfony\Component\DependencyInjection\ContainerInterface;
|
|||
* @ViewsField("field")
|
||||
*/
|
||||
class Field extends FieldPluginBase implements CacheableDependencyInterface, MultiItemsFieldHandlerInterface {
|
||||
|
||||
use FieldAPIHandlerTrait;
|
||||
use PluginDependencyTrait;
|
||||
|
||||
/**
|
||||
* An array to store field renderable arrays for use by renderItems().
|
||||
|
@ -471,25 +475,9 @@ class Field extends FieldPluginBase implements CacheableDependencyInterface, Mul
|
|||
$form['field_api_classes']['#description'] .= ' ' . $this->t('Checking this option will cause the group Display Type and Separator values to be ignored.');
|
||||
}
|
||||
|
||||
// Get the currently selected formatter.
|
||||
$format = $this->options['type'];
|
||||
|
||||
$settings = $this->options['settings'] + $this->formatterPluginManager->getDefaultSettings($format);
|
||||
|
||||
$options = array(
|
||||
'field_definition' => $field,
|
||||
'configuration' => array(
|
||||
'type' => $format,
|
||||
'settings' => $settings,
|
||||
'label' => '',
|
||||
'weight' => 0,
|
||||
),
|
||||
'view_mode' => '_custom',
|
||||
);
|
||||
|
||||
// Get the settings form.
|
||||
$settings_form = array('#value' => array());
|
||||
if ($formatter = $this->formatterPluginManager->getInstance($options)) {
|
||||
if ($formatter = $this->getFormatterInstance()) {
|
||||
$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');
|
||||
|
@ -948,22 +936,50 @@ class Field extends FieldPluginBase implements CacheableDependencyInterface, Mul
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field formatter instance.
|
||||
*
|
||||
* @return \Drupal\Core\Field\FormatterInterface|null
|
||||
* The field formatter instance.
|
||||
*/
|
||||
protected function getFormatterInstance() {
|
||||
$settings = $this->options['settings'] + $this->formatterPluginManager->getDefaultSettings($this->options['type']);
|
||||
|
||||
$options = [
|
||||
'field_definition' => $this->getFieldDefinition(),
|
||||
'configuration' => [
|
||||
'type' => $this->options['type'],
|
||||
'settings' => $settings,
|
||||
'label' => '',
|
||||
'weight' => 0,
|
||||
],
|
||||
'view_mode' => '_custom',
|
||||
];
|
||||
|
||||
return $this->formatterPluginManager->getInstance($options);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function calculateDependencies() {
|
||||
$dependencies = parent::calculateDependencies();
|
||||
$this->dependencies = parent::calculateDependencies();
|
||||
|
||||
// Add the module providing the configured field storage as a dependency.
|
||||
if (($field_storage_definition = $this->getFieldStorageDefinition()) && $field_storage_definition instanceof EntityInterface) {
|
||||
$dependencies['config'][] = $field_storage_definition->getConfigDependencyName();
|
||||
$this->dependencies['config'][] = $field_storage_definition->getConfigDependencyName();
|
||||
}
|
||||
// Add the module providing the formatter.
|
||||
if (!empty($this->options['type'])) {
|
||||
$dependencies['module'][] = $this->formatterPluginManager->getDefinition($this->options['type'])['provider'];
|
||||
// Add the module providing the formatter.
|
||||
$this->dependencies['module'][] = $this->formatterPluginManager->getDefinition($this->options['type'])['provider'];
|
||||
|
||||
// Add the formatter's dependencies.
|
||||
if (($formatter = $this->getFormatterInstance()) && $formatter instanceof DependentPluginInterface) {
|
||||
$this->calculatePluginDependencies($formatter);
|
||||
}
|
||||
}
|
||||
|
||||
return $dependencies;
|
||||
return $this->dependencies;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\views\Tests\Update\ImageStyleDependencyUpdateTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\views\Tests\Update;
|
||||
|
||||
use Drupal\system\Tests\Update\UpdatePathTestBase;
|
||||
use Drupal\views\Entity\View;
|
||||
|
||||
/**
|
||||
* Tests Views image style dependencies update.
|
||||
*
|
||||
* @group views
|
||||
*/
|
||||
class ImageStyleDependencyUpdateTest extends UpdatePathTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setDatabaseDumpFiles() {
|
||||
$this->databaseDumpFiles = [
|
||||
__DIR__ . '/../../../../system/tests/fixtures/update/drupal-8-rc1.bare.standard.php.gz',
|
||||
__DIR__ . '/../../../../system/tests/fixtures/update/drupal8.views-image-style-dependency-2649914.php',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the updating of views dependencies to image styles.
|
||||
*/
|
||||
public function testUpdateImageStyleDependencies() {
|
||||
$config_dependencies = View::load('foo')->getDependencies()['config'];
|
||||
|
||||
// Checks that 'thumbnail' image style is not a dependency of view 'foo'.
|
||||
$this->assertFalse(in_array('image.style.thumbnail', $config_dependencies));
|
||||
|
||||
// We test the case the the field formatter image style doesn't exist.
|
||||
// Checks that 'nonexistent' image style is not a dependency of view 'foo'.
|
||||
$this->assertFalse(in_array('image.style.nonexistent', $config_dependencies));
|
||||
|
||||
// Run updates.
|
||||
$this->runUpdates();
|
||||
|
||||
$config_dependencies = View::load('foo')->getDependencies()['config'];
|
||||
|
||||
// Checks that 'thumbnail' image style is a dependency of view 'foo'.
|
||||
$this->assertTrue(in_array('image.style.thumbnail', $config_dependencies));
|
||||
|
||||
// The 'nonexistent' style doesn't exist, thus is not a dependency. Checks
|
||||
// that 'nonexistent' image style is a not dependency of view 'foo'.
|
||||
$this->assertFalse(in_array('image.style.nonexistent', $config_dependencies));
|
||||
}
|
||||
|
||||
}
|
|
@ -18,7 +18,10 @@ use Drupal\simpletest\KernelTestBase;
|
|||
* requires the full web test environment provided by WebTestBase, extend
|
||||
* ViewTestBase instead.
|
||||
*
|
||||
* @see \Drupal\views\Tests\ViewTestBase
|
||||
* @deprecated in Drupal 8.0.x, will be removed in Drupal 8.2.x. Use
|
||||
* \Drupal\Tests\views\Kernel\ViewsKernelTestBase instead.
|
||||
*
|
||||
* @see \Drupal\Tests\views\Kernel\ViewsKernelTestBase
|
||||
*/
|
||||
abstract class ViewKernelTestBase extends KernelTestBase {
|
||||
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\views\Kernel\ViewsConfigDependenciesIntegrationTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\views\Kernel;
|
||||
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Drupal\image\Entity\ImageStyle;
|
||||
use Drupal\views\Entity\View;
|
||||
|
||||
/**
|
||||
* Tests integration of views with other modules.
|
||||
*
|
||||
* @group views
|
||||
*/
|
||||
class ViewsConfigDependenciesIntegrationTest extends ViewsKernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['field', 'file', 'image', 'entity_test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $testViews = ['entity_test_fields'];
|
||||
|
||||
/**
|
||||
* Tests integration with image module.
|
||||
*/
|
||||
public function testImage() {
|
||||
/** @var \Drupal\image\ImageStyleInterface $style */
|
||||
$style = ImageStyle::create(['name' => 'foo']);
|
||||
$style->save();
|
||||
|
||||
// Create a new image field 'bar' to be used in 'entity_test_fields' view.
|
||||
FieldStorageConfig::create([
|
||||
'entity_type' => 'entity_test',
|
||||
'field_name' => 'bar',
|
||||
'type' => 'image',
|
||||
])->save();
|
||||
FieldConfig::create([
|
||||
'entity_type' => 'entity_test',
|
||||
'bundle' => 'entity_test',
|
||||
'field_name' => 'bar',
|
||||
])->save();
|
||||
|
||||
/** @var \Drupal\views\ViewEntityInterface $view */
|
||||
$view = View::load('entity_test_fields');
|
||||
$display =& $view->getDisplay('default');
|
||||
|
||||
// Add the 'bar' image field to 'entity_test_fields' view.
|
||||
$display['display_options']['fields']['bar'] = [
|
||||
'id' => 'bar',
|
||||
'field' => 'bar',
|
||||
'plugin_id' => 'field',
|
||||
'table' => 'entity_test__bar',
|
||||
'entity_type' => 'entity_test',
|
||||
'entity_field' => 'bar',
|
||||
'type' => 'image',
|
||||
'settings' => ['image_style' => 'foo', 'image_link' => ''],
|
||||
];
|
||||
$view->save();
|
||||
|
||||
$dependencies = $view->getDependencies() + ['config' => []];
|
||||
|
||||
// Checks that style 'foo' is a dependency of view 'entity_test_fields'.
|
||||
$this->assertTrue(in_array('image.style.foo', $dependencies['config']));
|
||||
|
||||
// Delete the 'foo' image style.
|
||||
$style->delete();
|
||||
|
||||
// Checks that the view has been deleted too.
|
||||
$this->assertNull(View::load('entity_test_fields'));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,156 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\Tests\views\Kernel\ViewsKernelTestBase.
|
||||
*/
|
||||
|
||||
namespace Drupal\Tests\views\Kernel;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\Query\SelectInterface;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\views\Tests\ViewResultAssertionTrait;
|
||||
use Drupal\views\Tests\ViewTestData;
|
||||
|
||||
/**
|
||||
* Defines a base class for Views kernel testing.
|
||||
*/
|
||||
class ViewsKernelTestBase extends KernelTestBase {
|
||||
|
||||
use ViewResultAssertionTrait;
|
||||
|
||||
/**
|
||||
* Views to be enabled.
|
||||
*
|
||||
* Test classes should override this property and provide the list of testing
|
||||
* views.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $testViews = [];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = ['system', 'views', 'views_test_config', 'views_test_data', 'user'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*
|
||||
* @param bool $import_test_views
|
||||
* Should the views specified on the test class be imported. If you need
|
||||
* to setup some additional stuff, like fields, you need to call false and
|
||||
* then call createTestViews for your own.
|
||||
*/
|
||||
protected function setUp($import_test_views = TRUE) {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('system', ['router', 'sequences', 'key_value_expire']);
|
||||
$this->setUpFixtures();
|
||||
|
||||
if ($import_test_views) {
|
||||
ViewTestData::createTestViews(get_class($this), ['views_test_config']);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Sets up the configuration and schema of views and views_test_data modules.
|
||||
*
|
||||
* Because the schema of views_test_data.module is dependent on the test
|
||||
* using it, it cannot be enabled normally.
|
||||
*/
|
||||
protected function setUpFixtures() {
|
||||
// First install the system module. Many Views have Page displays have menu
|
||||
// links, and for those to work, the system menus must already be present.
|
||||
$this->installConfig(['system']);
|
||||
|
||||
/** @var \Drupal\Core\State\StateInterface $state */
|
||||
$state = $this->container->get('state');
|
||||
// Define the schema and views data variable before enabling the test module.
|
||||
$state->set('views_test_data_schema', $this->schemaDefinition());
|
||||
$state->set('views_test_data_views_data', $this->viewsData());
|
||||
|
||||
$this->installConfig(['views', 'views_test_config', 'views_test_data']);
|
||||
foreach ($this->schemaDefinition() as $table => $schema) {
|
||||
$this->installSchema('views_test_data', $table);
|
||||
}
|
||||
|
||||
$this->container->get('router.builder')->rebuild();
|
||||
|
||||
// Load the test dataset.
|
||||
$data_set = $this->dataSet();
|
||||
$query = Database::getConnection()->insert('views_test_data')
|
||||
->fields(array_keys($data_set[0]));
|
||||
foreach ($data_set as $record) {
|
||||
$query->values($record);
|
||||
}
|
||||
$query->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Orders a nested array containing a result set based on a given column.
|
||||
*
|
||||
* @param array $result_set
|
||||
* An array of rows from a result set, with each row as an associative
|
||||
* array keyed by column name.
|
||||
* @param string $column
|
||||
* The column name by which to sort the result set.
|
||||
* @param bool $reverse
|
||||
* (optional) Boolean indicating whether to sort the result set in reverse
|
||||
* order. Defaults to FALSE.
|
||||
*
|
||||
* @return array
|
||||
* The sorted result set.
|
||||
*/
|
||||
protected function orderResultSet($result_set, $column, $reverse = FALSE) {
|
||||
$order = $reverse ? -1 : 1;
|
||||
usort($result_set, function ($a, $b) use ($column, $order) {
|
||||
if ($a[$column] == $b[$column]) {
|
||||
return 0;
|
||||
}
|
||||
return $order * (($a[$column] < $b[$column]) ? -1 : 1);
|
||||
});
|
||||
return $result_set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a view with debugging.
|
||||
*
|
||||
* @param \Drupal\views\ViewExecutable $view
|
||||
* The view object.
|
||||
* @param array $args
|
||||
* (optional) An array of the view arguments to use for the view.
|
||||
*/
|
||||
protected function executeView($view, array $args = []) {
|
||||
$view->setDisplay();
|
||||
$view->preExecute($args);
|
||||
$view->execute();
|
||||
$verbose_message = '<pre>Executed view: ' . ((string) $view->build_info['query']). '</pre>';
|
||||
if ($view->build_info['query'] instanceof SelectInterface) {
|
||||
$verbose_message .= '<pre>Arguments: ' . print_r($view->build_info['query']->getArguments(), TRUE) . '</pre>';
|
||||
}
|
||||
$this->verbose($verbose_message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the schema definition.
|
||||
*/
|
||||
protected function schemaDefinition() {
|
||||
return ViewTestData::schemaDefinition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the views data definition.
|
||||
*/
|
||||
protected function viewsData() {
|
||||
return ViewTestData::viewsData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a very simple test dataset.
|
||||
*/
|
||||
protected function dataSet() {
|
||||
return ViewTestData::dataSet();
|
||||
}
|
||||
|
||||
}
|
|
@ -6,8 +6,28 @@
|
|||
*/
|
||||
|
||||
use Drupal\Core\StringTranslation\TranslatableMarkup;
|
||||
use Drupal\views\Entity\View;
|
||||
use Drupal\views\Views;
|
||||
|
||||
/**
|
||||
* @addtogroup updates-8.0.x
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Update dependencies to image style.
|
||||
*/
|
||||
function views_post_update_image_style_dependencies() {
|
||||
$views = View::loadMultiple();
|
||||
array_walk($views, function(View $view) {
|
||||
$view->save();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @} End of "addtogroup updates-8.0.x".
|
||||
*/
|
||||
|
||||
/**
|
||||
* @addtogroup updates-8.0.0-beta
|
||||
* @{
|
||||
|
|
Loading…
Reference in New Issue