Problem/Motivation
While working on a D8 port of a contrib using the request_path condition plugin, I observed the following behavior. I then repeated this bug using the block config form.
Steps to reproduce
- Configure a block's page visibility settings.
- Enter a path, such as
<front>
. - Select the negate option "Show for the listed pages".
- Save the form.
- Click to configure the same block and view the pages visibility settings.
- Observe that the radio option, "Show for the listed pages" is not selected.
Expected behavior
I'd expect that the selected radio option will appear after saving the config form.
Why and how
\Drupal\block\BlockForm.php uses the request_path condition plugin and subsequently the ConditionPluginBase::buildConfigurationForm to build it's negate form element, which is a checkbox.
BlockForm.php changes this checkbox to a radio form element with an options index array with two elements, false/0 and true/1.
However, during form submission, the negate value is saved as FALSE. During form rendering, the logic, $element['#value'] !== FALSE
, in public static function Radio::preRenderRadio, prevents the false radio input, "Show for the listed pages", from being checked. Note, this logic was added https://www.drupal.org/node/971120#comment-3706168.
Tangential Issues
However, this scenario is straightforward. I can see user confusion if the pages form element is left empty ( no paths are added ) and the radio option "Show for the listed pages" is selected. While this radio option translates to FALSE negate condition as opposed to interpreting that this block will "Show for the listed pages", which there are none, so I deduce that this block will NOT show any pages.
Proposed resolution
- Consider whether or not the introduction of this logic,
$element['#value'] !== FALSE
in the radio renderer is source of bug. - If it is, remove it
- If it's not, use ternary to switch the negate's default value form element from boolean to integer during buildVisibilityInterface method so that the radio renderer will check the appropriate radio input.
- Add #states to the negate form element to show/hide when pages form element is not empty. *Likely needs a separate issue created. See #2451461: Hide/show block page visibility negate form element if pages form element is/is not empty
Remaining tasks
- Build consensus regarding source of bug.
- Identify source of bug.
- Identify solution to bug.
User interface changes
- Check the "Show for the listed pages" radio input if False.
- Possibly show/hide block's page visibility negate option as condition if paths are provided or not. See #2451461: Hide/show block page visibility negate form element if pages form element is/is not empty
API changes
Not sure
Comment | File | Size | Author |
---|---|---|---|
#14 | interdiff.txt | 999 bytes | tim.plunkett |
#14 | block_page_visibility-2450637-14.patch | 2.62 KB | tim.plunkett |
#11 | block_page_visibility-2450637-11-FAIL.patch | 1.1 KB | tim.plunkett |
#11 | block_page_visibility-2450637-11-PASS.patch | 1.84 KB | tim.plunkett |
#9 | block_page_visibility-2450637-9.patch | 758 bytes | tim.plunkett |
Comments
Comment #1
mrjmd CreditAttribution: mrjmd commentedI can confirm this behavior is happening in the latest 8.0.x.
A quick test does show that the change noted in the issue summary does appear to solve the problem, but I'm not familiar enough with D8 FAPI so say whether it would have other side-effects.
Here's what I did to test the solution, after following the steps to reproduce above:
Comment #2
jasonawantHello,
Here's a patch that removes the offending logic from the preRenderRadio method. Feedback welcomed. Thanks, Jason.
Comment #4
jasonawantHello,
I've created a feature request for the hide/show functionality mentioned in the issue summary. See it here > https://www.drupal.org/node/2451461
Comment #5
jasonawantHello,
It looks like I'll need to learn how to execute tests locally to figure this out.
I suppose if this is the right approach, we'll need to update/modify the tests appropriately, is that right?
1) BlockTest fails because of
$this->assertIdentical([], $block->get('visibility'));
. Do we update the the first parameter with something resembling the mixed property return by $block->get('visibility'), something akin toarray ( 'request_path' => array ( 'id' => 'request_path', 'pages' => '', 'negate' => false, 'context_mapping' => array ( ), ), )
.2) Not sure what a path forward would be here yet. I need to run these tests locally to know what's going on here.
Comment #6
Wim LeersFixing the STR HTML.
Comment #7
Wim LeersIt's even worse than the IS suggests: no radio button is selected initially:
(This is when configuring the "Search form" block after a fresh install.)
Seems like this problem goes back to the way condition plugins (see #2339151: Conditions / context system does not allow for multiple configurable contexts, eg. language types, where this was introduced IIRC). The problem doesn't lie in
\Drupal\Core\Render\Element\Radio
, but in how condition plugins map data from Block config entities to the form.Pinging @EclipseGc and @TimPlunkett to help.
Comment #8
Wim LeersAdding #2451461: Hide/show block page visibility negate form element if pages form element is/is not empty — created in #4 — to the related issues.
Comment #9
tim.plunkett\Drupal\Core\Condition\ConditionPluginBase::buildConfigurationForm uses ['#type' => 'checkbox'] for the negate portion of the form.
For UX reasons, \Drupal\block\BlockForm::buildVisibilityInterface changes the type to radios, and doesn't cast the value.
We used to cast the values, it was probably removed in that commit as well.
Comment #10
Wim Leers@tim.plunkett++
Comment #11
tim.plunkettYep, sure enough.
This was removed in #2339151: Conditions / context system does not allow for multiple configurable contexts, eg. language types:
and replaced with
Turns out that cast *was* important!
However, this was just a confusing UI problem, the values were saved. So this can probably be downgraded.
Comment #14
tim.plunkettYay for having to work around FAPI.
We need this because \Drupal\Core\Condition\ConditionPluginCollection::getConfiguration() will compare 0 and FALSE strictly.
Comment #15
jasonawantHi,
I applied the patch in 14 and tested. Everything looks good. I followed the new logic in BlockTest.php as follows.
$this->assertFieldChecked('edit-visibility-request-path-negate-0');
ensures the the negate option defaults to false when initially viewing the config form.After saving the test block with
$edit['visibility[request_path][negate]'] = TRUE;
, the tests check to ensure the the negate option is TRUE with$this->assertFieldChecked('edit-visibility-request-path-negate-1');
.All looks good to me, thanks! Jason.
Comment #16
alexpottThis issue addresses a major bug and is allowed per https://www.drupal.org/core/beta-changes. Committed 2eee259 and pushed to 8.0.x. Thanks!