summaryrefslogtreecommitdiffstatshomepage
path: root/core/modules/views
diff options
context:
space:
mode:
Diffstat (limited to 'core/modules/views')
-rw-r--r--core/modules/views/config/schema/views.schema.yml15
-rw-r--r--core/modules/views/src/FieldViewsDataProvider.php31
-rw-r--r--core/modules/views/src/Hook/ViewsHooks.php16
-rw-r--r--core/modules/views/src/Hook/ViewsViewsHooks.php3
-rw-r--r--core/modules/views/src/Plugin/views/argument/LanguageArgument.php12
-rw-r--r--core/modules/views/src/Plugin/views/display/Block.php11
-rw-r--r--core/modules/views/src/Plugin/views/field/Boolean.php5
-rw-r--r--core/modules/views/src/Plugin/views/field/Url.php2
-rw-r--r--core/modules/views/src/Plugin/views/filter/FilterPluginBase.php10
-rw-r--r--core/modules/views/src/Plugin/views/style/Table.php2
-rw-r--r--core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php5
-rw-r--r--core/modules/views/src/ViewExecutable.php2
-rw-r--r--core/modules/views/src/ViewsConfigUpdater.php10
-rw-r--r--core/modules/views/tests/fixtures/update/views-block-items-per-page.php48
-rw-r--r--core/modules/views/tests/modules/action_bulk_test/config/install/views.view.test_bulk_form.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_click_sort.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_click_sort_ajax.yml9
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_content_ajax.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_distinct_click_sorting.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_operations.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_header.yml2
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_filter_placeholder_text.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_form_multiple.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_glossary.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache_none.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_table.yml1
-rw-r--r--core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_render.yml2
-rw-r--r--core/modules/views/tests/src/Functional/Plugin/ContextualFiltersBlockContextTest.php2
-rw-r--r--core/modules/views/tests/src/Functional/Update/BlockItemsPerPageUpdateTest.php41
-rw-r--r--core/modules/views/tests/src/Functional/Wizard/ItemsPerPageTest.php121
-rw-r--r--core/modules/views/tests/src/Kernel/Plugin/ViewsBlockTest.php22
-rw-r--r--core/modules/views/tests/src/Unit/Plugin/views/display/BlockTest.php18
-rw-r--r--core/modules/views/views.api.php5
-rw-r--r--core/modules/views/views.post_update.php23
35 files changed, 365 insertions, 63 deletions
diff --git a/core/modules/views/config/schema/views.schema.yml b/core/modules/views/config/schema/views.schema.yml
index 8ddd73931f2c..12187c6b9465 100644
--- a/core/modules/views/config/schema/views.schema.yml
+++ b/core/modules/views/config/schema/views.schema.yml
@@ -140,16 +140,29 @@ views.view.*:
views_block:
type: block_settings
label: 'View block'
+ constraints:
+ FullyValidatable: ~
mapping:
views_label:
type: label
label: 'Title'
+ requiredKey: false
items_per_page:
- type: string
+ type: integer
label: 'Items per block'
+ constraints:
+ Range:
+ min: 1
+ # Will only be respected if the associated View is configured to allow this to be overridden.
+ # @see \Drupal\views\Plugin\views\display\Block::blockForm()
+ requiredKey: false
+ # NULL to use the default defined by the view.
+ nullable: true
block.settings.views_block:*:
type: views_block
+ constraints:
+ FullyValidatable: ~
block.settings.views_exposed_filter_block:*:
type: views_block
diff --git a/core/modules/views/src/FieldViewsDataProvider.php b/core/modules/views/src/FieldViewsDataProvider.php
index c0b9d50b2d9c..fad34ff460e2 100644
--- a/core/modules/views/src/FieldViewsDataProvider.php
+++ b/core/modules/views/src/FieldViewsDataProvider.php
@@ -139,8 +139,8 @@ class FieldViewsDataProvider {
if (!empty($translatable_configs) && empty($untranslatable_configs)) {
$translation_join_type = 'language';
}
- // If the field is translatable only on certain bundles, there will be a join
- // on langcode OR bundle name.
+ // If the field is translatable only on certain bundles, there will be a
+ // join on langcode OR bundle name.
elseif (!empty($translatable_configs) && !empty($untranslatable_configs)) {
foreach ($untranslatable_configs as $config) {
$untranslatable_config_bundles[] = $config->getTargetBundle();
@@ -268,8 +268,8 @@ class FieldViewsDataProvider {
'help' => $this->t('Appears in: @bundles.', ['@bundles' => implode(', ', $bundles_names)]),
];
- // Go through and create a list of aliases for all possible combinations of
- // entity type + name.
+ // Go through and create a list of aliases for all possible combinations
+ // of entity type + name.
$aliases = [];
$also_known = [];
foreach ($all_labels as $label_name => $true) {
@@ -296,15 +296,15 @@ class FieldViewsDataProvider {
}
if ($aliases) {
$data[$table_alias][$field_alias]['aliases'] = $aliases;
- // The $also_known variable contains markup that is HTML escaped and that
- // loses safeness when imploded. The help text is used in #description
- // and therefore XSS admin filtered by default. Escaped HTML is not
- // altered by XSS filtering, therefore it is safe to just concatenate the
- // strings. Afterwards we mark the entire string as safe, so it won't be
- // escaped, no matter where it is used.
+ // The $also_known variable contains markup that is HTML escaped and
+ // that loses safeness when imploded. The help text is used in
+ // #description and therefore XSS admin filtered by default. Escaped
+ // HTML is not altered by XSS filtering, therefore it is safe to just
+ // concatenate the strings. Afterwards we mark the entire string as
+ // safe, so it won't be escaped, no matter where it is used.
// Considering the dual use of this help data (both as metadata and as
- // help text), other patterns such as use of #markup would not be correct
- // here.
+ // help text), other patterns such as use of #markup would not be
+ // correct here.
$data[$table_alias][$field_alias]['help'] = Markup::create($data[$table_alias][$field_alias]['help'] . ' ' . $this->t('Also known as:') . ' ' . implode(', ', $also_known));
}
@@ -328,7 +328,8 @@ class FieldViewsDataProvider {
foreach ($field_columns as $column => $attributes) {
$allow_sort = TRUE;
- // Identify likely filters and arguments for each column based on field type.
+ // Identify likely filters and arguments for each column based on field
+ // type.
switch ($attributes['type']) {
case 'int':
case 'mediumint':
@@ -387,8 +388,8 @@ class FieldViewsDataProvider {
'help' => $this->t('Appears in: @bundles.', ['@bundles' => implode(', ', $bundles_names)]),
];
- // Go through and create a list of aliases for all possible combinations of
- // entity type + name.
+ // Go through and create a list of aliases for all possible combinations
+ // of entity type + name.
$aliases = [];
$also_known = [];
foreach ($all_labels as $label_name => $true) {
diff --git a/core/modules/views/src/Hook/ViewsHooks.php b/core/modules/views/src/Hook/ViewsHooks.php
index 6facc63de6de..b309887723c3 100644
--- a/core/modules/views/src/Hook/ViewsHooks.php
+++ b/core/modules/views/src/Hook/ViewsHooks.php
@@ -2,6 +2,7 @@
namespace Drupal\views\Hook;
+use Drupal\block\BlockInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\views\ViewsConfigUpdater;
use Drupal\views\ViewEntityInterface;
@@ -378,4 +379,19 @@ class ViewsHooks {
$config_updater->updateAll($view);
}
+ /**
+ * Implements hook_ENTITY_TYPE_presave() for blocks.
+ */
+ #[Hook('block_presave')]
+ public function blockPresave(BlockInterface $block): void {
+ if (str_starts_with($block->getPluginId(), 'views_block:')) {
+ $settings = $block->get('settings');
+ if (isset($settings['items_per_page']) && $settings['items_per_page'] === 'none') {
+ @trigger_error('Saving a views block with "none" items per page is deprecated in drupal:11.2.0 and removed in drupal:12.0.0. To use the items per page defined by the view, use NULL. See https://www.drupal.org/node/3522240', E_USER_DEPRECATED);
+ $settings['items_per_page'] = NULL;
+ $block->set('settings', $settings);
+ }
+ }
+ }
+
}
diff --git a/core/modules/views/src/Hook/ViewsViewsHooks.php b/core/modules/views/src/Hook/ViewsViewsHooks.php
index a54decce9e62..4f10f689646c 100644
--- a/core/modules/views/src/Hook/ViewsViewsHooks.php
+++ b/core/modules/views/src/Hook/ViewsViewsHooks.php
@@ -143,8 +143,9 @@ class ViewsViewsHooks {
}
}
// Registers an action bulk form per entity.
+ $all_actions = \Drupal::entityTypeManager()->getStorage('action')->loadMultiple();
foreach (\Drupal::entityTypeManager()->getDefinitions() as $entity_type => $entity_info) {
- $actions = array_filter(\Drupal::entityTypeManager()->getStorage('action')->loadMultiple(), function (ActionConfigEntityInterface $action) use ($entity_type) {
+ $actions = array_filter($all_actions, function (ActionConfigEntityInterface $action) use ($entity_type) {
return $action->getType() == $entity_type;
});
if (empty($actions)) {
diff --git a/core/modules/views/src/Plugin/views/argument/LanguageArgument.php b/core/modules/views/src/Plugin/views/argument/LanguageArgument.php
index 23980568f764..81a2181dd5a2 100644
--- a/core/modules/views/src/Plugin/views/argument/LanguageArgument.php
+++ b/core/modules/views/src/Plugin/views/argument/LanguageArgument.php
@@ -15,21 +15,19 @@ use Drupal\views\Attribute\ViewsArgument;
class LanguageArgument extends ArgumentPluginBase {
/**
- * Overrides \Drupal\views\Plugin\views\argument\ArgumentPluginBase::summaryName().
- *
- * Gets the user-friendly version of the language name.
+ * {@inheritdoc}
*/
public function summaryName($data) {
+ // Gets the user-friendly version of the language name.
return $this->language($data->{$this->name_alias});
}
/**
- * Overrides \Drupal\views\Plugin\views\argument\ArgumentPluginBase::title().
- *
- * Gets the user friendly version of the language name for display as a
- * title placeholder.
+ * {@inheritdoc}
*/
public function title() {
+ // Gets the user friendly version of the language name for display as a
+ // title placeholder.
return $this->language($this->argument);
}
diff --git a/core/modules/views/src/Plugin/views/display/Block.php b/core/modules/views/src/Plugin/views/display/Block.php
index 6532990b8d3e..5692ee1df013 100644
--- a/core/modules/views/src/Plugin/views/display/Block.php
+++ b/core/modules/views/src/Plugin/views/display/Block.php
@@ -120,7 +120,7 @@ class Block extends DisplayPluginBase {
* @see \Drupal\views\Plugin\Block\ViewsBlock::defaultConfiguration()
*/
public function blockSettings(array $settings) {
- $settings['items_per_page'] = 'none';
+ $settings['items_per_page'] = NULL;
return $settings;
}
@@ -315,7 +315,7 @@ class Block extends DisplayPluginBase {
40 => 40,
48 => 48,
],
- '#default_value' => $block_configuration['items_per_page'],
+ '#default_value' => $block_configuration['items_per_page'] ?? 'none',
];
break;
}
@@ -353,7 +353,7 @@ class Block extends DisplayPluginBase {
*/
public function blockSubmit(ViewsBlock $block, $form, FormStateInterface $form_state) {
if ($items_per_page = $form_state->getValue(['override', 'items_per_page'])) {
- $block->setConfigurationValue('items_per_page', $items_per_page);
+ $block->setConfigurationValue('items_per_page', $items_per_page === 'none' ? NULL : intval($items_per_page));
}
$form_state->unsetValue(['override', 'items_per_page']);
}
@@ -366,8 +366,9 @@ class Block extends DisplayPluginBase {
*/
public function preBlockBuild(ViewsBlock $block) {
$config = $block->getConfiguration();
- if ($config['items_per_page'] !== 'none') {
- $this->view->setItemsPerPage($config['items_per_page']);
+ if (is_numeric($config['items_per_page']) && $config['items_per_page'] > 0) {
+ // @todo Delete the intval() in https://www.drupal.org/project/drupal/issues/3521221
+ $this->view->setItemsPerPage(intval($config['items_per_page']));
}
}
diff --git a/core/modules/views/src/Plugin/views/field/Boolean.php b/core/modules/views/src/Plugin/views/field/Boolean.php
index f2eb8f639b87..0c91fdc59509 100644
--- a/core/modules/views/src/Plugin/views/field/Boolean.php
+++ b/core/modules/views/src/Plugin/views/field/Boolean.php
@@ -16,8 +16,9 @@ use Drupal\views\Plugin\views\display\DisplayPluginBase;
* Allows for display of true/false, yes/no, on/off, enabled/disabled.
*
* Definition terms:
- * - output formats: An array where the first entry is displayed on boolean true
- * and the second is displayed on boolean false. An example for sticky is:
+ * - output formats: An array where the first entry is displayed on boolean
+ * true and the second is displayed on boolean false. An example for sticky
+ * is:
* @code
* 'output formats' => [
* 'sticky' => [t('Sticky'), ''],
diff --git a/core/modules/views/src/Plugin/views/field/Url.php b/core/modules/views/src/Plugin/views/field/Url.php
index 18e40a61f0e9..7f5dd0a33653 100644
--- a/core/modules/views/src/Plugin/views/field/Url.php
+++ b/core/modules/views/src/Plugin/views/field/Url.php
@@ -9,7 +9,7 @@ use Drupal\views\Attribute\ViewsField;
use Drupal\views\ResultRow;
/**
- * Field handler to provide simple renderer that turns a URL into a clickable link.
+ * Field handler to provide a renderer that turns a URL into a clickable link.
*
* @ingroup views_field_handlers
*/
diff --git a/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php b/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
index e3b5b87ac2ef..0f526735d6e3 100644
--- a/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
+++ b/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
@@ -889,7 +889,7 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
* (optional) The form element to set any errors on.
*
* @return string
- * Returns an error message if validation fails, or NULL if validation passes.
+ * The error message if validation fails, or NULL if validation passes.
*/
protected function validateIdentifier($identifier, ?FormStateInterface $form_state = NULL, &$form_group = []) {
$error = '';
@@ -1226,9 +1226,11 @@ abstract class FilterPluginBase extends HandlerBase implements CacheableDependen
continue;
}
// Each rows contains three widgets:
- // a) The title, where users define how they identify a pair of operator | value
- // b) The operator
- // c) The value (or values) to use in the filter with the selected operator
+ // - The title, where users define how they identify a pair of
+ // operator | value.
+ // - The operator.
+ // - The value (or values) to use in the filter with the selected
+ // operator.
// In each row, we have to display the operator form and the value from
// $row acts as a fake form to render each widget in a row.
diff --git a/core/modules/views/src/Plugin/views/style/Table.php b/core/modules/views/src/Plugin/views/style/Table.php
index 48adbf427ede..561628ac6820 100644
--- a/core/modules/views/src/Plugin/views/style/Table.php
+++ b/core/modules/views/src/Plugin/views/style/Table.php
@@ -71,7 +71,7 @@ class Table extends StylePluginBase implements CacheableDependencyInterface {
$options = parent::defineOptions();
$options['columns'] = ['default' => []];
- $options['class'] = ['default' => []];
+ $options['class'] = ['default' => ''];
$options['default'] = ['default' => ''];
$options['info'] = ['default' => []];
$options['override'] = ['default' => TRUE];
diff --git a/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php b/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php
index ef8568203cd4..d53e93cecec1 100644
--- a/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php
+++ b/core/modules/views/src/Plugin/views/wizard/WizardPluginBase.php
@@ -208,7 +208,8 @@ abstract class WizardPluginBase extends PluginBase implements WizardInterface {
* Gets the availableSorts property.
*
* @return array
- * An array of available sorts, keyed by sort ID, containing sort information.
+ * An array whose keys are the available sort options and whose
+ * corresponding values are human readable labels.
*/
public function getAvailableSorts() {
return $this->availableSorts;
@@ -483,7 +484,7 @@ abstract class WizardPluginBase extends PluginBase implements WizardInterface {
}
/**
- * Gets the current value of a #select element, from within a form constructor function.
+ * Gets the current value of a #select element.
*
* This function is intended for use in highly dynamic forms (in particular
* the add view wizard) which are rebuilt in different ways depending on which
diff --git a/core/modules/views/src/ViewExecutable.php b/core/modules/views/src/ViewExecutable.php
index 55e3a8d91453..4867ba48f9a6 100644
--- a/core/modules/views/src/ViewExecutable.php
+++ b/core/modules/views/src/ViewExecutable.php
@@ -348,7 +348,7 @@ class ViewExecutable {
public $footer;
/**
- * Stores the area handlers for the empty text which are initialized on this view.
+ * The area handlers for the empty text which are initialized on this view.
*
* An array containing Drupal\views\Plugin\views\area\AreaPluginBase objects.
*
diff --git a/core/modules/views/src/ViewsConfigUpdater.php b/core/modules/views/src/ViewsConfigUpdater.php
index 3b2709cc60c2..9c5d38e10ac5 100644
--- a/core/modules/views/src/ViewsConfigUpdater.php
+++ b/core/modules/views/src/ViewsConfigUpdater.php
@@ -134,6 +134,9 @@ class ViewsConfigUpdater implements ContainerInjectionInterface {
if ($this->processRememberRolesUpdate($handler, $handler_type)) {
$changed = TRUE;
}
+ if ($this->processTableCssClassUpdate($view)) {
+ $changed = TRUE;
+ }
return $changed;
});
}
@@ -335,6 +338,7 @@ class ViewsConfigUpdater implements ContainerInjectionInterface {
if (
isset($display['display_options']['style']) &&
$display['display_options']['style']['type'] === 'table' &&
+ isset($display['display_options']['style']['options']) &&
!isset($display['display_options']['style']['options']['class'])
) {
$display['display_options']['style']['options']['class'] = '';
@@ -346,6 +350,12 @@ class ViewsConfigUpdater implements ContainerInjectionInterface {
$view->set('display', $displays);
}
+ $deprecations_triggered = &$this->triggeredDeprecations['table_css_class'][$view->id()];
+ if ($this->deprecationsEnabled && $changed && !$deprecations_triggered) {
+ $deprecations_triggered = TRUE;
+ @trigger_error(sprintf('The update to add a default table CSS class for view "%s" is deprecated in drupal:11.2.0 and is removed from drupal:12.0.0. Profile, module and theme provided configuration should be updated. See https://www.drupal.org/node/3499943', $view->id()), E_USER_DEPRECATED);
+ }
+
return $changed;
}
diff --git a/core/modules/views/tests/fixtures/update/views-block-items-per-page.php b/core/modules/views/tests/fixtures/update/views-block-items-per-page.php
new file mode 100644
index 000000000000..542992a24df2
--- /dev/null
+++ b/core/modules/views/tests/fixtures/update/views-block-items-per-page.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * @file
+ * Creates a Views block with an `items_per_page` setting of `none`.
+ */
+
+declare(strict_types=1);
+
+use Drupal\Core\Database\Database;
+use Drupal\Core\Serialization\Yaml;
+
+$block_data = Yaml::decode(<<<END
+uuid: ecdad54d-8165-4ed3-a678-8ad20b388282
+langcode: en
+status: true
+dependencies:
+ config:
+ - views.view.who_s_online
+ module:
+ - views
+ theme:
+ - olivero
+id: olivero_who_s_online
+theme: olivero
+region: header
+weight: 0
+provider: null
+plugin: 'views_block:who_s_online-who_s_online_block'
+settings:
+ id: 'views_block:who_s_online-who_s_online_block'
+ label: ''
+ label_display: visible
+ provider: views
+ views_label: ''
+ items_per_page: none
+visibility: { }
+END
+);
+
+Database::getConnection()
+ ->insert('config')
+ ->fields([
+ 'collection' => '',
+ 'name' => 'block.block.olivero_who_s_online',
+ 'data' => serialize($block_data),
+ ])
+ ->execute();
diff --git a/core/modules/views/tests/modules/action_bulk_test/config/install/views.view.test_bulk_form.yml b/core/modules/views/tests/modules/action_bulk_test/config/install/views.view.test_bulk_form.yml
index cba11476ae8b..573705f502da 100644
--- a/core/modules/views/tests/modules/action_bulk_test/config/install/views.view.test_bulk_form.yml
+++ b/core/modules/views/tests/modules/action_bulk_test/config/install/views.view.test_bulk_form.yml
@@ -126,6 +126,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
columns:
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_click_sort.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_click_sort.yml
index 3ab5ecb66894..077b4bb4ce16 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_click_sort.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_click_sort.yml
@@ -42,6 +42,7 @@ display:
style:
type: table
options:
+ class: ''
info:
id:
sortable: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_click_sort_ajax.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_click_sort_ajax.yml
index bf65e0eaf379..1157a4e60bca 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_click_sort_ajax.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_click_sort_ajax.yml
@@ -43,6 +43,15 @@ display:
style:
type: table
options:
+ grouping: { }
+ class: ''
+ row_class: ''
+ default_row_class: true
+ override: true
+ sticky: true
+ caption: ''
+ summary: ''
+ description: ''
info:
id:
sortable: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_content_ajax.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_content_ajax.yml
index db3fd0693bf7..2036ee77c5fb 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_content_ajax.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_content_ajax.yml
@@ -57,6 +57,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_distinct_click_sorting.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_distinct_click_sorting.yml
index 55813bc9c49a..fe6c14f744bf 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_distinct_click_sorting.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_distinct_click_sorting.yml
@@ -65,6 +65,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_operations.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_operations.yml
index a19cea01b9f3..5ed73c2a1178 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_operations.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_entity_operations.yml
@@ -133,6 +133,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_header.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_header.yml
index 091c4375635e..a8c84a7a6497 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_header.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_field_header.yml
@@ -20,6 +20,8 @@ display:
element_label_type: h2
style:
type: table
+ options:
+ class: ''
display_extenders: { }
display_plugin: default
display_title: Default
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_filter_placeholder_text.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_filter_placeholder_text.yml
index 1f33e75b61fb..1217b6c86eba 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_filter_placeholder_text.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_filter_placeholder_text.yml
@@ -52,6 +52,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_form_multiple.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_form_multiple.yml
index ddb305fb162e..dffcbe3c0b72 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_form_multiple.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_form_multiple.yml
@@ -40,6 +40,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_glossary.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_glossary.yml
index 21ac6945808e..aa3e387ee905 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_glossary.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_glossary.yml
@@ -286,6 +286,7 @@ display:
override: true
sticky: false
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
uses_fields: false
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml
index b52621ee9908..a4bda56b5437 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache.yml
@@ -68,6 +68,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache_none.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache_none.yml
index aea7b9040134..91382cd03900 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache_none.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_row_render_cache_none.yml
@@ -68,6 +68,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_table.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_table.yml
index 10d215d29b51..77cca5f518f3 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_table.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_table.yml
@@ -63,6 +63,7 @@ display:
type: table
options:
grouping: { }
+ class: ''
row_class: ''
default_row_class: true
override: true
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_render.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_render.yml
index cec43f7c7dc7..98fa2836be96 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_render.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view_render.yml
@@ -48,6 +48,8 @@ display:
plugin_id: standard
style:
type: table
+ options:
+ class: ''
display_plugin: default
display_title: Default
id: default
diff --git a/core/modules/views/tests/src/Functional/Plugin/ContextualFiltersBlockContextTest.php b/core/modules/views/tests/src/Functional/Plugin/ContextualFiltersBlockContextTest.php
index 955226497f95..0384c917083e 100644
--- a/core/modules/views/tests/src/Functional/Plugin/ContextualFiltersBlockContextTest.php
+++ b/core/modules/views/tests/src/Functional/Plugin/ContextualFiltersBlockContextTest.php
@@ -122,7 +122,7 @@ class ContextualFiltersBlockContextTest extends ViewTestBase {
'provider' => 'views',
'label_display' => 'visible',
'views_label' => '',
- 'items_per_page' => 'none',
+ 'items_per_page' => NULL,
'context_mapping' => ['nid' => '@node.node_route_context:node'],
];
$this->assertEquals($expected_settings, $block->getPlugin()->getConfiguration(), 'Block settings are correct.');
diff --git a/core/modules/views/tests/src/Functional/Update/BlockItemsPerPageUpdateTest.php b/core/modules/views/tests/src/Functional/Update/BlockItemsPerPageUpdateTest.php
new file mode 100644
index 000000000000..bd250cc2736e
--- /dev/null
+++ b/core/modules/views/tests/src/Functional/Update/BlockItemsPerPageUpdateTest.php
@@ -0,0 +1,41 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\views\Functional\Update;
+
+use Drupal\block\Entity\Block;
+use Drupal\FunctionalTests\Update\UpdatePathTestBase;
+
+/**
+ * @group Update
+ * @covers views_post_update_block_items_per_page
+ */
+final class BlockItemsPerPageUpdateTest extends UpdatePathTestBase {
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function setDatabaseDumpFiles(): void {
+ $this->databaseDumpFiles = [
+ __DIR__ . '/../../../../../system/tests/fixtures/update/drupal-10.3.0.filled.standard.php.gz',
+ __DIR__ . '/../../../fixtures/update/views-block-items-per-page.php',
+ ];
+ }
+
+ /**
+ * Tests changing an `items_per_page` setting of `none` to NULL.
+ */
+ public function testUpdateItemsPerPage(): void {
+ $settings = Block::load('olivero_who_s_online')?->get('settings');
+ $this->assertIsArray($settings);
+ $this->assertSame('none', $settings['items_per_page']);
+
+ $this->runUpdates();
+
+ $settings = Block::load('olivero_who_s_online')?->get('settings');
+ $this->assertIsArray($settings);
+ $this->assertNull($settings['items_per_page']);
+ }
+
+}
diff --git a/core/modules/views/tests/src/Functional/Wizard/ItemsPerPageTest.php b/core/modules/views/tests/src/Functional/Wizard/ItemsPerPageTest.php
index 5f9cd364dac8..d290a27cd191 100644
--- a/core/modules/views/tests/src/Functional/Wizard/ItemsPerPageTest.php
+++ b/core/modules/views/tests/src/Functional/Wizard/ItemsPerPageTest.php
@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace Drupal\Tests\views\Functional\Wizard;
+use Drupal\views\Entity\View;
+
/**
* Tests that the views wizard can specify the number of items per page.
*
@@ -19,6 +21,16 @@ class ItemsPerPageTest extends WizardTestBase {
/**
* {@inheritdoc}
*/
+ protected static $configSchemaCheckerExclusions = [
+ // To be able to test with the now invalid:
+ // - `items_per_page: 'none'`
+ // - `items_per_page: '5'`
+ 'block.block.views_block_items_per_page_test_with_historical_override',
+ ];
+
+ /**
+ * {@inheritdoc}
+ */
protected function setUp($import_test_views = TRUE, $modules = []): void {
parent::setUp($import_test_views, $modules);
@@ -27,6 +39,12 @@ class ItemsPerPageTest extends WizardTestBase {
/**
* Tests the number of items per page.
+ *
+ * This should be removed from the `legacy` group in
+ * https://drupal.org/i/3521221; see
+ * \Drupal\views\Hook\ViewsHooks::blockPresave().
+ *
+ * @group legacy
*/
public function testItemsPerPage(): void {
$this->drupalCreateContentType(['type' => 'article']);
@@ -83,7 +101,7 @@ class ItemsPerPageTest extends WizardTestBase {
$this->drupalGet($view['page[path]']);
$this->assertSession()->statusCodeEquals(200);
- // Make sure the page display shows the nodes we expect, and that they
+ // Make sure the page display shows the 4 nodes we expect, and that they
// appear in the expected order.
$this->assertSession()->addressEquals($view['page[path]']);
$this->assertSession()->pageTextContains($view['page[title]']);
@@ -109,21 +127,94 @@ class ItemsPerPageTest extends WizardTestBase {
// Place the block, visit a page that displays the block, and check that the
// nodes we expect appear in the correct order.
- $this->drupalPlaceBlock("views_block:{$view['id']}-block_1");
-
+ $block = $this->drupalPlaceBlock("views_block:{$view['id']}-block_1");
+
+ // Asserts that the 3 newest articles are listed, which is the configuration
+ // for the `block` display in the view. In other words: the `items_per_page`
+ // setting in the `View` config entity is respected.
+ $assert_3_newest_nodes = function () use ($node5, $node4, $node3, $node2, $node1, $page_node) {
+ $this->drupalGet('user');
+ $content = $this->getSession()->getPage()->getContent();
+ $this->assertSession()->pageTextContains($node5->label());
+ $this->assertSession()->pageTextContains($node4->label());
+ $this->assertSession()->pageTextContains($node3->label());
+ $this->assertSession()->pageTextNotContains($node2->label());
+ $this->assertSession()->pageTextNotContains($node1->label());
+ $this->assertSession()->pageTextNotContains($page_node->label());
+ $pos5 = strpos($content, $node5->label());
+ $pos4 = strpos($content, $node4->label());
+ $pos3 = strpos($content, $node3->label());
+ $this->assertGreaterThan($pos5, $pos4);
+ $this->assertGreaterThan($pos4, $pos3);
+ };
+ self::assertSame(4, View::load($view['id'])->toArray()['display']['default']['display_options']['pager']['options']['items_per_page']);
+ self::assertSame(3, View::load($view['id'])->toArray()['display']['block_1']['display_options']['pager']['options']['items_per_page']);
+ self::assertArrayNotHasKey('items_per_page', $block->get('settings'));
+ $assert_3_newest_nodes();
+ $block->delete();
+
+ // Because the `allow[items_per_page]` checkbox is checked, it is allowed to
+ // override the `items_per_page` setting for the Views's `block` display,
+ // and is actually respected. Valid values are `null` ("do not override")
+ // and a positive integer.
+ $block = $this->drupalPlaceBlock("views_block:{$view['id']}-block_1", [
+ 'items_per_page' => NULL,
+ ]);
+ self::assertSame(4, View::load($view['id'])->toArray()['display']['default']['display_options']['pager']['options']['items_per_page']);
+ self::assertSame(3, View::load($view['id'])->toArray()['display']['block_1']['display_options']['pager']['options']['items_per_page']);
+ self::assertNull($block->get('settings')['items_per_page']);
+ $assert_3_newest_nodes();
+ $block->delete();
+
+ $block = $this->drupalPlaceBlock("views_block:{$view['id']}-block_1", [
+ 'items_per_page' => 5,
+ ]);
+ self::assertSame(4, View::load($view['id'])->toArray()['display']['default']['display_options']['pager']['options']['items_per_page']);
+ self::assertSame(3, View::load($view['id'])->toArray()['display']['block_1']['display_options']['pager']['options']['items_per_page']);
+ self::assertSame(5, $block->get('settings')['items_per_page']);
$this->drupalGet('user');
- $content = $this->getSession()->getPage()->getContent();
- $this->assertSession()->pageTextContains($node5->label());
- $this->assertSession()->pageTextContains($node4->label());
- $this->assertSession()->pageTextContains($node3->label());
- $this->assertSession()->pageTextNotContains($node2->label());
- $this->assertSession()->pageTextNotContains($node1->label());
- $this->assertSession()->pageTextNotContains($page_node->label());
- $pos5 = strpos($content, $node5->label());
- $pos4 = strpos($content, $node4->label());
- $pos3 = strpos($content, $node3->label());
- $this->assertGreaterThan($pos5, $pos4);
- $this->assertGreaterThan($pos4, $pos3);
+ foreach ([$node5, $node4, $node3, $node2, $node1] as $node) {
+ $this->assertSession()->pageTextContains($node->label());
+ }
+ $block->delete();
+
+ // Finally: set `items_per_page: 'none'`, which is the predecessor of
+ // `items_per_page: null`. This must continue to work as before even if the
+ // configuration is no longer considered valid, because otherwise we risk
+ // breaking e.g. blocks placed using Layout Builder.
+ // @todo Delete in https://www.drupal.org/project/drupal/issues/3521221.
+ $block = $this->drupalPlaceBlock("views_block:{$view['id']}-block_1", [
+ 'id' => 'views_block_items_per_page_test_with_historical_override',
+ ]);
+ // Explicitly set the `items_per_page` setting to a string without casting.
+ // It should be changed to NULL by the pre-save hook.
+ // @see \Drupal\views\Hook\ViewsHooks::blockPresave()
+ $block->set('settings', [
+ 'items_per_page' => 'none',
+ ])->trustData()->save();
+ $this->expectDeprecation('Saving a views block with "none" items per page is deprecated in drupal:11.2.0 and removed in drupal:12.0.0. To use the items per page defined by the view, use NULL. See https://www.drupal.org/node/3522240');
+ self::assertNull($block->get('settings')['items_per_page']);
+ self::assertSame(4, View::load($view['id'])->toArray()['display']['default']['display_options']['pager']['options']['items_per_page']);
+ self::assertSame(3, View::load($view['id'])->toArray()['display']['block_1']['display_options']['pager']['options']['items_per_page']);
+ $assert_3_newest_nodes();
+ $block->delete();
+
+ // Truly finally: set `items_per_page: '5'`, because for the same reason as
+ // above, blocks placed using Layout Builder may still have stale settings.
+ $block = $this->drupalPlaceBlock("views_block:{$view['id']}-block_1", [
+ 'id' => 'views_block_items_per_page_test_with_historical_override',
+ ]);
+ // Explicitly set the `items_per_page` setting to a string without casting.
+ $block->set('settings', [
+ 'items_per_page' => '5',
+ ])->trustData()->save();
+ self::assertSame('5', $block->get('settings')['items_per_page']);
+ self::assertSame(4, View::load($view['id'])->toArray()['display']['default']['display_options']['pager']['options']['items_per_page']);
+ self::assertSame(3, View::load($view['id'])->toArray()['display']['block_1']['display_options']['pager']['options']['items_per_page']);
+ $this->drupalGet('user');
+ foreach ([$node5, $node4, $node3, $node2, $node1] as $node) {
+ $this->assertSession()->pageTextContains($node->label());
+ }
}
}
diff --git a/core/modules/views/tests/src/Kernel/Plugin/ViewsBlockTest.php b/core/modules/views/tests/src/Kernel/Plugin/ViewsBlockTest.php
index b336584c74d6..ebd9df73e01d 100644
--- a/core/modules/views/tests/src/Kernel/Plugin/ViewsBlockTest.php
+++ b/core/modules/views/tests/src/Kernel/Plugin/ViewsBlockTest.php
@@ -4,6 +4,8 @@ declare(strict_types=1);
namespace Drupal\Tests\views\Kernel\Plugin;
+use Drupal\Core\Extension\ThemeInstallerInterface;
+use Drupal\Tests\block\Traits\BlockCreationTrait;
use Drupal\views\Plugin\Block\ViewsBlock;
use Drupal\views\Tests\ViewTestData;
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
@@ -16,6 +18,8 @@ use Drupal\views\Views;
*/
class ViewsBlockTest extends ViewsKernelTestBase {
+ use BlockCreationTrait;
+
/**
* {@inheritdoc}
*/
@@ -142,4 +146,22 @@ class ViewsBlockTest extends ViewsKernelTestBase {
$this->assertEquals('"test_view_block::block_1" views block', $views_block->getPreviewFallbackString());
}
+ /**
+ * Tests that saving a Views block with items_per_page = `none` is deprecated.
+ *
+ * @covers \Drupal\views\Hook\ViewsHooks::blockPresave
+ *
+ * @group legacy
+ */
+ public function testSaveBlockWithDeprecatedItemsPerPageSetting(): void {
+ $this->container->get(ThemeInstallerInterface::class)->install(['stark']);
+
+ $this->expectDeprecation('Saving a views block with "none" items per page is deprecated in drupal:11.2.0 and removed in drupal:12.0.0. To use the items per page defined by the view, use NULL. See https://www.drupal.org/node/3522240');
+ $block = $this->placeBlock('views_block:test_view_block-block_1', [
+ 'items_per_page' => 'none',
+ ]);
+ $settings = $block->get('settings');
+ $this->assertNull($settings['items_per_page']);
+ }
+
}
diff --git a/core/modules/views/tests/src/Unit/Plugin/views/display/BlockTest.php b/core/modules/views/tests/src/Unit/Plugin/views/display/BlockTest.php
index 0d72bf27b4ac..6988a04d8b48 100644
--- a/core/modules/views/tests/src/Unit/Plugin/views/display/BlockTest.php
+++ b/core/modules/views/tests/src/Unit/Plugin/views/display/BlockTest.php
@@ -62,29 +62,37 @@ class BlockTest extends UnitTestCase {
/**
* Tests the build method with no overriding.
+ *
+ * @testWith [null]
+ * ["none"]
+ * [0]
+ * @todo Delete the last two cases in https://www.drupal.org/project/drupal/issues/3521221. The last one is `intval('none')`.
*/
- public function testBuildNoOverride(): void {
+ public function testBuildNoOverride($items_per_page_setting): void {
$this->executable->expects($this->never())
->method('setItemsPerPage');
$this->blockPlugin->expects($this->once())
->method('getConfiguration')
- ->willReturn(['items_per_page' => 'none']);
+ ->willReturn(['items_per_page' => $items_per_page_setting]);
$this->blockDisplay->preBlockBuild($this->blockPlugin);
}
/**
* Tests the build method with overriding items per page.
+ *
+ * @testWith [5, 5]
+ * ["5", 5]
*/
- public function testBuildOverride(): void {
+ public function testBuildOverride(mixed $input, int $expected): void {
$this->executable->expects($this->once())
->method('setItemsPerPage')
- ->with(5);
+ ->with($expected);
$this->blockPlugin->expects($this->once())
->method('getConfiguration')
- ->willReturn(['items_per_page' => 5]);
+ ->willReturn(['items_per_page' => $input]);
$this->blockDisplay->preBlockBuild($this->blockPlugin);
}
diff --git a/core/modules/views/views.api.php b/core/modules/views/views.api.php
index 4fbcad6730ea..6579f93199bb 100644
--- a/core/modules/views/views.api.php
+++ b/core/modules/views/views.api.php
@@ -532,8 +532,9 @@ function hook_views_data_alter(array &$data) {
* When collecting the views data, views_views_data() invokes this hook for each
* field storage definition, on the module that provides the field storage
* definition. If the return value is empty, the result of
- * FieldViewsDataProvider::defaultFieldImplementation() is used instead. Then the result is altered
- * by invoking hook_field_views_data_alter() on all modules.
+ * FieldViewsDataProvider::defaultFieldImplementation() is used instead. Then
+ * the result is altered by invoking hook_field_views_data_alter() on all
+ * modules.
*
* If no hook implementation exists, hook_views_data() falls back to
* FieldViewsDataProvider::defaultFieldImplementation().
diff --git a/core/modules/views/views.post_update.php b/core/modules/views/views.post_update.php
index c3f352e99a42..48623fc593e0 100644
--- a/core/modules/views/views.post_update.php
+++ b/core/modules/views/views.post_update.php
@@ -5,6 +5,7 @@
* Post update functions for Views.
*/
+use Drupal\block\BlockInterface;
use Drupal\Core\Config\Entity\ConfigEntityUpdater;
use Drupal\views\ViewEntityInterface;
use Drupal\views\ViewsConfigUpdater;
@@ -83,7 +84,29 @@ function views_post_update_update_remember_role_empty(?array &$sandbox = NULL):
function views_post_update_table_css_class(?array &$sandbox = NULL): void {
/** @var \Drupal\views\ViewsConfigUpdater $view_config_updater */
$view_config_updater = \Drupal::classResolver(ViewsConfigUpdater::class);
+ $view_config_updater->setDeprecationsEnabled(FALSE);
\Drupal::classResolver(ConfigEntityUpdater::class)->update($sandbox, 'view', function (ViewEntityInterface $view) use ($view_config_updater): bool {
return $view_config_updater->needsTableCssClassUpdate($view);
});
}
+
+/**
+ * Defaults `items_per_page` to NULL in Views blocks.
+ */
+function views_post_update_block_items_per_page(?array &$sandbox = NULL): void {
+ if (!\Drupal::moduleHandler()->moduleExists('block')) {
+ return;
+ }
+ \Drupal::classResolver(ConfigEntityUpdater::class)
+ ->update($sandbox, 'block', function (BlockInterface $block): bool {
+ if (str_starts_with($block->getPluginId(), 'views_block:')) {
+ $settings = $block->get('settings');
+ if ($settings['items_per_page'] === 'none') {
+ $settings['items_per_page'] = NULL;
+ $block->set('settings', $settings);
+ return TRUE;
+ }
+ }
+ return FALSE;
+ });
+}