Problem/Motivation
Some blocks, like user login, should be shown on 403/404 response pages.
Others, like the help block, should not.
Proposed resolution
Add a visibility setting to blocks that restricts visibility to any combination of 200/403/404 responses. Default this to show the block for all responses.
Remaining tasks
Review the test cases and decide whether they match the expected behavior.
Review UX of the configuration form
Write the change record
User interface changes
* Adds a new visibility setting for blocks
API changes
* New condition plugin response_status for 200/403/404 response code
Screenshot before/after
Before
After
Comment | File | Size | Author |
---|---|---|---|
#189 | 2245767-189.patch | 19.06 KB | benjifisher |
#189 | interdiff-2245767-169-189.txt | 574 bytes | benjifisher |
#169 | 2245767-block-response-status-169.patch | 19.11 KB | John Pitcairn |
Issue fork drupal-2245767
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #1
tim.plunkettThis adjusts the help block.
Comment #2
dawehnerHere is a test.
Comment #4
tim.plunkettSo, is it okay to rely on the request in an access check? Aren't these cached somehow?
Comment #5
alexrayu CreditAttribution: alexrayu commentedProbably it's better to say, 'Show block on error pages' rather than 'exception pages'. 'Exception' is quite esoteric.
Comment #6
dawehnerIf the access is cached, it is just cached statically, so this is fine. Can we please inject the request stack instead?
Comment #7
tim.plunkettAddressed both #5 and #6.
Comment #8
alexrayu CreditAttribution: alexrayu commentedThe patch works great for me.
Comment #9
alexrayu CreditAttribution: alexrayu commentedComment #10
olli CreditAttribution: olli commentedReroll after #2245783: Regression: Help blocks display on 403/404 page and a small fix to the checkbox. I think we need a new test because the other issue added a similar test.
Comment #11
olli CreditAttribution: olli commentedOur ExceptionController does not set the 'exception' attribute. Should we check for '_exception_statuscode' here or set the attribute in ExceptionController?
Per #2245783: Regression: Help blocks display on 403/404 page I think we should not change this, but always hide the help block on 403/404.
Comment #12
dawehnerBut doens't the exception listener?
Comment #13
olli CreditAttribution: olli commentedYou are right it does, but that request (with exception attribute) is not used if you set error pages at admin/config/system/site-information.
Comment #14
olli CreditAttribution: olli commentedHere is a new test and the exception attribute. Couldn't find any usages for _exception_statuscode - do we need it?
Comment #15
tim.plunkettIt doesn't seem like we are using it anywhere yet, but I'm not sure if there are other plans for it. Can you open a new issue about removing that part?
Really good catch on the subrequest, thanks!
Here's the patch without the removal of _exception_statuscode.
Comment #16
olli CreditAttribution: olli commentedThanks, filed #2251249: Make exception and _exception_statuscode available on all exception pages..
What do you think about #11.2? The help block does not really work well on error pages and "could potentially cause an information disclosure vulnerability" (#2245783-9: Regression: Help blocks display on 403/404 page).
Comment #18
jhedstromPatch no longer applies.
Comment #24
mpolishchuck CreditAttribution: mpolishchuck at Alpha Web Group for Dazzle commentedHello.
I've created new patch for this issue (tested on Drupal 8.5.6). It resolves the issue for me. But it does not contain changes for help block (it will be hidden on error pages in any case).
Hoping it will be useful for someone.
Comment #25
mpolishchuck CreditAttribution: mpolishchuck at Alpha Web Group for Dazzle commentedUpdated version of patch #24 with fixed tests affected by this change.
Comment #26
mpolishchuck CreditAttribution: mpolishchuck at Alpha Web Group for Dazzle commentedUpdated version of patch #24 with fixed tests affected by this change.
Comment #27
ptmkenny CreditAttribution: ptmkenny commentedI tried the patch in #26, but it does not work in the following case:
I have a node (content type: basic page) that I use as a custom 403 (access denied) page.
I set this as the custom 403 page on: /admin/config/system/site-information. The path is /node/21, and the alias is /error/403.
On the block admin page, I added the user login block to the content region. I added the restriction "show only on certain pages":
/error/*
/view/*
/node/21
I have a view that requires "add content type ABC permission" to access. The path to the view is /view/abc. The anonymous user does NOT have this permission.
So, as an anonymous user, I go to /view/abc and am shown the content of /node/21. BUT, the user login block is not shown.
If I go to the block administration screen and remove the "show only on certain pages" restriction, then I see the block as the anonymous user on /view/abc, but I don't want to show the login block on every single page.
Since this patch adds a checkbox "Show block on error pages", I assume that if I have the error page path set as an allowed path and I have checked the "Show block on error pages" checkbox, then I should see the block, but I don't.
Comment #28
zanvidmar CreditAttribution: zanvidmar as a volunteer commentedI can confirm that patch #26 works as designed in combination of custom error page set via Basic site settings and rabbit hole module.
Comment #30
bmcclure CreditAttribution: bmcclure as a volunteer and at Top Floor commentedI thought this would solve an issue I was having, but this turned out not to do what I would expect and isn't really useful in my case.
I would expect that I can explicitly choose to show a block on error pages, regardless of other visibility settings. If other visibility settings are taken into account, then there seems to be no way to only show a block on the 404 page, but nowhere else.
I tried making the block visible on the node I'm using as a 404 page, and then checking the box to display the block on error pages, but it seems that the path filter is overriding the "show on error pages" checkbox. Is this only able to be used on blocks that are visible by default on the site?
It almost seems like we need a path filter such as "[404]" or "[error]" or something to be able to match any path that would return that error, so that it can be combined with other path-based filters.
Comment #31
geek-merlin> I would expect that I can explicitly choose to show a block on error pages, regardless of other visibility settings. If other visibility settings are taken into account, then there seems to be no way to only show a block on the 404 page, but nowhere else.
This is what i expected too from the IS.
> Add a visibility setting to blocks that shows them on 403/404 pages.
Looking into the code, this adds an additional restriction on the path condition. We should either
* a) extend this path condition addition to errorpages/ordinarypages/both
* b) make this a separate condition plugin
Comment #32
jidrone CreditAttribution: jidrone as a volunteer commentedI think a separate condition plugin is better, I have the code for that in a project in working on.
If you want I can provide that code for this patch.
Comment #33
DuaelFrHi there! Thank you all for the work done here so far.
I agree that a separate condition plugin would be better. It would totally improve site builders options.
@jidrone would you share your code with us, please?
Comment #34
geek-merlin@jidrone #32:
> If you want I can provide that code for this patch.
I'd appreciate and review that.
Comment #35
leymannxIs this code similar to the code of https://git.drupalcode.org/project/block_in_page_not_found?
Comment #36
leymannxOkay I added a new condition in
core/modules/system/src/Plugin/Condition/StatusCode.php
. And I basically made it follow all similar conditions. You can select status code 200, 403 and 404. Or select none to have this block shown on all pages. In a first attempt I tried to make this work with only 403 and 404 being selectable but this would have made this condition plugin leave the common pattern. I think having 200, 403 and 404 as options makes more sense in this place and maybe even works better together with the other condition plugins.We still need tests though. I'm still quite inexperienced with tests. But with someone giving me some hints (where to add them exactly or what to look at as samples or similar use cases) I'm absolutely willing to look into it as well.
Comment #37
leymannxComment #38
leymannxUi, seems there's a bit more to do.
$ drush cim
is failing now due tostatus_code
not being a valid plugin ID. Need to look if this comes from Drupal or from Drush.Comment #39
leymannxFalse alarm. Probably just the cache. But love to hear your opinions.
Comment #40
geek-merlinGreat stuff. The interface looks super intuitive. Code-wise it looks quite straightforward. Did not test though.
Comment #41
phjouIt would be nice to add an option like we have for the page paths.
Show for the listed codes
Hide for the listed codes
Because if you use the option, not "path/test" path, you can also wanted it to not show on the 404 page and you cannot predict the path. Just a suggestion.
Comment #42
leymannxThanks for the feedback.
I think this show/hide option for the listed status codes doesn't make much sense for this one condition plugin. There are only three codes. If you don't want to have a block shown on the 404 page, you'd select 200 and 403, keeping 404 unselected.
Hiding a block on all three status codes wouldn't make much sense as well. Why would you place it on the page then at all? And even if this would be necessary for whatever reason you'd instead simply create a new region naming it "Disabled" for example which never gets printed anywhere and place this block in this region.
This condition plugin should enable you to
Probably some test cases in there.
I'm currently using the provided patch on a site to have a "Login and redirect back here" button placed on 403 status code and path restricted to "/node/*" and "/admin/content". Editors following a link to https://example.com/node/1/edit or https://example.com/admin/content while not being logged in will see this block. But it won't appear on just /admin or /admin/config 403 pages for example. Works fine so far.
Comment #43
leymannxI haven't tested it yet with a specific node defined as 403/404 page. But I'd say it should work there as well.
Comment #44
geek-merlin#41-43:
#42 is correct, "not 404" is equivalent to "200, 403"
If you need a more complex condition, use [Block Visibility Groups](https://www.drupal.org/project/block_visibility_groups).
HTH!
Comment #46
mikeegoulding CreditAttribution: mikeegoulding at Four Kitchens commentedUsed #36 on a site recently with a specific node targeted instead of just the status code and it worked well. Really saved the day too as we needed a specific block on the 404 page.
Comment #47
Phil Wolstenholme CreditAttribution: Phil Wolstenholme as a volunteer commentedI'm using #36 to try and only show breadcrumbs on non-error pages.
I've checked the '200' box to only show the breadcrumbs on HTTP OK pages. Now my breadcrumbs are gone from all pages, not just from my 404 page.
I'm going to check out https://www.drupal.org/project/response_code_condition in the interim.
Comment #48
SimeonKesmev CreditAttribution: SimeonKesmev commentedI haven't done a full review of the patch, but used it and hit an error. The condition is expecting that a request can be only interrupted by HttpExceptionInterface exceptions, but that's not the case. For example see Drupal\Core\Form\EnforcedResponseException.
Here is the fixed patch to handle this. It would be good to have a deeper look into whether a better handling of this is necessary.
Comment #49
tim.plunkettThis needs automated test coverage.
The code looks good, some nits below.
The plugin ID for the plugin instance.
Normally the order is configuration, id, def, and then whatever custom stuff.
Also keep the ); on its own line please
Please remove the extra spaces before the => on each line
Can this use formatPlural() instead of t()?
Comment #50
leymannxThanks a lot for the review. Included feedback from #48 and #49.
I'm still completely new to core tests. I don't even know how to run core tests. But I suppose we are going for core/modules/block/tests/src/Unit/Plugin/migrate/process/BlockVisibilityTest.php right?
Comment #51
leymannxOr core/modules/block/tests/src/Functional/BlockTest.php? Or both?
Comment #52
leymannxComment #53
leymannxI can confirm #47.
$status = $this->requestStack->getCurrentRequest()->attributes->get('exception');
returns NULL on 200ers since there's no exception. We need to get the status code differently. I will have look into the mentioned module.Comment #54
geek-merlinThe module mentioned in #47 leverges the same code as we and says it only works for 4xx response codes.
Comment #55
leymannxThe Response Code Condition module has the same problem. It currently only works with error codes, but not with 200. See ResponseCodeCondition.php#L103-105.
I'm wondering myself now if it's safe to assume that if there's no exception the status code must be 200. I reworked the last patch that way now and it seems to work just fine when testing it manually. But again, I'm unsure about the assumption.
I also created an associative array to work with. I think that's much faster than
in_array()
.Comment #56
leymannxComment #57
geek-merlin> I'm wondering myself now if it's safe to assume that if there's no exception the status code must be 200.
I think not academically, but for our purposes, we can say that the absence of a HttpException means a 200 response.
We could have a redirect (301/303) response, but these don't show blocks.
> I also created an associative array to work with. I think that's much faster than in_array().
i did not look into the code, but the the impact usually is neglebible so i would not sacrifice readability for it.
Comment #58
leymannxI got some enlightening input under a related post on Drupal Answers from @4k4.
The method
\Drupal::requestStack()->getCurrentRequest()->attributes->get('exception')->getStatusCode()
actually is there to differentiate between a 403 or 404 error and serve the corresponding error page (from Drupal not from the server). The absence of that exception doesn't tell that the status code is 200. It just tells you are not on an error page.This finally brought me to the conclusion that the patch I added to introduce a status code condition simply hasn't the right naming yet. This condition must not be there to differentiate between status codes. It must differentiate between being on of the two error pages (403, 404) or on a page with no errors. So this condition should be named: "ErrorPage.php", label: "Error Pages". The rest can probably stay the same. Just some renaming.
The options should be like @4k4 suggests:
* no-error pages
* 403 page
* 404 page
Comment #60
Daniel KorteRerolled against 9.1.x and changed to ErrorPage.php, label: "Error Pages" per #58
Comment #61
jyotimishra-developer CreditAttribution: jyotimishra-developer commentedComment #62
jyotimishra-developer CreditAttribution: jyotimishra-developer commentedComment #63
jyotimishra-developer CreditAttribution: jyotimishra-developer commentedAssigning myself for testing
Comment #64
jyotimishra-developer CreditAttribution: jyotimishra-developer commentedHi, I have applied the patch in #60 in Drupal instance 9.1.x and it worked!!
sharing screenshoots for reference.
Steps that I followed to test:
1. Applied patch
2. Go to base-url/admin/structure/block
3. Configure any block, you will find an extra column there "Error Pages"
4. Click on "Error pages", will display some error code options, choose any of them and save the block
5. Visit Access denied page, if you choose 403 option on error pages while configuring the block
6. you will that block on Access denied page.
Note : there is also a small checkbox on "Error pages" option while configuring the block that is "Negate the condition ", if you checked this enable, it will perform in opposite direction.
Comment #65
jyotimishra-developer CreditAttribution: jyotimishra-developer commentedShared screenshoots manner :
1. Block configuring page before applying patch
2. Block configuring page, after applying patch
3. Access denied page screenshot without configuring search block, you see search block there
4. Access denied page screenshot after configuring search block with negate condition, which means you will not see search block there
Comment #66
jyotimishra-developer CreditAttribution: jyotimishra-developer at Srijan | A Material+ Company for Drupal India Association commentedComment #67
jyotimishra-developer CreditAttribution: jyotimishra-developer at Srijan | A Material+ Company for Drupal India Association commentedIt is RTBC from my side.
Comment #68
jyotimishra-developer CreditAttribution: jyotimishra-developer at Srijan | A Material+ Company for Drupal India Association commentedComment #69
mlncn CreditAttribution: mlncn at Agaric for Drutopia, Cambridge, Massachusetts Family Policy Council, Find It Cambridge commentedThanks jyotimishra123!
Comment #70
xjmThis still needs test coverage.
I think probably a usability review is also in order. You can get usability feedback in the
#ux
channel in Drupal Slack.Thanks!
Comment #71
leymannxI posted the issue in the #ux channel on Slack.
Comment #72
ckrinaWe reviewed this last week during the weekly UX call and we agreed on suggesting to move it back to a separate tab with the title Error pages because it's easier to define separate conditions. Also, removing the 200 option (so 403 and 404 are indeed error pages) and it's easier to understand to anybody not used to that terminology and it's clear that "Pages" and "Error Pages" are different things.
Also, another suggestion is to add the English word for each error, like "Page Not Found (404)".
Comment #74
andrewmacpherson CreditAttribution: andrewmacpherson as a volunteer commentedThis patch addresses #72.
Each condition plugin already gets a separate tab. It's pictured in the second screenshot of #65. So there's nothing to do here.
Done. Also removed some 200-specific logic from
evaluate()
.Done.
Comment #75
andrewmacpherson CreditAttribution: andrewmacpherson as a volunteer commentedI've been thinking about how extensible this is. Can a custom module add another status code?
The patch here adds a validation function, which only accepts status codes in the 4xx range.
With that constraint in place, a custom module can do something as simple as this:
Also, an updated screenshot of the block form.
Comment #76
andrewmacpherson CreditAttribution: andrewmacpherson as a volunteer commentedThe "negate the condition" checkbox hasn't been discussed yet. All of the visibility plugins get this from
ConditionPluginBase
butBlockForm
hides it for most of them.In this case though, the negate feature is VERY useful. A key use case of this condition will be to keep error pages clutter-free.
Should we change the checkbox to a pair of radio buttons, like the Pages (path) condition? I've left it alone, for now.
(Aside: what's the reason for hiding the negate checkbox on the role, language, and content type conditions? I went looking for the history of that, but couldn't find the issue.)
Comment #77
andrewmacpherson CreditAttribution: andrewmacpherson as a volunteer commentedCoding standards fixes. The testbots complained about #75.
Comment #78
jidrone CreditAttribution: jidrone as a volunteer commentedRegarding the "Negate" I think it's ok to show it because it gives the site builders more flexibility, also there are issues like this #2986958: Add support for negating user role condition for block visibility where other developers are claiming that removed functionality for other conditions.
We should keep the checkboxes because it is possible to add more error codes, then using checkboxes allows creating customized conditions.
Comment #80
SpadXIII CreditAttribution: SpadXIII at SIM commentedFor those that need it, I did a reroll for drupal 8.9 of the last patch in #77
Comment #81
John Pitcairn CreditAttribution: John Pitcairn commentedRegarding the "negate" condition, I'd definitely want that available.
This makes it far easier do things like to use a node as a 403/404 page, while you also want to limit the page title block visibility based on content type, and based on negated /node/* paths. The 403/404 negate condition allows setting up multiple title blocks to do this. Without it, you wind up with two title blocks on 403/404, or no title block when the user views that actual node. This has been a thorn in my side for years.
Comment #82
vikashsoni CreditAttribution: vikashsoni as a volunteer and at Zyxware Technologies commented#80, #77, #75 not working in 9.3.x.dev
#60 Patch working in drupal-9.1.x sharing screenshot ....
Comment #83
othermachines CreditAttribution: othermachines commentedThe patch in #77 works for me on Drupal 9.2.7.
I added the core Search form block to the content area, and it is displaying correctly when 403, 404, or both, are selected under "Error Pages".
One small nit: "Error Pages" should be "Error pages" (small 'p') as per Drupal conventions.
Comment #84
ankithashettyRerolled the patch in #77, thanks!
Comment #85
danflanagan8Here's a first pass at test coverage. It's a Kernel test that has lots of test cases. Maybe we'll want to add
BlockTest::testBlockVisibility()
too, but this is a start at least.However, in writing these tests, I discovered I was very confused about the expected behavior when the response code is 200. Should this condition always execute as TRUE if the response code is 200?
I'm also confused about the case where "negate" is checked but neither of the 4xx boxes is checked. If the response is a 403 then I would expect the condition to execute as TRUE. But if I uncheck the negate box and get a 403, then the condition will still be TRUE because it's unconfigured and essentially ignored. It's so weird to me that there would be a scenario where "negate" does not create the opposite result.
Can someone clarify these scenarios for me? It would be great if someone could evaluate each of the test cases in the new data provider.
I still think the UX needs some improvements. I think this was clearer back when there was a 200 checkbox and no negate checkbox. At the very least, there needs to be some clarification for what this condition does when the response is 200.
Comment #86
danflanagan8I always do that with data providers for some reason...
This one should run. It's going to fail though on the two cases where negating does not result in the opposite value when the condition is executed.
Comment #88
danflanagan8So now we have some test cases to work against!
The ones that failed in #86 are cases where checking "negate" does not change the result of the condition. Which is weird to me.
This new condition also needs schema. That can be added to
system.schema.yml
and will end up looking very much likecondition.plugin.request_path
which is near the bottom of that file.Comment #89
John Pitcairn CreditAttribution: John Pitcairn commentedMaybe this needs to be a bit more user-friendly, with a
#states
element that hides the negate checkbox unless a 403/404 condition is checked, and corresponding validation, ie "You have not selected anything to negate". Maybe that's a separate core issue for block visibility plugins?That aside, to my mind, negate checked with no other conditions is pure boolean, saying something like
NOT(IN([ ]))
, ie show the block on any response (when the response is notempty()
if we want to be pedantic). Implement that as a fallback, but it's a bit silly, and hardly beginner-friendly. It shouldn't be a possible UI selection.Comment #90
John Pitcairn CreditAttribution: John Pitcairn commentedWe are only providing 2 status codes as checkboxes, corresponding to what core config considers "error pages", so all status codes in the description for negate is misleading (503 doesn't even hit Drupal for example) and should say all error codes above or such. Let's not assume site builders are clueless about other HTML error codes.
The boolean logic isn't the problem. The labelling/descriptions is the problem.
Comment #91
ibullockFor anyone moving on from using #55 previously you might run into the same issue I did where the status_code plug-in is missing.
My fix was to temporarily put the file back at
core/modules/system/src/Plugin/Condition/StatusCode.php
, find all blocks using this setting and resave them, then export the config and remove the file again.I'm not sure if I'll need to take similar steps yet in my deployment, but it at least got me past the error locally.
Comment #92
tim.plunkettI realize there are a lot of potentially misleading examples in core, but I don't think this should check
isNegated()
itself.ConditionManager::execute()
(which is what is called by$condition->execute()
) has this after evaluating the condition:Comment #93
danflanagan8Thanks, @tim.plunkett. I removed that
isNegated
check and now all the test cases pass.I also added schema, which I pointed out as missing in #88. I went with
string
as the type. Even though they are numeric strings, they're not really numbers because we don't do math with them.In addition to the updated patch, I have updated the Remaining Steps in the IS. To me it looks like there are at least two important things remaining:
Regarding #1, I'm still confused about what is supposed to happen on 200 pages and I'm not positive the tests accurately reflect the expected behavior.
Regarding #2, part of the reason I'm confused about #1 is that the configuration form is not clear enough (to me at least).
Comment #95
mlncn CreditAttribution: mlncn at Agaric for Drutopia, Cambridge, Massachusetts Family Policy Council, Find It Cambridge commentedAnother contrib module for this, https://www.drupal.org/project/http_client_error_status
Comment #96
mlncn CreditAttribution: mlncn at Agaric for Drutopia, Cambridge, Massachusetts Family Policy Council, Find It Cambridge commentedThis patch is more powerful, and seems to be through code review and working. Would be great to have in core!
Comment #97
quietone CreditAttribution: quietone at PreviousNext commentedNice to see progress here.
Starting a review.
The issue summary is out of date. It should state that this patch is adding a new tab to the configure block screens, not just adding a setting. Also, the screenshot does not match the latest patch. This needs up-to-date before and after screenshots in the Issue summary. Adding tag.
This is changing the UI, adding Usability tag.
Questions were asked in #93 that need to be addressed.
I skimmed the code and this jumped out at me.
It would be a lot easier to understand if the name explained what this test case is for? Or, maybe in the doc block explain the cases? As is it is hard to figure out if a test base is missing.
Comment #99
tobiasbUpdated issue summary.
I bump also the version to 10.1.x, because this is the version for new features.
Comment #100
tobiasbComment #101
tobiasbThe code is now more typehinted and the test data yielded with inline comments.
Comment #102
tobiasbComment #103
tobiasbUpdated after screenshot, based on 10.1.x code.
Comment #104
andrey_zb CreditAttribution: andrey_zb as a volunteer commented#93 works good on Drupal 9.4.8, so I guess it is RTBC
Comment #105
prasanth_kp CreditAttribution: prasanth_kp as a volunteer and at Zyxware Technologies commented#102 applied successfully on 10.1.x-dev
Comment #106
podarok#102 looks good
Comment #107
catchMostly looks great, but wasn't sure about these two points:
Why are we validating a range between 400 and 499 when the options are hard-coded to 404 and 403 - also checkboxes should enforce that these are the only possible values sent to the form anyway?
Is 'return true' the language we use elsewhere? Can't we use something more colloquial/closer to what actually happens like 'show' or 'allow'?
Also this should have a change record.
Comment #108
jidrone CreditAttribution: jidrone as a volunteer and at Agileana commentedI agree that validation is not needed for this condition, so I did the following changes.
Interdiff tool didn't want to work for me today, so I couldn't provide interdiff now, sorry.
Comment #109
simgui8 CreditAttribution: simgui8 as a volunteer and commented#108 works fine, thanks
Comment #110
gaurav-mathur CreditAttribution: gaurav-mathur at Dotsquares Ltd. commentedApplied patch #102 successfully on drupal version 10.1.x-dev
Adding screenshot for the reference.
Thank You
Comment #111
SpadXIII CreditAttribution: SpadXIII at SIM commentedQuick re-roll for 9.5.x
Comment #112
Anas_maw CreditAttribution: Anas_maw at Coders Enterprise Web & Mobile Solutions commentedThe last patch is not working as expected, for example, if you leave the error pages tab empty (don't check any options) and save the block, the block will not appears at all, the problem is because the array_filter function on submitConfigurationForm is removed in patch #75 and added to validateConfigurationForm function, then the validateConfigurationForm removed in patch #108, what causes empty options to be saved them in the configuration, and the evaluate function will not return true.
This patch solves the issue by re-adding array_filter on submitConfigurationForm.
This patch is for Drupal 9.5.x
Comment #113
Anas_maw CreditAttribution: Anas_maw at Coders Enterprise Web & Mobile Solutions commentedThis patch is for 10.1.x
Comment #114
ameymudras CreditAttribution: ameymudras at Salsa Digital commentedRe rolling #112 for 10.1.x
Comment #115
Anas_maw CreditAttribution: Anas_maw at Coders Enterprise Web & Mobile Solutions commentedThank you @ameymudras
Comment #116
sonam.chaturvedi CreditAttribution: sonam.chaturvedi at Salsa Digital commentedVerified and tested patch #114 on 10.1.x-dev. Patch applied successfully.
Test Steps:
1. Goto Structure > Block Layout
2. Configure any block
3. Check "Error Pages" tab exist with checkboxes for status code (403, 404) and Negate condition
4. Check by default, this is true for blocks
5. Place "User Login" block in content region and do not check any options under "Error Pages" tab
6. Visit "access denied" and "page not found" pages and check "User login" block renders
7. Now, edit "User Login" block > Select "Negate condition" checkbox under Error pages tab
8. Visit "access denied" and "page not found" pages and check "User login" block does not render
9. Now, edit "User Login" block > Select only "Access denied (403)" checkbox under Error pages tab
10. Visit "access denied" and "page not found" pages and check "User login" block renders only on "access denied" pages and not on "page not found" pages.
Test Results:
1. By default, Error pages with status code 403 and 404 are enabled for all blocks
2. When no checkbox option is selected under Error page tab for "User login" block > "User login" block appears on 403 and 404 pages
3. When only "Negate condition" checkbox option is selected under Error page tab for "User login" block > "User login" block does not appear on 403 and 404 pages
4. When only "Access denied (403)" checkbox option is selected under Error page tab for "user login" block > "User login" block appears only on 403 pages and not on 404 page.
Before Patch:
After Patch:
When no status code is selected > "User login" block renders on 403 and 404 pages by default:
When only 403 selected > "User login" block appears on 403 only and not on 404 page:
Moving to RTBC
Comment #117
catchNot strictly relevant to this issue but just a bit of history/archaeology - we used to hide blocks completely on 404 pages: #232037: block_list() renders all blocks, even on 404, then we showed them again in #423992: Remove show_blocks page variable. In both cases it was all or nothing, adding it to block visibility never came up as far as I know but possibly would have saved us some flip-flopping 10-15 years ago.
Comment #118
alexpottAlong with the change to just save the array keys we should also change this to
Then we have nice config with no duplication.
The whole select none to display on all being the same as selecting both 403 and 404 is very very confusing from a user experience perspective. In order to make this as usable as it is for the other options here in the blocks UI (i.e. roles and content type) we need to fix blocks.js as per below BUT also we're adding a further complexity by adding a negate - in order to allow us to see this block should not appear on a page. I think we need to revisit how the javascript function
checkboxesSummary()
in block.js works if a negate option is present.$this->configuration['status_codes'] = array_keys(array_filter($form_state->getValue('status_codes')));
Will make nicer config along with the change above to the schema.
Comment #119
alexpottThe blocks.js changes actually was present in patches around #108 but got removed (probably due to a mistake) in #111.
Comment #120
alexpottIt's important we have a test that actually saves a block plugin with this configured because without that we have no test coverage of the configuration schema.
Comment #121
John Pitcairn CreditAttribution: John Pitcairn commentedIt's just silly here IMO. There are only 2 options. They are not dynamic like content types or roles so there will never be more, which is the only reason you'd need negation and select-none-means-all.
Why do we try to force a consistent selection UI for both a simple non dynamic set and a more complex dynamic one when it results in far worse UX in the simple case?
Two checkboxes (checked by default?), no negation, less confusing help text?
Comment #122
leymannx🎯👆🏻
Comment #123
DuaelFr#121 let's suppose we only have those two checkboxes and see what use cases are covered and how:
We could have a "200" checkbox too but we couldn't call this filter "Error pages". If you really want to remove the negate condition (which is quite common in a lot of other conditions), we could rename the entire condition set to "Response status" and provide "200 - Normal response", "404 - Page not found" and "403 - Forbidden access" choices.
I'm okay with the existing implementation but I'm no usability expert. I only hope this won't need another 8 years to get into Core.
Comment #124
John Pitcairn CreditAttribution: John Pitcairn commentedMy bad, I was neglecting those use cases.
So this one needs nothing selected = "no condition". It's the labelling and description that cause confusion by mixing "show", "enforce" and "allow" without good explanation. We could simplify and clarify the labelling and descriptions, using the "restrict" terminology that is already used in Pages, Roles and Content type vertical tab summaries (for Claro).
And this one needs the negation, ie not 403 or not 404 for the above. I've seen non-technical users really struggle with this concept when implemented as a single checkbox. I think this should use a pair of radios explicitly stating what happens, the same as the Pages condition. The default should be "hide", which is not ambiguous with a default of nothing checked. Users have to explicity choose "show" and leave nothing checked to generate an ambiguous state, and that's when the checkbox description really needs to describe what will happen.
Finally, if the vertical tab label is "Error pages", I don't think we should be labelling the selection UI as "Status code".
So, something like:
Error pages
[ ] Access denied (403)
[ ] Page not found (404)
If no error pages are selected, no restrictions will be applied.
( ) Show for the selected error pages
(•) Hide for the selected error pages
Comment #125
John Pitcairn CreditAttribution: John Pitcairn commentedHmm. Unfortunately that isn't quite how block visibility conditions and negate work. Nothing selected = no restrictions are applied, but negate applies as if both are selected.
Comment #126
DuaelFrI keep thinking that this is not user friendly because status 200 results are entirely implicit. If someone does not have a basic understanding of how this condition works, they will have quite a hard time to figure out what to do.
Is suggest the following:
This way, the default status as shown above is the current behavior of blocks (shown on every pages) and it's explicit for all use cases I listed in #123.
What do you think about it?
Comment #127
John Pitcairn CreditAttribution: John Pitcairn commentedUnfortunately thats not how negation works. The problem is that selecting none and negating (hide) is the same as selecting all and negating. There are examples of this all over core and contrib, but it seldom produces a good UX.
So nothing selected and "hide for selected" hides for all. That's very confusing and very difficult to explain with non-dynamic labels and descriptions.
Maybe #states could be used here to hide the negation options until a selection is made, and we unset the negation if nothing is selected.
So hide for all becomes an explicit choice (check both and negate), with show for all remaining the default.
I should have time to try that today.
Comment #128
John Pitcairn CreditAttribution: John Pitcairn commentedThis patch:
The last point is inconsistent with how other conditions with negation behave when nothing is selected (ie poor UX). But it does help remove a major source of confusion.
It does not address the other points from #118. Setting to needs review for testing.
I think this may surface a bug in #states where deselecting one checkbox from a checked pair with an OR condition triggers a change when it shouldn't, but only after an initial load in that state. Not sure if there is an existing issue for that.
Comment #129
John Pitcairn CreditAttribution: John Pitcairn commentedAddressing the use cases from #123:
display a block on all pages (error and non-error)
Default configuration, nothing checked
display a block only on all error pages
Check 404 and 403, plus show on selected error pages
display a block only on all non-error pages
Check 404 and 403, plus hide on selected error pages
display a block only on 404 or 403
Check 404 or 403, plus show on selected error pages
display a block on all non-error pages AND 404 or 403 (but not both)
Check 403 or 404 (but not both), plus hide on selected error pages
Comment #130
John Pitcairn CreditAttribution: John Pitcairn commentedBack to needs work for the points from #118. I may have time to look at those over the next day.
Comment #131
John Pitcairn CreditAttribution: John Pitcairn commentedThis can be worked around by using "invisible" as the negate element state rather than "visible", with a simple AND condition for the checkbox states. Good.Nope.Comment #132
John Pitcairn CreditAttribution: John Pitcairn commented@alexpott, from #118
I think we can get away with checking whether a condition is set OR negated, and the summary saying either of:
Which at least is a correct indication of whether restrictions are applied or not. Note it's restricted "for" not "to".
The only other example of this in standard profile for checkboxes is the Vocabulary condition, and that sidesteps the issue by not providing a summary at all. The Pages condition summary is wrong if its textarea is empty and it is negated, but a minor change as above would help. Roles and Content type conditions are not negatable.
Beyond that, I think fixing the poor UX of other negatable conditions is beyond the scope of this issue, but definitely warrants an issue of its own. We could get this committed and use the work done here as a possible way forward.
Comment #133
_utsavsharma CreditAttribution: _utsavsharma at OpenSense Labs for DrupalFit commentedMade changes as per the #118.
Cannot address 118.3 .
Please review.
Comment #134
_utsavsharma CreditAttribution: _utsavsharma at OpenSense Labs for DrupalFit commentedForgot to upload patch.
Comment #135
_utsavsharma CreditAttribution: _utsavsharma at OpenSense Labs for DrupalFit commentedMy mistake forgot to upload patch.
Comment #136
John Pitcairn CreditAttribution: John Pitcairn commentedThanks! I will attempt to address #118 point 3 as I described above. Tomorrow.
We will also need a test as per #120.
Comment #137
alexpottI think #126 is a great idea because then we don't need to deal with negation at all. And it can work like the role / content selectors. We don't need to fix any JS or anything. Like the forma can be:
If none are selected it's not restricted. If 200 is selected then the blocks only appear on 200s... etc. This will cover all use-cases. If you don't want the block to appear on 403 then select 404 and 200.
Comment #138
John Pitcairn CreditAttribution: John Pitcairn commentedOK, will revisit the patch shortly.
Note UX review earlier in the issue wanted the 200 removed, which is perhaps how we have ended up where we are...
Comment #139
John Pitcairn CreditAttribution: John Pitcairn commentedComment #140
John Pitcairn CreditAttribution: John Pitcairn commentedThis patch:
response_status
Do we need to run this past the UX team with our reasons for not following their advice?
Comment #141
John Pitcairn CreditAttribution: John Pitcairn commentedUpdated the IS and added a new after screenshot.
Comment #142
John Pitcairn CreditAttribution: John Pitcairn commented@alexpott: Where/how are existing block visibility condition schema tested via block save? Is it just
BlockTest::testBlockVisibility()
etc, and should we add a test there?Comment #143
alexpottLet's use the constants from \Symfony\Component\HttpFoundation\Response eg. Response::HTTP_OK
Then we don't need to define our own and we're using what Drupal uses under the hood.
Let's call $config $allowed_codes (or something similar) instead. $config does not mean much.
Why do this? With an array this size I think
in_array(Response::HTTP_OK, $allowed_codes, TRUE)
instead ofisset()
is fine.Comment #144
alexpott@status_code - not @error_code
Comment #145
John Pitcairn CreditAttribution: John Pitcairn commentedOK. I may not have any time for a few days, if anyone else wants to jump on it.
Comment #146
John Pitcairn CreditAttribution: John Pitcairn commentedThis patch addresses #143 points 2-4. The relevant Symfony constants are used in the plugin and tests, and all handling/comparison of those is changed to expect integers.
Setting to needs review for tests (passing locally). Assigning while I work on a test as per point 5.
Comment #147
John Pitcairn CreditAttribution: John Pitcairn commentedComment #148
John Pitcairn CreditAttribution: John Pitcairn commentedThis patch:
BlockTest::testBlockVisibility()
Unassigning. Still to do:
Comment #149
John Pitcairn CreditAttribution: John Pitcairn commentedComment #150
John Pitcairn CreditAttribution: John Pitcairn commentedComment #151
John Pitcairn CreditAttribution: John Pitcairn commentedThis should test the block does appear on a 403, so we are testing a real exception response. Will revisit.
Comment #152
John Pitcairn CreditAttribution: John Pitcairn commentedComment #153
John Pitcairn CreditAttribution: John Pitcairn commentedThis tests the block is displayed on a 200 or 404 response and not displayed on a 403 response, before the test user is logged out and the non-anonymous restriction is tested. Should be good to go now.
Comment #154
John Pitcairn CreditAttribution: John Pitcairn commentedDraft change record: https://www.drupal.org/node/3335460
Comment #155
John Pitcairn CreditAttribution: John Pitcairn commentedHave requested a UX review on Slack. Time zones being what they are it probably won't be a realtime conversation...
Comment #156
lauriiiSorry for not reading any of the back scroll – leaving feedback purely on what I see in the screenshot in the issue summary. I think something we should probably clarify is whether selecting an option will make the block visible or invisible on the selected pages. Also, it's currently unclear what is the behavior when none of the options are selected.
Comment #157
John Pitcairn CreditAttribution: John Pitcairn commentedThanks @Lauriii.
We did have more help text in there earlier. I've added something similar back in:
Shows the block on pages with any matching response status. If nothing is checked, the block is shown on all pages. Other response statuses are not used.
This patch differs only by that change. I've updated the issue summary screenshot.
I have to point out that no other core block condition plugins bother to explain what happens if nothing is selected/entered. Let alone what "negate" means in that context.
Comment #158
John Pitcairn CreditAttribution: John Pitcairn commentedComment #159
malaynayak CreditAttribution: malaynayak as a volunteer and at Pegasystems commentedAdding a patch compatible with Drupal 9.5.x. Patch #157 fails for Drupal 9.5.4.
Comment #160
John Pitcairn CreditAttribution: John Pitcairn commentedComment #161
klonosIs it worth it adding 503 to the mix here? (to allow visibility conditions for blocks when in maintenance mode)
Comment #162
smustgrave CreditAttribution: smustgrave at Mobomo commentedThis issue is being reviewed by the kind folks in Slack, #needs-review-queue-initiative. We are working to keep the size of Needs Review queue [2700+ issues] to around 400 (1 month or less), following Review a patch or merge request as a guide.
Removing credit for #159 as it's expected for a patch to be tested before uploaded
Reviewing #157
ResponseStatus
1. Return functions should be typehinted
Functionality everything appears to be working.
Added a block to only appear in 404
Going to /blah I see the block
Going to regular page I do not
Same for 403.
Question this was previously tested for usability in #72 what additional feature needs to be tested?
Comment #163
John Pitcairn CreditAttribution: John Pitcairn commentedThat previous usability review asked us to provide only 403/404 checkboxes and add a "negate" checkbox. The plug in was labelled "error pages" at that time.
We tried that, and it caused confusion over what was being negated, especially when no error page was checked. See @alexpott's comments at #143 and earlier.
We decided it is clearer and cleaner to remove the negation, include a "200 success" checkbox, and label the plugin "response status" instead.
We assume we need an additional usability review due to those changes. Do we?
Note due to timezone differences I am unable to participate in Slack UX review meetings. It would be great if somebody else could do so, specifically referencing the points above.
Comment #164
John Pitcairn CreditAttribution: John Pitcairn commented@klonos: what would the checkbox label be? Are there any other circumstances where Drupal/Symfony returns 503? Maybe this could be a followup issue?
Comment #165
smustgrave CreditAttribution: smustgrave at Mobomo commentedSo then in that case it will need UX review again. Will post to see if they can add to their next call.
Question on the 503, definitely think it would be a follow up that’s postponed on this ticket. But if you get a 503 then I don’t think the Drupal render system is called
Comment #166
klonos@ John Pitcairn I haven't checked whether there were other cases where 503 is returned - I just happened to notice that in D7 in drupal_deliver_html_page() there are checks for 404, 403, and also for 503, and in the case of 503 it was only calling things relevant to maintenance status (it is adding a "'503 Service unavailable'" HTTP "Status" header as well).
Anyway, I thought that it might be relevant here, in case people want to add blocks that are shown during maintenance mode only. That's why I brought it up.
Can certainly be a follow-up issue if people think that it is not relevant/appropriate here or that it unnecessarily broadens the initial scope of the issue.
Comment #167
John Pitcairn CreditAttribution: John Pitcairn commentedI think there is a maintenance page template, but I don't think it can display blocks, can it? The entire region/block mechanism is bypassed. I think.
It would be good to get this 9 year old issue committed, so yeah looking at 503 as a followup issue would be ideal.
Comment #168
smustgrave CreditAttribution: smustgrave at Mobomo commentedPosted in #ux slack to see if anyone can take a look.
Tagging for Followup for someone to open that other ticket for 503.
Can go back into review after typehints per #162.
Comment #169
John Pitcairn CreditAttribution: John Pitcairn commentedAddressing #162: added return typehints to methods in
\Drupal\system\Plugin\Condition\ResponseStatus
.Comment #170
John Pitcairn CreditAttribution: John Pitcairn commentedI've had a poke around in the code involved in displaying the "site under maintenance" page. That seems designed to operate when the DB is not available at all, or Drupal is not fully bootstrapped etc, ie it cannot invoke the block layout system.
Doing so would not be a simple enhancement of the functionality added here, therefore I think we can remove the "needs followup" tag for this issue. A separate core issue could be filed for supporting blocks in the maintenance page, with addition of a 503 option to the response status condition as a necessary component of that. But I'm pretty sure that will have been well thrashed out years ago.
Comment #171
John Pitcairn CreditAttribution: John Pitcairn commentedComment #172
alexpottI think this might be blocked by #3348592: [regression] Language switcher block throws exception when no route is matched
Comment #173
alexpottActually
$exception = \Drupal::requestStack()->getCurrentRequest()->attributes->get('exception');
works with BigPipe so that's good.Comment #174
simgui8 CreditAttribution: simgui8 as a volunteer and commented#169 works for me (thanks everyone)
Comment #175
Gábor HojtsyI think this feature makes a lot of sense, however it did not make it into 10.1, so untagging from release highlights. Would be great to get into 10.2 and then tag for 10.2 release highlights though :)
Comment #177
smustgrave CreditAttribution: smustgrave at Mobomo commentedWill need a reroll for 11.x
It's been posted to the #ux channel few times also. Believe it's on a radar.
Comment #178
John Pitcairn CreditAttribution: John Pitcairn commented#169 tests pass for 11.x.
Comment #179
smustgrave CreditAttribution: smustgrave at Mobomo commentedBrought this up at DrupalCon Pittsburgh and the usability tag can be removed as the bulk was done by @ckrina in #72.
So testing this out by placing a block to appear on each status.
Verified the block appears specifically on those codes and not on valid pages.
Some good excitement for this feature!
Comment #180
Qusai Taha CreditAttribution: Qusai Taha at Vardot commentedRe-roll Patch for 10.0.9
Comment #181
John Pitcairn CreditAttribution: John Pitcairn commentedComment #183
John Pitcairn CreditAttribution: John Pitcairn commentedComment #185
catchStill looks good to me.
Committed/pushed to 11.x and tagging for 10.2.0 release highlights.
The re-roll in #180 looks unnecessary so I committed #169.
Did my best with issue credit, but a lot of people and comments on the issue so apologies for any omissions.
Comment #186
SpokjeSeems like this broke HEAD of 11.x with a cheery
Looks like it's because the new test
\Drupal\KernelTests\Core\Plugin\Condition\ResponseStatusTest
uses the DB tablesequences
in its::setUp()
($this->installSchema('system', ['sequences']);
).This was deprecated in #2665216: Deprecate Drupal\Core\Database\Connection::nextId() and the {sequences} table and schema.
Comment #188
catchWhoops. Reverted for now.
Comment #189
benjifisherAccording to the change record https://www.drupal.org/node/3349345,
I made that change and the test passes locally.
The interdiff compares my patch to the one in #169, since @catch mentioned (#185) that he was ignoring the patch in #180.
I have not looked at the rest of the patch.
Comment #190
smustgrave CreditAttribution: smustgrave at Mobomo commentedSince this was previously reviewed and only issue was the sequence think its safe to RTBC again
Comment #192
lauriiiCommitted badd417 and pushed to 11.x. Thanks!
Comment #194
matoeil CreditAttribution: matoeil commentedwill this not be pushed to drupal10 ?
Comment #195
xurizaemon@matoeil at https://git.drupalcode.org/project/drupal/-/commit/badd417 you can see this becomes available in Drupal 10.2 (click "Tags containing this commit").