Problem/Motivation
This is a Views-specific problem in HEAD. But it's a problem that any controller that is using render caching for the entire render array it returns can experience. Because:
class Controller {
public function something() {
return [
'#cache' => ['keys' => […], …],
'#title' => 'Llamas are awesome',
'children' => […],
];
}
}
means that on a cache hit, #title
will not be set.
Steps to reproduce
- Install standard, login and visit front page
- You see the page title "Welcome to site"
- Reload the page
Expected: You see the same title again.
Actual: No title.
Proposed resolution
When a controller returns a render array, and it has #cache[keys]
set to enable render caching (i.e. the entire render array returned by the controller is render cacheable), then we need to make sure that it doesn't lose #title
. Because we allow #title
to be set on the page-level render array (remember $page['#title']
in D7), and render caching only caches #markup
, #cache
and #attached
.
Remaining tasks
None.
User interface changes
None.
API changes
None.
Comment | File | Size | Author |
---|---|---|---|
#45 | 2505989.45.patch | 16.53 KB | alexpott |
#45 | 44-45-interdiff.txt | 9.86 KB | alexpott |
#44 | 2505989.43.patch | 18.76 KB | alexpott |
#44 | 35-43-interdiff.txt | 4.52 KB | alexpott |
#35 | 2505989.35.patch | 16.06 KB | alexpott |
Comments
Comment #1
olli CreditAttribution: olli commentedChanging cache setting to 'none' solves the problem...
Comment #2
dawehnerOh I see, we probably need to add #title to
#cache_properties
by default on controller resuts?Comment #3
catchThis is quite disconcerting and might be a general issue (although entity titles are OK, but we do special things there anyway). Bumping to major.
Comment #4
olli CreditAttribution: olli commentedTaxonomy term pages have the same problem. Feed looks ok. For blocks these area and argument handler title overrides don't seem to work at all.
Here's a patch that adds '#title' to '#cache_properties' for page displays. Title does not disappear now but is double escaped on reload.
Comment #5
dawehnerI think the right fix is something like this.
+1 for merging test coverage together here ...
Comment #7
olli CreditAttribution: olli commentedOh I see, this is more general issue. For nodes, the title seems to work because after reload we get it from _title_callback. It does appear but quick edit is not working properly.
Comment #8
olli CreditAttribution: olli commentedWith #5, article title looks like this after reload:
Comment #9
dawehner@olli
I tried hard to reproduce that, but I could not. Are you sure this is now still a problem?
We have tests now.
Comment #10
xjmUh. Yeah I'd consider this critical. Even though it's confined to one feature, it's a pretty important one.
Comment #11
Wim LeersI'm pretty sure this is a Views-specific problem in HEAD. But it's a problem that any controller that is using render caching for the entire render array it returns can experience. Because:
means that on a cache hit,
#title
will not be set.Comment #12
BerdirI noticed this in page manager a while ago, moved to a nested element as a workaround.
Comment #14
xjmHmm, is there any way we could add a general test for the scenario described in #11 and/or somehow flag render arrays like that?
Also I think we really need to document #11 thoroughly somewhere.
Comment #15
Wim Leers#14: #9 is trying to fix it generically :)
Comment #16
dawehnerYeah just like #9 fixes it.
Working on a test.
Comment #17
chx CreditAttribution: chx commentedand a better title too. Thanks!
Comment #18
dawehnerMh, I'm not sure its worth to unit test
\Drupal\Core\Render\MainContent\HtmlRenderer
Regarding the general fix, I think its not possible. We want to get rid of pretty much everything beside #markup, so well, people need to use the
#cache_properties
api.Comment #19
Wim LeersComment #20
dawehnerThank you to alexpott for helping me here, well its still not fixed actually, mhh.
Comment #21
Wim LeersWell, I'd argue that the changes in
HtmlRenderer
are the generic fix.This is an unnecessary change.
What about something like
We could wrap this in
Comment #24
larowlangiddyup
Comment #25
larowlanSo the failing test is because the title retrieved from cache is sanitized again.
Working on it
Comment #26
dawehnerThis is a alex thing atm.
Comment #27
alexpottSo it turns out we need to know which # properties are safe when we render cache them.
Comment #28
Fabianx CreditAttribution: Fabianx as a volunteer commentedThe problem is that the #cache_properties API only works with children to mark them as Safe again.
There is two cases here:
a) #title is already at top-level, in which case we could check if its safe and wrap in a SafeString instead.
b) #title is bubbled up dynamically, which is really hard.
( and #post_render does not work as call_user_func does not pass $elements by reference ), which in strict mode could be considered a regression compared to Drupal 7 ...
So not many options.
Probably the best is to do it like we do it with drupal_set_message() and check SafeMarkup::isSafe() for each string property and then either store as array with [ safe => TRUE ] case and unwrap array on retrieval or wrap directly in a SafeString object.
Comment #29
Wim LeersAsked plach & Fabianx to review the changes in
RenderCache
, since they brought that to core.Comment #30
Fabianx CreditAttribution: Fabianx as a volunteer commentedLove it, lets clean this internal property up by unsetting it here.
RTBC once that change is made from me.
Comment #32
Wim LeersSee #21.2 & 3, I think that'd help make this significantly more understandable/less mysterious.
Shouldn't this also require updates to the test coverage in
\Drupal\Tests\Core\Render\RendererTest::testRenderCacheProperties()
?Nit: should have a
\n
in between.After that, RTBC from me.
Comment #33
alexpottFixed #30 and #32.
Fingers-crossed for green-ness.
Comment #35
alexpottFixing the unit tests...
Comment #37
alexpottThe fail in #35 was because this got a PHP5.5 bot without APCu.
Comment #40
Wim LeersIS updated.
RTBC.
Comment #41
dawehnerLooks great from my point of view!
Comment #42
chx CreditAttribution: chx commentedDoxygen? RendererInterface::render mentions #cache_properties so I presume we would want to put #safe_cache_properties there as well.
Comment #43
Wim LeersGood point.
Comment #44
alexpottI’m concerned about the implementation.
At the moment if you cached the following render array
With the
#custom_property
it will break. Let’s make this more robust.Also added docs to RenderCacheInterface - this doesn't belong along side
#cache_properties
inRendererInterface::render
because it is never return from render as it is unset there. Perhaps we should move all of this SafeMarkup logic inside RenderCache::get()?Comment #45
alexpottYep we can deal with #safe_cache_properties internally in RenderCache.. I think the solution attached is much neater as #safe_cache_properties is never exposed to the render system at large.
Comment #46
Wim LeersThis is so much more elegant. Excellent :)
Nit: I think it'd be clearer if these were named,
#custom_property_safe
and#custom_property_unsafe
, respectively.Though this makes it sufficiently clear. Consider the previous point a soft suggestion :)
Comment #47
Fabianx CreditAttribution: Fabianx as a volunteer commentedDo we need to expose this here?
Should we not check for is_scalar() instead?
Comment #48
chx CreditAttribution: chx commentedI still do not see #safe_cache_properties documented...
Comment #49
alexpott@chx I don't think it needs to be. It is set in RenderCache::set() and removed in RenderCache::get() - and also will be removed when we do #2506581: Remove SafeMarkup::set() from Renderer::doRender. It is never exposed in the render system.
Comment #50
olli CreditAttribution: olli commentedManually tested the patch and it seems to fix the title for views (tested front page and term page). It also fixes quick editing of node titles on cache hit!
Why was this changed? With the patch applied, this method is not called anymore on cache hit.
The assertTitle() in the second case should see
<span>
because that is escaped in '#title', right?Comment #51
olli CreditAttribution: olli commentedShould we also add a case where title contains some html that is not escaped? Somethink like "Cached title with some html"
Comment #52
dawehnerTo be clear, this is exactly what auto escaping is doing for you. There is no longer a need to do that manually.
Comment #53
alexpottre #51
- well this is not possible with a Node - since it is alway checkPlain'd (before and after this patch) but I do think it is worth discussing was is and what is not allowed in a title. Just not in this issue.
re #50.2 - nope spans are removed for the title... See template_process_html
And now we're not checkPlain'ing this NodeViewController::title() this actually works as desired.
Comment #54
dawehnerLet's do that in a separate issue: #2530474: Discuss whether | which html tags to allow in entity labels
I think with the response of alex, the feedback from olli got adressed
Comment #55
catchCommitted/pushed to 8.0.x, thanks!
Comment #57
olli CreditAttribution: olli commentedThanks @dawehner and @alexpott for the replies and fixing this!
Found the problem in #50.2 and filed #2530908: Caching problem in PageTitleTest.
Comment #58
Fabianx CreditAttribution: Fabianx as a volunteer commentedHmm, #47 was not addressed :/.
So asking post-commit #47 again:
1. Why do we expose #safe_cache_properties in getCacheableRenderArray()? It should be an internal implementation detail only ...
2. Why not use is_scalar() instead of the is_array check? What happens with objects?
Both could be fixed in a non-critical follow-up, but would really love to have those answered.
Comment #59
Wim LeersDarn! I completely overlooked that :(
Let's open a major follow-up to fix the things we didn't get right here. Sorry for having missed that, Fabianx! :(
Comment #60
alexpottRe #58/#59 this will be addressed in #2506581: Remove SafeMarkup::set() from Renderer::doRender