Problem/Motivation
#2815709: Settings tray conceptually incompatible with configuration overrides such as translations points out problems when using Settings Tray with blocks that have translation overrides. This problem will also existing with other configuration overrides.
For most blocks the overrides would be on the block entities themselves but for other blocks such as menu blocks that expose menu configuration it adds even more complexity.
Proposed resolution
For blocks that:
- Are currently overridden or
- if have related configuration they are exposing the ability to edit and that configuration is overridden
Then:
- Remove the "Quick edit" link
- Remove the ability to click and edit the blocks
- Remove the "Editable" styling classes and attributes
Original: Proposed Solution:
As a starting point to make the Settings Tray module stable @xjm, the Drupal 8 release manager, suggested that if a block has overrides we should not allow editing in the Settings Tray but rather give the user a message that it is not available to edit the Settings Tray and provide a link to the Block configuration form. This form would allow the user to configure other overrides.
Remaining tasks
Review patch
User interface changes
The "Quick edit" links for blocks that are overridden or have related configuration overrides would be removed.
API changes
New interface \Drupal\settings_tray\Form\BlockFormWithRelatedConfigInterface
'settings_tray' plugin forms would use denote they have related configuration and that form should not accessible if the configuration is currently overridden.
Data model changes
None
| Comment | File | Size | Author |
|---|---|---|---|
| #89 | Screen Shot 2018-01-19 at 10.55.09 AM.png | 54.24 KB | webchick |
| #89 | Screen Shot 2018-01-19 at 2.07.34 PM.png | 65.65 KB | webchick |
| #89 | Screen Shot 2018-01-19 at 2.06.38 PM.png | 98.28 KB | webchick |
| #83 | 2919373-83.patch | 25.38 KB | tedbow |
| #83 | interdiff-81-83.txt | 2.85 KB | tedbow |
Comments
Comment #2
tedbowOk here is a start. It is not working and I am not sure how to make it work.
Here is what I have done so far.
Created to
SettingsTrayServiceProviderto set a container parameter of all the services that are tagged with `config.factory.override`Created `\Drupal\settings_tray\Block\BlockEntityOffCanvasForm::hasOverrides` to determine if the current block entity has overrides by calling `\Drupal\Core\Config\ConfigFactoryOverrideInterface::loadOverrides`. This does not work a block I know has a translation override.
The current code will call
\Drupal\language\Config\LanguageConfigFactoryOverride::loadOverridesif config_translation is enabled but it will not find the override. It seems I would have to call\Drupal\language\Config\LanguageConfigFactoryOverride::setLanguagefirst.If I do that then it will find the override but that doesn't help because we are trying to finding overrides for any modules so I can't assume that knownledge.
Ideas? Is the right direction?
Comment #3
bircherThis would be so much simpler with #2923004: Add method to check if any overrides are applied to \Drupal\Core\Config\Config.
Comment #4
tedbowComment #5
tedbowOk. Here is different approach. The working approach takes the patch from here #2923004-8: Add method to check if any overrides are applied to \Drupal\Core\Config\Config.
I had to check in
settings_tray_block_view_alter()to determine the block has overrides. We can't check insettings_tray_contextual_links_view_alter()because this won't be called every time there might be new override.Also we can't check in
\Drupal\settings_tray\Block\BlockEntityOffCanvasForm::form()because it will be on different route at this point and won't have the same overrides.Comment #6
tedbowThis patch adds a test. It needed a couple changes to the JS because it was not compatible with our PhantomJS configuration.
Comment #7
GrandmaGlassesRopeManArrow function. Destructure `Drupal` & `drupalSettings`.
The wrapping of the .forEach method is kind of strange. Additionally the closing parenthesis for the .forEach method shouldn't be on a new line, and the comma shouldn't be there either. I think you want something like,
Comment #8
tedbow@drpal thanks for the review!
1. Changed to the arrow function and destructed
drupalSettings. I didn't destructureDrupalbecause not in the patch usesDrupalso it would affect 10 lines that have nothing to do with this issue.2. Fixed
UPDATE: looks like the test failure was a problem with 8.5.x
Comment #10
tedbowSetting to "Needs review" because tests passed.
Comment #11
tedbowJust some small fixes.
Comment #12
tedbowJust some small fixes.
Comment #13
wim leersWoahhhh
Perhaps an
@seeto point to the route name?Woahhhhhh! This is scary.
This is leaking cacheability metadata.
->toString(TRUE), assign to a variable, then use$variable->getGeneratedUrl()and add then do$this->renderer->addCacheableDependency($build, $variable).Comment #14
tedbow@Wim Leers thanks for the review!
1. This is because we now have
black-magic.jsin our vender directory!Just kidding, possible in es6 with destructuring.
2. Added
3. I have broken this code up a bit to make it more readable. But maybe there is a bigger probelm?
4. Fixed
Comment #15
samuel.mortensonSome quick nits:
Is there any harm in always calling
NestedArray::getValuehere? I think the behavior is the same, even if$keyis empty.Is the
TRUEhere some debug code left over?And in terms of manual testing, I'm a little confused on how to see the error message on overridden blocks. Here's a flow I went through that seems unchanged with or without the patch:
To me the ideal UX here would be to see "Rechercher" in the title field, even though I know that isn't what this patch is doing. I did expect some behavior to change with the patch, namely that an error is shown on overridden blocks. Are language overrides a special case?
Edit: I re-applied the patch locally this morning and I'm not able to configure a Block with overrides, please ignore my manual testing note.
Comment #16
tedbow@samuel.mortenson thanks for the review!
1. this is from the changes in #2923004: Add method to check if any overrides are applied to \Drupal\Core\Config\Config. I added the "REVIEW" patch to make it more clear what is needed for just this issue. When #2923004 get committed we can remove all that from this patch.
2. 😅 whoops yeah that whole surrounding
ifwas testing and can be removed. Fixed.I update this patch to use the latest patch from #2923004 which now uses
hasOverrides().Comment #18
xjmlolz trailing whitespace (in the ES6 file, even, so we should fix that). :)
Comment #19
tedbow@xjm whoops, thanks for catching that!
Fixed the patch in #16 because my 8.5.x was not up-to-date.
Also fixed "REVIEW" patch so it only has settings_tray related changes
Comment #20
wim leersNote: this will break if somebody alters the URL for this route: the JS will not know.
That has a very tiny probability though.
Nit: s/url/URL/
I still think this is pretty scary.
AFAICT it's possible to let PHP override this instead, either in
hook_contextual_links_view_alter(), or even earlier, inhook_contextual_links_alter(). Then we also don't need JS to dynamically replace the URL … because it'll contain the correct URL.I feel like I'm overlooking something… so I'm sorry if I have!
This is all making the Settings Tray module decidedly coupled to the Block module. The module description should mention that.
#2897272: Fix module description, hook_help(), and document module scope in *.api.php file is already doing that, so that's good :)
s/not/no/
s/edit/editing/
Nit: trailing period that should be removed.
Should it be or ?
Wouldn't
$has_confirm_formbe better?Supernit: extraneous
\n.s/the//
Any reason we don't just always install this module for this test? Then we know it also doesn't cause side effects for other tests.
Plus, makes this test slightly simpler.
WEird indentation.
s/select/selector/
s/Off/off/
Comment #21
ada hernandez commented#1 pending
#2 fixed
#3 pending
#4 pending
#5 fixed
#6 pending
#7 changed to “block”
#8 fixed
#9 fixed
#10 fixed
#11 pending
#12 fixed
#13 fixed
Comment #22
tedbow@Adita thanks for the patch.
Reviewing #21
2. 👍
5, 👍
7. 👍
8. 👍
9. 👍
10 👍
12. There was still problem with the indentation. fixed
13. 👍
Addressing remaining #20
1. No longer an issue b/c JS removed in 3)
3.
@Wim Leers I respect your fear. Let see if we can fix this.
Looks like this is possible with
hook_contextual_links_alter()probably inhook_contextual_links_view_alter()but better earlier(also I forget about
hook_contextual_links_alter()because for some reason it is incore/lib/Drupal/Core/Menu/menu.api.phpand notcontextual.api.phpnot sure why)so this removes all the JS changes 🎉. So #20.1 is not longer an issue
This also would allow a contrib module to use
hook_contextual_links_view_alter()if it could create a good way to edit overridden blocks off-canvas dialog.4.
Why? Because of the permission? The existing route is already using this permission. Block is already a dependency. Am I missing something?
6. Fixed
11. No reason just thinking of performance of one more module installed. But it should be fine to always have it enabled. Fixed.
Comment #23
wim leers:D
Nice! Do you also like this solution better? It definitely results in a patch that is quite a bit smaller!
You can ignore that — like I said in #20.4:
"warning" vs "notice
"override" vs "overridden"
I think this could be made a bit more consistent.
The patch is adding this. But the class already contains this:
This is pretty confusing.
Comment #24
tedbow#23
I do like it better. I tried to reproduce problem I thought I had found before with contextual links not being render for a different language context. My previous comment in the code
This, especially the last sentence, seems not to be true.
getBlockSelector()so I don't need the new method.Comment #25
wim leersRTBC, but this is blocked on #2923004: Add method to check if any overrides are applied to \Drupal\Core\Config\Config, so marking as postponed instead :)
Comment #26
tedbow#2923004: Add method to check if any overrides are applied to \Drupal\Core\Config\Config was committed!!!!
This #24 except with those changes removed.
Unpostponing, also setting to RTBC since @Wim Leers said
Comment #27
wim leersRTBC++
Comment #28
xjmDo we really need a function for this? It's used only once.
Comment #29
tedbow#28 nope, removed
Comment #30
webchickOk, Ted and I just reviewed the patch functionality in a monster 90 minute review session!
The general problem is as described in #2408549: Display status message on configuration forms when there are overridden values (which I didn't realize is still outstanding!), except a bit more "in your face," because we're exposing this form to content author-types. The proposed fix from the patch is that if you hit a block that has overrides (e.g. settings.php hard-coding, translations, etc.) that instead of showing them something that doesn't make sense (like a form that does nothing when you save it), we instead show a message that basically says "buzz off" except with oodles of Drupal jargon. ;)
So my first reaction that was, "Egads, we really need to English-ify that message..."
But my second reaction was, "Why are we even letting them click on Quick edit in the first place then if they in fact can't actually quickly edit?" This is consistent with how we handle e.g. menu items as well; if a menu item would lead someone to a 403 error, we simply don't show them the link so they are not then tempted to click on it.
This is especially annoying because on a given page in Spanish with 8+ clickable regions, 6+ of them will give you the error message if you click on them. But the only way to know which is which is by clicking/"pogo-sticking" on all of them and seeing the result.
So I think the preferred way to handle this, at least from my POV (I will check with the wider UX team in a sec), is to apply the same pattern we have for the page title block to any of these override blocks. In other words, don't show the dotted outline, enticing someone to click, and don't expose a Quick Edit option at all in the first place. Instead, just have the standard contextual links button, as other elements get, and let them choose "Configure block" from there if they want.
@tedbow's concern about this was that people will be confused if in English the page has 8+ regions to click on to quick edit, but in Spanish it only has 2. Which is probably fair. But I don't know that this concern needs to hold the module from being stable; we could iterate on this more post-release, IMO.
Additionally, we hit a couple of bugs during testing which were:
- The "Site name" block is not getting the message treatment, despite also having translatable text inside. Need to do something like inspect "related" config to see if it's overridden.
- For whatever reason, halfway through all of a sudden we stopped getting the message in the tray, even when we were triggering conditions that "should" have triggered the message (e.g. editing the search block while viewing in Spanish). Suspect some kind of caching issue in Contextual Links.
- Somehow the block description got saved as the Spanish translation of the block description?? And we couldn't replicate this from the "normal" block config form.
Also, escalating to "major" since this is a stable blocker for Settings Tray.
Comment #31
tedbowOK. So the thought from the discussion with @webchick was:
This patch does this.
I am not positive right now this didn't happen because I was debugging I am might have stopped a page request in the middle. I will look out for this but haven't seen it before except once months ago.
This happen when I was demo'ing the problem now without the patch. So I edited a block that was currently being overridden with a translation. With any version of this patch so far you would not be able to do this.
Comment #32
tedbowSince the patch no longer opens the tray at all for overridden forms then there is no need for the new route or controller
This patch removes them.
Comment #33
tedbowThis patch handles this. It introduces
\Drupal\settings_tray\Form\BlockFormWithRelatedConfigInterfaceThe 2 settings tray plugin forms implement it.
it simply has
hasOverriddenConfig(). It should be to the block form to decide whether it should be consider overriddenAdded test coverage for the site name being overridden. Then the branding block should not have "Quick edit".
Later when we tackle #2815709: Settings tray conceptually incompatible with configuration overrides such as translations we could decide that we want to actually let the block be edited and how it should behave.
Comment #34
tedbowSelf review
The comment and function name should be updated to indicate that it not only checks the block also related configuration.
Nit: period.
Also update comment
This change is no longer needed. Removing all changes to this file.
Comment #36
tedbowUnrelated test fail in MediaSourceImageTest::testMediaImageSource()
Retesting
Comment #37
tedbowComment #38
tedbowThis is the new interface I introduced so that it can be determined if the "Quick edit" functionality should be enable for the block. @see
_settings_tray_has_block_related_overrides()Right now it is connect to the idea of overridden configuration. My other idea was to just provide something more general like
preventSettingsTrayQuickEditthat would returnboolinstead.That was the 2 forms provided by the module could use this interface to prevent "Quick edit" being used based on overridden configuration but then contrib or other future core uses could prevent a particular block from being used with Settings Tray under other circumstances.
Note if a block should not be used with Settings Tray at all then it should use set the 'settings_tray' form handler to
FALSElike is done in\Drupal\settings_tray_test\Plugin\Block\SettingsTrayFormAnnotationIsFalseBlockComment #39
wim leers#28: good call removing that
@internalhelper method. It used to be called >1 times, not anymore. #29 is better :)#30 (@webchick's review):
I feel like we're going in circles. #30 is re-iterating things already pointed out in #2815709.
Gábor and I have been saying this for a year or so — see #2815709: Settings tray conceptually incompatible with configuration overrides such as translations, which is the issue that spawned this issue. Settings Tray effectively doesn't allow you to do anything on many Multilingual sites.
The thing is that this patch is a step forward compared to the behavior in HEAD, where there is not even this very Drupal-y warning. In HEAD, it'll still show you a form, but it won't actually edit the currently overridden configuration.
IOW: this patch is step 1, #2815709: Settings tray conceptually incompatible with configuration overrides such as translations is step 2. As documented comment at #2815709-39: Settings tray conceptually incompatible with configuration overrides such as translations by @tedbow:
#2815709 is a must-have, per @xjm's comment at #2815709-37: Settings tray conceptually incompatible with configuration overrides such as translations.
#31:
but that's okay.
I hate to ask this but … doesn't that mean that we'd be shipping an unstable module?Apparently #33 addressed this!#38:
@internalfor now, so that we don't commit to this API just yet? IOW: Settings Tray could ship as stable functionality-wise and API-wise, but this portion of the API would not yet be considered stable.Patch review:
When is this not set?
Shouldn't this be
if (isset($links['settings_tray.block_configure'])) {?
s/Any/An/
If we're adding this as a non-internal interface, then we should update
settings_tray.api.php.In fact, shouldn't this simply become the required interface?.
And shouldn't this become
SettingsTrayBlockFormInterface extends \Drupal\Core\Plugin\PluginFormInterface? Because … shouldn't every block implement this? Block plugins that don't consume non-block config would alwaysreturn FALSE;.Comment #40
berdirI assume that settings try uses the config entity API and not low-level config.
Then #2910353: Prevent saving config entities when configuration overrides are applied should be interesting, what I'm doing there is a) adding a hasOverrides() to any config entity and b) throw an exception when trying to save a config entity with active overrides. This means settings tray would then trigger that exception instead of overriding data.
Comment #41
wim leersIt uses the Config API, not the Config Entity API, because things like site name + slogan are stored in simple config, not config entities.
See
\Drupal\settings_tray\Form\SystemBrandingOffCanvasForm::submitConfigurationForm()in particular.Comment #42
berdirMaybe that specific block, but the same problem can also exist for block-level settings like the label. there the API would then prevent
it then.
this basically. This works around the config entity API right now to get at getOverrides(). With my issue, it wouldn't have to do that anymore.
Comment #43
berdirAlso, I didn't read all the of the discussion here, but what's the reason that \Drupal\settings_tray\Form\SystemMenuOffCanvasForm::setPlugin() for example isn't using \Drupal\Core\Config\Entity\ConfigEntityStorageInterface::loadOverrideFree()?
Yes, that could be confusing when you look at the a non-default language and then you edit and you get it in the default config. But isn't it just as confusing that you just won't be able to edit it at all in non-default languages without any visual feedback?
Comment #44
wim leersIIRC that's basically how it works today, and that's what's so confusing.
👌 Indeed! That is supposed to be addressed in #2815709: Settings tray conceptually incompatible with configuration overrides such as translations, this issue is supposed to be the stopgap.
Comment #45
berdir> IIRC that's basically how it works today, and that's what's so confusing.
That is how it works for the simple config (SystemBrandingOffCanvasForm), but that's not how it works with SystemMenuOffCanvasForm for example. That uses a normal load() when it should be using a loadOverridesFree() to get the same behavior.
So what happens there is you load the menu with the translated information, then you change some of it and change the translation back as the original language. That's exactly what #2910353: Prevent saving config entities when configuration overrides are applied wants to prevent.
Given that, I have two points:
A) This issue is currently defined as a task. But the menu case is actually a bug as you can lose your original labels/config. So possibly this issue should be a bug, to unify the different (in some case wrong) cases into one.
B) I'm not really sure that going from settings-tray-always-shows-original to settings-try-never-shows-edit-with-override is really an UX improvement . My approach as a non-ux developer would be instead to show a message indicating that there are configuration overrides/translations (translations is just one use case for having overrides, it could also be domain.module, settings.php based overriddes or something based on the moon phase) and provide a link to the standard form/translate UI.
The second point is just my personal UX opinion (which is likely not worth much :)) combined with the knowledge that we have no use case in core that allows to edit overriden form values in the same place as non-overridden ones, and with the immense complexity of multiple sources of overrides (even possibly on the same config elements), I don't think it's realistic to try and do that.
Comment #46
wim leersIsn't that pretty much what this patch does, the screenshot in #30 shows, and @webchick is arguing against in #30?
Comment #47
tedbow#39 thanks for review.
Re:
After that comment I chatted with @xjm and commented below that solution in this issue sufficient enough for a first pass to mark the module stable because it will stop the user from seeing config values that are currently being overridden.
I wrote
But I forgot explicitly say that this what would be needed for the module to be marked "stable". I will ask @xjm to comment on that issue or #2922603: Mark Settings Tray module as stable.
#2922603 only lists this issue and I know she has reviewed that.
#38.1
I think we should actually change this a bit so won't need this API.
We should remove the "Quick edit" link if the just the related configuration is overridden. We should just add extra configuration fields. The is way they will be like all other blocks that don't add the extra configuration. So we can handle that on each 'settings_tray' plugin form. This patch will do that. So I am removing the new interface.
So now #39 patch review.
1. thats better. fixed
2. why not check down to the nested level we are actually going to need?
3. No longer applies because interface removed.
4. No longer applies because interface removed.
#40
Isn't
$this->configFactory->getEditable('system.site')low-level config. Or maybe I don't know the right terms.Comment #48
tedbowforgot to remove these use statements.
Comment #49
tedbowThis patch is fixing use statements as per #48
#46
Yes before #30 we were showing the Settings Tray dialog when you clicked "Quick edit" but then giving you only:
The message "This block cannot be edited in the Settings Tray form because it has configuration overrides in effect."
The link to edit the block.
It was not handling the related configuration, either site name/slogan or menu, being overridden. This is what @xjm had suggested as to be before stable.
Comment #50
wim leersMakes sense!
Patch in #47 looks good. A few nits, and a bunch of remarks that could make the patch much simpler:
No longer checks related configuration. Name needs to be updated. Basically, a revert of #34's interdiff in this hunk.
Wrapping this in an if-test and all the indentation changes make this patch much scarier looking than it is.
I think it could be far simpler, by using
#access:If access is not allowed, this subtree of the form is never rendered, and Form API prevents submitting values that don't exist in the rendered form. So, this is unnecessary.
Therefore, only keeping the
$form_state->hasValue()is sufficient.In fact, you could make this the if-test!
… which also means this is called in only one place, so you can inline it in point 2.
Same story here.
Nit: extraneous
\n.Nit: missing space.
Comment #51
tedbow@Wim Leers thanks for review!
1. fixed
2. fixing along with 5)
3-4 fixed
6. fixed
7. fixed
8. fixed
Yay a smaller patch!
Comment #52
tedbowAt least I am making new mistakes and not the same ones. 😅
Uploading with .patch extension
Comment #53
tedbowOk this is an old mistake, leaving in code that I was using to debug 😁
Comment #54
tedbowAnd so it goes...
Comment #55
wim leers👍
Comment #56
berdirI see this is still used twice but a single line now. With my issue referenced before, it will simply become $block->hasOverrides(), maybe inline with a @todo on that issue?
getValue() doesn't really make sense, those values will always there, they will simply have the default value that was set on the form, so this it will never be FALSE.
Same here, these conditions for validate and save are not really doing anything.
Also, it's not changed her, but setPlugin() must use loadOverrideFree(). Otherwise this will right now save overridden values now and will throw an exception after my issue is in.
Comment #57
berdirSo confirming this doesn't show up is not enough to cover what I wrote, you need to save the form and make sure you did not save anything you shouldn't have.
And for that reason, as I wrote earlier, I still think this should be classified as a bug, not a task.
Comment #58
tedbow@Berdir thanks for review.
#56
AccessResult::allowedIf(!$site_config_immutable->hasOverrides('name') && !$site_config_immutable->hasOverrides('slogan'))->isAllowed()hasMenuOverrides()and checking that insubmitConfigurationForm()andvalidateConfigurationForm(). Then changing the form element to use'#access' => AccessResult::allowedIf(!$this->hasMenuOverrides()),I am changing here but would it actually make a difference? because with these updates it would never save the menu unless
hasOverrides()is false.#57
Added test coverage for saving the form when the elements have been removed. Changing to "bug"
Comment #59
wim leers#56.2 + 3: So my advice/analysis in #50.3 was wrong? If so, then I'm unaware of that change in Form API — it definitely used to be the case that you could rely on the values in form state. My bad for putting @tedbow on the wrong path then — sorry Ted! :(
That's IMHO out of scope
Good point!
Addresses #57, nice!
Details can be refined further later, what matters now is to get this in before alpha. @Berdir's feedback was addressed, moving back to RTBC.
Comment #60
tedbow@Wim Leers thanks for the RTBC but I found some more problems
I got these if statements wrong. They should both be
if (!$this->hasMenuOverrides()) {If there are no overrides then we can validate and save the menu.
So why weren't the tests failing. I checked manually and what happens in this situation is that the menu links just get disabled.
@webchick this is what was causing the menu blocks to disappear when we were testing but still show on the block page. The block was there but since no links where enabled the block didn't render.
So I put a test in to make sure the block and the link still renders.
I hope it passes. It is randomly failing for me locally. Literally no code changes and pass then fail. I suspect our good friend phantomjs.
I split this up into 2 methods.
testOverriddenBlock()which tests for when the block itself is overridden. I noticed we were only testing when the block was overridden before it was rendered the first time.And it testing it seems that if rendered a block then added a language override then it would not always have it "Quick edit" link remove but it would have the
data-drupal-settingstrayattribute removed.So I updated the new
testOverriddenBlock()to display the block once and then do the override. This currently will fail 😒Comment #62
tedbowYay for predicting failure 😛
Ok. So no longer think it is possible to remove contextual links via the server-side contextual links relate hooks.
They don't fire every time is edit or when new override is added.
So I am now adding and attribute
data-settings-tray-overriddeninsettings_tray_preprocess_block().Then JS
prepareAjaxLinks()I simply check the ajax element, which are already looping through is within a block with the attribute and if so remove the element. Don't worry @Wim Leers this Javascript is nothing like the scary JS I originally had in this patch 😜Fingers crossed all tests should pass.
Comment #63
berdir@Wim: Yep #50.3 was wrong and has been wrong as far as I can remember. #access just makes form elements invisible, they're still there and processed by the form system, which means it simply falls back to the default value. It actually explicitly checks access, to not allow submitting a value for those. That's by design, otherwise the very common process of hiding some form elements in some cases with #access (often in form alter hooks) would result in suddenly losing/saving non-existing values.
I think it makes sense to do the loadOverrideFree() change here (thanks for doing that already), it just seems wrong to build the edit fom with overriden values even if we're then not actually going to save it. You never now what people are going to do with that form...
Comment #64
wim leersSee, this is what I remembered.
Comment #65
berdirApparently #63 wasn't clear enough. The parts of the patch that I can comment on now look fine to me.
The server-side part should in theory work if the override does include the relevant cache contexts. Since you use a custom config override, you might have to clear caches there through the test. But I'll leave that part for Wim.
Comment #67
tedbow@Berdir thanks, I am sure it was clear enough for everyone but me 😜
This patch just adds a comment to
settings_tray_preprocess_block()explaining what thedata-settings-tray-overriddenis and why we aren't using the contextual hooks.Comment #68
GrandmaGlassesRopeManCouple of quick questions,
disabledalways only ever be one element?Comment #69
tedbow@drpal thanks for the review! 👊
yes, see https://api.jquery.com/closest/
Since it gets the ancestors in the DOM tree that matches it will always only get 1(or none). The only way it could get more is
instance.elementreferenced more that 1 element then there would be other problems. So should only react to 1.Fixed
Fixed
Comment #71
tedbowOk obviously I something up moving development against 8.6.x.
Rerolled #69
Also in process of the reroll I noticed
This new @param
$has_confirm_formis not needed anymore. It was added in an earlier version of this patch when we were showing the overridden notice in the Settings Tray and so not all instances had an actual form.Removing
Comment #72
GrandmaGlassesRopeMan@tedbow Great. Thanks. Looks good to me.
Comment #73
wim leersWhy is this a problem now? Why wasn't it a problem earlier?
This makes me ask a larger question: Shouldn't we then ensure Contextual Links are refreshed when necessary? If we can pass up-to-date information to some JS, we can also ensure that Contextual Links are re-rendered when necessary. This is similar to #2773591: New contextual links are not available after a module is installed.
The tricky bit here is that Contextual Links were originally designed for use cases where links very much remained the same; which links were visible mostly depended on the permissions you had as a user. permissions are one axis to vary by. But now each individual link may need to be become visible or invisible based on changes not in configuration, but in configuration overrides. And configuration overrides do not have detailed dependency information. They do have cacheability metadata. But they don't notify of individual overrides being added, removed or edited. So … sigh … yes, I think the interdiff #62 is probably appropriate. But please create a new issue in https://www.drupal.org/project/issues/drupal?version=8.x&component=conte... to document this need, and add a
@todopointing to that new issue in bothsettings_tray.es6.jsandsettings_tray_preprocess_block()to remove the work-arounds in Settings Tray when that issue gets fixed.Once again: lack of full dependency information is the source of bugs :( :(
Patch review:
s/the need for this/this/
Nit: s/Javascript/JavaScript/
s/It not/It is not/
s/because they […] overrides/because they are only called when the client-side cache of contextual links is empty, invalidated, or a new variation is detected, and it currently is not possible to reliably detect configuration override changes, and therefore it is not possible to invalidate the client-side cache of contextual links when needed./
@todos requested earlier in the comment.Comment #74
tedbow#73
Yes this was probably always problem we just didn't test coverage that caught it.
\Drupal\Tests\settings_tray\FunctionalJavascript\SettingsTrayBlockFormTest::testOverriddenBlocknow tests for this.I created #2937467: Access to certain contextual links may vary not by permissions, but by config overrides: add "config overrides" client-side cache tag.
Currently I have a failing test there that proves that the contextual links are not alterable after you save a block with updated data.
The review
1. fixed
2. fixing but just to be clear this a existing line that just was surrounded in a new if statement
3. fixed
4. added todo's
Comment #75
wim leersI updated #2937467: Access to certain contextual links may vary not by permissions, but by config overrides: add "config overrides" client-side cache tag based on what I wrote in #73, but with more detail.
Having written that, I realized that actually this patch is getting something wrong: contextual links are automatically omitted if the user does not have access to the route (see my update to #2937467 if you want more nuance). So the behavior we're adding here is to effectively make the
entity.block.off_canvas_formroute return a 403 if the block has config overrides.But we're implementing it at the level of "contextual links have been rendered, now let's forcibly remove it in JS based on some metadata the server sends". The correct way to fix it would be to add a requirement to the
entity.block.off_canvas_formroute that forbids access if the block does have overrides.So:
turn this into an access check and use it for the
entity.block.off_canvas_formroute. And add test coverage that if you go to that URL directly (without using Settings Tray), you also get a 403. (This is a gap in the current test coverage.)That would then solve the problem in a way that is A) more robust, B) future-proof: it will allow us to remove all hacks/work-arounds once #2937467: Access to certain contextual links may vary not by permissions, but by config overrides: add "config overrides" client-side cache tag lands.
We'll still need a work-around. But because it's now solved in a forward-compatible way, we can fix the problem in a different way: rather than relying on correct client-side cache invalidation that we don't yet have (that's the task of #2937467), we can fix the problem by … simply not ever client-side caching contextual links for blocks that have a Settings Tray link. We can find those in
window.sessionStoragewith IDs like:So, our work-around can simply be that whenever
drupalContextualLinkAddedis called, we delete all entries inwindow.sessionStoragewhose key starts withDrupal.contextual.blockand containssettings_tray:.Comment #76
wim leersThanks to #2937467-8: Access to certain contextual links may vary not by permissions, but by config overrides: add "config overrides" client-side cache tag, I saw a much simpler solution, which still relies on that access check I mentioned in #75. I simply forgot about the
metadatakey in Contextual Links metadata.(And yes, the docs for Contextual Links are terrible. That's at least partially my fault. With better docs, we'd have arrived at this solution before comment 20, instead of after comment 70. 😞)
Really curious to hear what you think about this, @tedbow!
Comment #78
tedbow@Wim Leers this great!
So from my understanding from your description and looking the contextual module code is:
data-contextual-idfor example `block:block=bartik_search&langcode=en|settings_tray::langcode=en`This sounds like good approach and really working with the contextual module how it works now.
I am not sure we would need #2937467: Access to certain contextual links may vary not by permissions, but by config overrides: add "config overrides" client-side cache tag anymore this no longer feels like a workaround/hack.
This approach would take more client-side storage, though thinking still minimal and but have better client-side performance because it would be invalidating the client-side links less often.
If we added #2937467: Access to certain contextual links may vary not by permissions, but by config overrides: add "config overrides" client-side cache tag and were to add a `config_overrides_hash` that would invalidate contextual links every time and config override was changed, whether or not you had Settings Tray installed.
there will be couple test failures. working on patch
Comment #79
tedbowJust 2 problems that I see.
\settings_tray_preprocess_block()we still need to check!_settings_tray_has_block_overrides()or it will add the `settings-tray-editable` class and `data-drupal-settingstray` attribute to blocks that don't have a "Quick edit" link.settings_tray_block_view_alter()we need to checkisset($build['#contextual_links']['block'])before adding the has `has_overrides` metadata.This did not cause a test failure but I saw it locally when went to `admin/config/regional/language` in
\_contextual_links_to_id()because the help block didn't have any contextual link.Comment #80
wim leers#78:
Exactly!
Agreed.
Agreed.
#79:
settings_tray_preprocess_block()does need to be updated (expanded). NW for this.NW only for expanding the existing docs in
settings_tray_preprocess_block(). Then this is RTBC again.Comment #81
tedbow#80 I am glad we agree!
Here is the updated comment for
settings_tray_preprocess_block()that mentions overrides.I will close now #2937467: Access to certain contextual links may vary not by permissions, but by config overrides: add "config overrides" client-side cache tag as Works as Designed. Someone can reopen if it is ever needed
Comment #82
wim leersComment #83
tedbowJust a couple small things we missed in
\Drupal\settings_tray\Form\SystemMenuOffCanvasForm::hasMenuOverrides()Comment #84
wim leersOh, #83 just adds an injected service in favor of using
\Drupal::. Works for me! Didn't care about this one really, because there's a@todoto remove it anyway.Comment #85
webchickAdjusting credit.
Comment #88
xjmHooray!
Comment #89
webchickOk, excellent!
Ted walked me through this patch three times now as a more "functional" / demo review. We found problems the first two times, but this last time everything worked as expected!
What the patch does is apply the same pattern we have for the Page Title block to any other block with configuration overrides. In other words, it hides the dotted border and removes the "Quick Edit" contextual link, in lieu of showing the user an error in the sidebar directing them to configure the block without Quick Edit. Then, for blocks with "sub"-configuration (such as menu blocks, and the site name block), applies a #access = FALSE to those elements only, still allowing Quick Edit of the remaining elements.
Here's a screenshot showing the three patterns: the primary menu doesn't have a translation, and therefore still has the dotted outline visible. The site name does have a configuration override, but also has other settings provided by the block plugin, so the dotted outline is still visible. And the "local tasks" menu is not quick editable, so shows only the contextual link.
When you click into e.g. the site branding block in English (without overrides), you see the block form in full:
If you click into it in Spanish (with overrides), the site name/slogan fields are removed, but you can still see the remainder of the settings:
Menu blocks work the same way, showing/hiding the menu link entries themselves.
This seems to strike the right balance between addressing the initial concerns of the issue, while utilizing existing UX patterns from elsewhere. (e.g. we don't let you click on a menu link that we know will lead to a 403.)
We also did a code walkthrough. The patch is much more simplified than earlier in this issue—great work, everyone! The addition of the context variant to work around local storage invalidation problems is great. Test coverage is quite extensive too and covers all of the edge cases Ted and I hit in testing.
THEREFORE!
Committed and pushed to 8.6.x, and since Settings Tray is still an experimental module (though hopefully not for too much longer :)), backported to 8.5.x as well.
Comment #90
tacituseu commentedLooks like it introduced test failures on PHP 5.5 & PostgreSQL 9.1:
https://www.drupal.org/pift-ci-job/864394
https://www.drupal.org/pift-ci-job/864395
Comment #91
tedbowThese are actually failing on Quick edit ajax callback. Currently Quick edit has no Functional Javascript test so there may be problem with testing on DrupalCI or something else going on. Hard to know because we have never tested it.
I created #2938308: Add QuickEdit Javascript tests
But first I think we should do #2938309: Only install Quick Edit when necessary for Settings Tray tests which I just created.
Quick edit was added to
$modulesbecause it is needed for\Drupal\Tests\settings_tray\FunctionalJavascript\SettingsTrayBlockFormTest::testQuickEditLinks()but it doesn't need to be on for the rest.It would not be bad idea to have it on for other tests to prove they work together but think that should not happen until after at least basic test for Quick edit in #2938309
UPDATED: I have uploaded a patch to #2938309: Only install Quick Edit when necessary for Settings Tray tests. I am testing the patch against all php/db combinations.