Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Problem/Motivation
ContextDefinitionInterface::getLabel
and ContextDefinitionInterface::getDescription
claims to return strings but after enabling the Rules module they return the @Translation annotation object which causes a fatal in twig. Notably the MockBlockManager does return new ContextDefinition($data_type, (string) $label, $required);
so there is evidence of non-string labels causing problems.
Proposed resolution
No idea.
Remaining tasks
Identify how the annotation got there.
User interface changes
API changes
Data model changes
Comment | File | Size | Author |
---|---|---|---|
#25 | interdiff.txt | 922 bytes | claudiu.cristea |
#25 | 2587179-25.patch | 1.57 KB | claudiu.cristea |
|
Comments
Comment #2
Torenware CreditAttribution: Torenware as a volunteer and commentedThis stuff is happening deep in the rendering system. Not clear where the Translation object is getting into the mix.
Comment #3
claudiu.cristea@Torenware, it seems that validateUtf8() wrongly receives a TranslatableMarkup object instead of a string. But I cannot reproduce the error. Can you give more details on how to reproduce the bug?
Comment #4
Torenware CreditAttribution: Torenware as a volunteer and commented@claudiu.cristea: I'm not sure how to come up with a simpler example. I've tried to create a simple render array that passes different things to a
#markup
item. t() doesn't cause the problem, and using theme object that loads twig hasn't done it (although it's a twig object that does it).On the system that displays this, the problem survives a cache rebuild.
I"m going to try a little more with the debugger, so I can see who introduces the TranslatableMarkup into the render process.
Comment #5
claudiu.cristeaAh, so it's not pure Drupal core but your module. Because I tested a lot to catch the error. So, it seems that is nothing wrong with Drupal core but maybe an error of implementation on your side?
Postponing this while we have more info :)
Comment #6
Torenware CreditAttribution: Torenware as a volunteer and commentedAfter some checking in the debugger, here's what I know:
Annotation from Note Type plugin (\Drupal\node\Plugin\Condition\NodeType):
Annotation from Data Comparison plugin (\Drupal\rules\Plugin\Condition\DataComparison):
Comment #7
Torenware CreditAttribution: Torenware as a volunteer and commentedThe bad conversion seems to occur at a different place than I thought earlier.
The problem occurs in
core/themes/classy/templates/form/form-element.html.twig
while Twig tries to escape the following string:
description = @Translation("The data to be checked to be empty, specified by using a data selector, e.g. 'node:uid:entity:name:value'."))
The exception is thrown in core/lib/Drupal/Core/Template/TwigExtension.php at line 441;
The routine wants to convert any object it's handed as an object via either ::__toString() or ::toString(). The translation object has neither, and the method throws.
I'm not sure why TranslatableMarkup has no toString() method. For all I know, it might be by design.
This is starting to look like a Twig rendering bug. There's nothing wrong with how the Rules Condition plugin is declared AFAICT, but it would appear that either TranslatableMarkup needs string conversion method, or that Twig needs to have code to explicitly handle that case.
Comment #8
Torenware CreditAttribution: Torenware as a volunteer and commentedComment #9
Torenware CreditAttribution: Torenware as a volunteer and commentedStill odder: class TranslatableMarkup has a ::__toString() method via
trait ToStringTrait
. In a stand-alone drush script, this is discovered, but it is not discovered when this is tested in TwigExtension.php.Not sure why this could be, but it's possible that we need the extra toString() method as well.
Comment #10
Torenware CreditAttribution: Torenware as a volunteer and commentedOK, I'm not sure if I have the right fix, but I have a fix.
This is incorrect:
Close, but in reality, the routine was passed a Translation annotation, and not TranslatableMarkup.
This is an example of a translatable object that is not TranslatableMarkup. It wraps a TranslatableMarkup, but is not one itself.
I enclose a patch that deals with the Annotation case. I'm not sure if I should be handling a Translation annotation or AnnotationInterface. For now, I'm doing the latter.
Not sure what to do here for a test.
Comment #11
claudiu.cristeaNo, sorry. This patch is hiding the cause of the bug. An Annotation object should be not passed ever in $arg. You need to check the caller. Why the caller is passing that object? That is a bug. But I don't think is the core doing this but other modules that you've installed or your custom module.
Comment #12
Torenware CreditAttribution: Torenware as a volunteer and commented@claudiu.cristea:
That would be the Block Module. Sorry ;-)
The calling code is in
BlockForm::buildVisibilityInterface()
, which calls into Core's condition system as so:by this point, the Translation annotation objects are already in the render stream. You will find that no contrib code is called anywhere in that chain. I've been trying to figure out how exactly the process works, but so far I haven't found it.
You can argue whether this is a block bug. But it's certainly a core bug. And the UI problem will appear as a Block UI problem.
Comment #13
claudiu.cristeaOK. Please provide the exact steps to reproduce the error. But I suggest you to test this on fresh Drupal 8.0.x-dev installation with no module installed, other than those from the current profile.
The steps from the issue summary didn't reveal any error for me.
Comment #14
chx CreditAttribution: chx commentedComment #15
claudiu.cristeaSo, this seems more like a Rules block sending wrong arguments to block system?
Comment #16
Torenware CreditAttribution: Torenware as a volunteer and commented@claudiu.cristea -- not exactly. I did find a Rules bug, but core was suppressing Translation objects, which is part of why you didn't see the crash on ajax.
The Rules issue had to do with matching criteria between contexts. I'll log it a bit later.
Core was effectively ignoring both label and description strings (which are always Translate annotations). The offending code looks like this:
Drupal\Core\Annotation\ContextDefinition::__construct(), from line 112:
In practice, label and description are never actually TranslatableMarkup, so this code was never used. They are always set to NULL right now.
I'd recommend doing two things here:
I've already done a pass on this.
Also, @chx had asked me to see if I could come up with some test code. That I also have. The bug in Rules exposed the
count($options) > 1
block inContextAwarePluginAssignmentTrait
. This was hidden because Core does not have enough context providers to ever get $options with more than 1 item.The test code is a small module that implements a simple context provider, and a plugin that actually uses label and description. This should be simple enough to make the problem understandable.
Comment #17
Torenware CreditAttribution: Torenware as a volunteer and commentedSome simple test code per @chx: https://github.com/torenware/test_contexts.
This does two things:
Just turn the sucker on, and look at how the different context tabs in Blocks render differently.
Comment #18
Torenware CreditAttribution: Torenware as a volunteer and commentedAdded a patch to allow description and label Translation annotations to actually be used in the UI. YMMV :-)
Comment #19
klausiRules uses its own ContextDefinition annotation class. Not sure what is going on here, maybe we did not swap out the core ContextDefinition correctly?
Can we reproduce this somehow in core alone and add that to the issue summary?
Comment #20
Torenware CreditAttribution: Torenware as a volunteer and commented@klausi -- there's a subtle difference between the Core class and the Rules substitute.
The Rules class was allowing Translate objects out, which is why we saw the crash with Rules installed, but not with Core alone.
The Core class attempts to prevent the crash by looking for the label and description keys, but the work-around is not correct: it assumes the items are TranslatableMarkup, which they are not. Core should accept the patch in #18 to get correct behavior. The current behavior is wrong because it strips away strings that are actually needed to display the UI right.
If Core wants to pass on the patch, we can do the same thing in our Rules substitute class. If we correctly turn the Translation objects into strings, then at least we will not see a problem when Rules is installed. I can create a patch for Rules to do this if you like.
Comment #21
claudiu.cristeaAs I said this is a Rules issue.
EDIT: I've implemented the workaround copied from
\Drupal\Core\Annotation\ContextDefinition::__construct()
and that should be removed when #2362727: Implement __toString() on Translation Annotation lands.Comment #22
claudiu.cristeaForgot the use statement.
Comment #23
claudiu.cristeaAnd I guess this is critical while it breaks the whole Block placement functionality.
Comment #25
claudiu.cristeaOK. Fixed the failure on tests except
\Drupal\Tests\rules\Kernel\ContextIntegrationTest
. That one makes the HEAD broken and does not belong to this patch. See the branch test broken https://www.drupal.org/pift-ci-job/56053.Comment #27
claudiu.cristeaThe only failure is the branch test failure. This patch is ready for review.
Comment #28
chx CreditAttribution: chx commentedYes this is a bad bug but it's no way critical: there's no data loss or security hole and the error is not even reproducible with core AFAICS.
Comment #29
chx CreditAttribution: chx commentedOh sorry I didn't realize this was moved into Rules. Nevermind me :)
Comment #30
klausiPlease file a pull request instead, see the Rules dev workflow: https://thefubhy.gitbooks.io/rules/content/contributing/start_developing...
Comment #31
claudiu.cristeaThat is a 404 link.
Comment #32
klausiWorks for me, did you click on a sliced off link in your email inbox?
Comment #33
claudiu.cristeahttps://github.com/fago/rules/pull/288
Comment #34
claudiu.cristeaComment #35
klausiCool, we should have some kind of test case for this. #2610458: Prevent Translation annotation objects from getting into the render system has a comment that shows a possibility where we assert that we are actually dealing with strings. So I think this is a duplicate of #2610458: Prevent Translation annotation objects from getting into the render system, correct?
Comment #36
klausiI think this is now a duplicate of #2610458: Prevent Translation annotation objects from getting into the render system, the block placing UI works for me.