Problem/Motivation

1. Display extenders are fundamentally incompatible with config (and therefore schema) assumptions. In config, the idea is every part of config has an owner that is somehow statically stored in the config as a dependency. The config entity shell itself is provided by views, but then all fields used will have a provider and therefore dependencies, etc. Display extenders are allowed to just alter the display with any additional keys in a non-identifiable way. It is not possible to tell if key X or Y is of the actual display_plugin or a display extender (by looking at the config). I think this needs a fundamental change, namely that display extenders would either be allowed to alter existing display settings or employ a ThirdPartySettingsTrait based solution where each 3rd party setting has clear ownership. I think that is a fundamental change and not sure this issue should also do that. We can exempt that test from strict schema checking for the scope here and open one more critical. Opinions?

Proposed resolution

Remaining tasks

User interface changes

API changes

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

dawehner’s picture

Status: Active » Needs review
FileSize
9.26 KB

Here is a patch for now.

Gábor Hojtsy’s picture

Title: Integrate display extenders into config schema » Display extenders are not possible to describe with config schema
Status: Needs review » Needs work
Issue tags: +Configuration schema
Parent issue: » #2183983: Find hidden configuration schema issues

First of all, thanks for this quick patch, amazing! Retitled as a bug (the prior title sounded like a task). Only found one bigger concern and a minor one.

  1. +++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
    @@ -220,6 +226,23 @@ public function initDisplay(ViewExecutable $view, array &$display, array &$optio
    +  public function setThirdPartySetting($module, $key, $value) {
    ...
    +    $display_extenders[$module][$key] = $value;
    
    @@ -706,6 +729,14 @@ protected function defineOptions() {
    +      $options['display_extenders']['contains'][$extender_id] = $extender->defineOptions();
    
    +++ b/core/modules/views/tests/modules/views_test_data/src/Plugin/views/display_extender/DisplayExtenderTest.php
    @@ -50,7 +52,7 @@ public function optionsSummary(&$categories, &$options) {
    +    $test_option = $this->displayHandler->getThirdPartySetting('display_extender_test', 'test_extender_test_option') ?: $this->t('Empty');
    
    @@ -70,7 +72,7 @@ public function buildOptionsForm(&$form, FormStateInterface $form_state) {
    +          '#default_value' => $this->displayHandler->getThirdPartySetting('display_extender_test', 'test_extender_test_option'),
    
    @@ -82,7 +84,7 @@ public function submitOptionsForm(&$form, FormStateInterface $form_state) {
    +        $this->displayHandler->setThirdPartySetting('display_extender_test', 'test_extender_test_option', $form_state->getValue('test_extender_test_option'));
    
    +++ b/core/modules/views_ui/src/Tests/DisplayExtenderUITest.php
    @@ -42,7 +42,9 @@ public function testDisplayExtenderUI() {
    +    $this->assertEqual($display_extender_options['display_extender_test']['test_extender_test_option'], $random_text, 'Make sure that the display extender option got saved.');
    

    Should this not use views_test_data as the module name which is where the extender is defined? Looks like currently its using a made-up module name.

    Currently in this proposed patch it looks like a mix of extender_id being actually used but promoted on the API as a module name.

  2. +++ b/core/modules/views_ui/src/Tests/DisplayExtenderUITest.php
    @@ -42,7 +42,9 @@ public function testDisplayExtenderUI() {
    +    debug($display_extender_options);
    

    This debug line was not intended to be left in I assume.

Gábor Hojtsy’s picture

Also I made the display extender test exempt from schema checking to avoid hitting this bug in #2385805-31: Views tests don't pass strict schema checking, this patch will need to undo that change to the display extender test.

Gábor Hojtsy’s picture

Issue tags: +Configuration system
Gábor Hojtsy’s picture

Issue tags: +D8 upgrade path
dawehner’s picture

Should this not use views_test_data as the module name which is where the extender is defined? Looks like currently its using a made-up module name.

Well, this is a place where the third party trait has a slightly different mind model. A display extender is not once per module, but potentially multiple times per module,
but ... well technically at least from my point of the view the architecture of third party, $module could be renamed to something else.

Gábor Hojtsy’s picture

@dawehner: So what ensures that a display extender named 'display_extender_test' is not defined on your site by both foo module and bar module? I guess the display extender plugin manager will figure out which one wins if both modules are enabled and you'll need to *hope* that config schema decided with the same winner so the right config schema is used (or you may end up with data loss on type casting for example due to the wrong schema). I think the third party interface uses module names instead of plugin ids, because in Drupal we at least have this illusion that module names are unique, and that is *actually* true for installed modules, you cannot have two modules installed with the same name. So if something is owned by module foo, we know exactly in the runtime which module foo it is. (For the illusion part, we used to have most modules on drupal.org and that ensured a consistent namespace and very few collisions).

So in short, I think it makes most sense that this interface uses module names and its storage should too. You can still use a level under there for any number of display extender configs provided by one module, no?

dawehner’s picture

Status: Needs work » Needs review
FileSize
9.25 KB
7.04 KB

@dawehner: So what ensures that a display extender named 'display_extender_test' is not defined on your site by both foo module and bar module? I guess the display extender plugin manager will figure out which one wins if both modules are enabled and you'll need to *hope* that config schema decided with the same winner so the right config schema is used (or you may end up with data loss on type casting for example due to the wrong schema). I think the third party interface uses module names instead of plugin ids, because in Drupal we at least have this illusion that module names are unique, and that is *actually* true for installed modules, you cannot have two modules installed with the same name. So if something is owned by module foo, we know exactly in the runtime which module foo it is. (For the illusion part, we used to have most modules on drupal.org and that ensured a consistent namespace and very few collisions).

Well, one the one hand its true that modules are ensured to be unique, and plugin IDs aren't.
On the other hand the assumption, that plugins are unique, is implicit done in MILLIONS of places.

Multiple modules can define the same plugin ID, and at least at the moment, nothing blocks them from doing so.
If you think this is problematic, this is an entire new meta critical.

One place of such an implicit assumption is schema API, all plugins are handled that way already, you define the schema by plugin ID already.

IMHO, one reason, why I think using the module name here is a bad idea, is that its not an expected API as a developer.
Nothing else in the plugins assumes the extensions of modules are used in the storage mechanism.

Here is an alternative approach to the problem, which lets display extenders just use the typical views plugins API and don't care about the rest.
Yes, it still assumes that plugin IDs are unique, but in case you really want to fix that, you would rather validate that on a more generic level.

Note: We drop the usage of the third party settings here, because its just not a good fit here, even the code gets simpler by not using it.

effulgentsia’s picture

This looks great to me. I agree with plugins being a better fit here than ThirdPartySettings, and that plugin IDs should be treatable as unique and that the potential for collision between modules is a separate problem. I'm tempted to RTBC this, except:

+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
@@ -2642,6 +2662,15 @@ protected function mergeHandler($type) {
+  public function getExtender() {

Let's pluralize this (getExtenders()).

+++ b/core/modules/views_ui/src/Tests/DisplayExtenderUITest.php
@@ -53,7 +53,8 @@ public function testDisplayExtenderUI() {
-    $this->assertEqual($view->display_handler->getOption('test_extender_test_option'), $random_text, 'Make sure that the display extender option got saved.');
+    $display_extender_options = $view->display_handler->getOption('display_extenders');
+    $this->assertEqual($display_extender_options['display_extender_test']['test_extender_test_option'], $random_text, 'Make sure that the display extender option got saved.');

I think this is a good change, and is essentially the entire point of this issue, but I don't know what the real world use cases of display extenders are, and if there are any potential problems with code needing to be specific about which extender it needs to get the option value from. So I'll leave the RTBC to someone who's more confident that contrib use cases will be ok with this change.

tim.plunkett’s picture

+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
@@ -701,6 +709,13 @@ protected function defineOptions() {
+    $options['display_extenders'] = ['default' => [], ];

Nitpick: the comma is not needed.

As far as the second point of #9, I've never known a need to access a display extender from the outside. So that shouldn't be a real problem.

Gábor Hojtsy’s picture

Status: Needs review » Needs work

Also needs work on #3. Should not be major.

Gábor Hojtsy’s picture

Status: Needs work » Needs review
FileSize
11.13 KB
5.91 KB

Done those changes. I made all extender items to extenders because this aligns with the rest of the properties (plugins, handlers, etc. where there is more than one). So not just in the getter.

dawehner’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: +Ghent DA sprint

The changes in #12 looks perfect, given that I'll RTBC the changes from gabor.

I think this is a good change, and is essentially the entire point of this issue, but I don't know what the real world use cases of display extenders are, and if there are any potential problems with code needing to be specific about which extender it needs to get the option value from. So I'll leave the RTBC to someone who's more confident that contrib use cases will be ok with this change.

Yeah, a typical example is something like metatags, which wants to add behaviour to all possible view displays.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 12: 2387149-12.patch, failed testing.

Gábor Hojtsy’s picture

Status: Needs work » Reviewed & tested by the community
FileSize
11.75 KB
951 bytes

Duh missed two places somehow. Should still be RTBC.

  • Dries committed dfb971e on 8.0.x
    Issue #2387149 by Gábor Hojtsy, dawehner: Display extenders are not...
Dries’s picture

Priority: Critical » Major
Status: Reviewed & tested by the community » Fixed

Committed to 8.x.

I don't think this was a 'critical' as Display Extenders seem like an edge case feature. Making this a 'major'. We should make sure we don't automatically make the child issues of #2183983: Find hidden configuration schema issues critical by inheritance.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.