On a website we enabled recently workflows and content moderation modules. At first we thought it was working great but we discovered that after editing a page it was impossible to edit the translation. This is the error shown in the log:
InvalidArgumentException: The state '' does not exist in workflow. in Drupal\workflows\Plugin\WorkflowTypeBase->getState() (line 155 of /home/dm166/www/core/modules/workflows/src/Plugin/WorkflowTypeBase.php).
I've found a relatively similar issue but even after updating to latest version and also on a fresh install from simplytest.me the bug is still present. Here is the similar issue: https://www.drupal.org/project/drupal/issues/2915398
Here are the steps to reproduce the issue:
- Enable i18n modules (content translation, interface translation, language)
- Add a language in admin/config/regional/language
- Add the language switcher block in admin/structure/block
- Enable page translation in admin/config/regional/content-language
- Create a page in English and translate it
- Enable the workflows and content moderation modules
- Enable editorial workflow for “basic page” content type in admin/config/workflow/workflows/manage/editorial
- Now you can edit the previous page in English, but after that if you try to edit the translated one you will have the error (preventing the edit page to be displayed)
I've made the priority major but I'm hesitating with critical since content in translated language can't be edited. The only "workaround" I've found is editing the code temporarily so that instead of throwing an error it set the state to "published".
| Comment | File | Size | Author |
|---|---|---|---|
| #51 | 3000573-8-7-51.patch | 12.01 KB | sam152 |
| #51 | 3000573-8-6-51.patch | 12.07 KB | sam152 |
| #43 | 3000573-43-8.6.x-backport.patch | 13.28 KB | sam152 |
| #43 | 3000573-43.patch | 12.01 KB | sam152 |
| #43 | 3000573-TEST-ONLY-43.patch | 11.03 KB | sam152 |
Comments
Comment #2
idflood commentedComment #3
idflood commentedThe issue seems to be in content_moderation/src/EntityTypeInfo.php > formAlter(). At the end of the function this line is causing the issue:
So this is to display the revision status in the sidebar (the top label). At this point the $entity->moderation_state->value is NULL.
Comment #4
idflood commentedHere is the first patch which fixes the issue. Since the widget was working I adapted the code from ModerationStateWidget->formElement.
I'm wondering if a better fix would be to instead change ModerationStateFieldItemList to always return a valid state. But there are probably cases where having an empty moderation_state is required so another solution would be to have a new method on it like "getValueOrInitialState".
Comment #5
timmillwoodI think it might be better to work out why
$entity->moderation_state->valueis returningNULL, it should always return something.Also, we'll need tests.
Comment #6
idflood commentedThanks for the feedback. I didn't find the solution yet but it will eventually be something in ModerationStateFieldItemList I think.
When the result is empty, the getModerationStateId() method does the following condition / return:
And just below we do return the default initial state if there is one. So one solution would be to change the return in this condition to return something like "$content_moderation_state->moderation_state->value || $default_state". It could work but I don't think it tackles the real root cause.
Instead the problem seems to be in loadContentModerationStateRevision(). Here it does the following and return then $content_moderation:
This brings us back to the related issue https://www.drupal.org/project/drupal/issues/2915398
If I try to do "$content_moderation_state->toArray()" before the addTranslation I find the correct moderation_state. But when inspecting the value of $moderation_state after the 2 conditions it returns an empty moderation_state. So even though the ->toArray() seems to be fixed the translation doesn't seem to be completely fixed.
Comment #7
ious commentedHi, I did enable content moderation on a multilingual website and I got the same issue on pre-existing content.
If I create some content after enabling content moderation, I do not have any issue.
If I want to enable content moderation for multiple content type with existing content, I need to remove the node translation, and re-translate it to avoid having this issue on existing content.
Cheers.
Comment #8
berdirCan confirm this when enabling content_moderation on an entity type with existing translations, you can edit and save one of those translations and then it fails with this exceptions when trying to edit another.
Should be possible to write a test for that, I'll see what I can do.
Comment #9
johnchqueLet's add some tests. :)
Comment #11
berdirleft-over comment, we actually don't do that here in this test.
Comment #12
johnchqueTrue, updating some other comment too. :)
Comment #14
sam152 commentedI wonder if we have a bug in
ModerationStateFieldItemListhere, from memory the computed field should call out togetInitialStateinternally. It's probably worth stepping into that computed part just to validate there isn't something obvious that can be fixed at that level.Comment #15
berdirDefinitely, we do need a better fix.
Started looking into it, in \Drupal\content_moderation\Plugin\Field\ModerationStateFieldItemList::getModerationStateId() , the call to loadContentModerationStateRevision() does return a $content_moderation_state but its state value is NULL.
The problem there is the call inside to addTranslation(), the existing values must be provided to that or it's initialized with empty fields. Like this:
$content_moderation_state->addTranslation($langcode, $content_moderation_state->toArray());
That should fix it.
Comment #16
scott_euser commentedYep, Berdir your comment in #15 solves the issue for me. Converted it to a patch + interdiff attached,
Comment #18
scott_euser commentedIt's the final line here in an existing test that is failing:
core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php
Comment #19
berdirThat test is a bit strange, the revision ids seem to be off by one. It says that the first french revision is *also* revision one, but I think by fixing this bug, we actually force a new revision already on that save (line 300) and that's correct because currently, content_moderation always forces a new revision. This happens in \Drupal\content_moderation\EntityOperations::entityPresave which checks for having a moderation state. that wasn't true by default but is now. I actually think that condition there was bogus and might have been hiding this bug, as pointed out in this issue, there should always be a value.
If you increase all the revision ids by one, the test should pass. Right now, it's still loading and comparing the previous revision, surprised that it tokes so many resaves to actually have a fail :)
Comment #20
dawehnerI had the same kind of issue in #3006078: Content forms with translation throw error when content_moderation is added after the entity exists already even with a really similar patch. This one fixes the problem for me too.
Comment #21
sam152 commentedNice sleuthing, in addition to the fix it would be great to have some additional test cases in
ModerationStateFieldItemListTest.Comment #22
johnchqueRight, having passing tests now.
What do we exactly want to test in ModerationStateFieldItemListTest?
Comment #24
scott_euser commentedNice spot with the revision IDs in that existing test.
So we have the new browser test in this patch that makes sure we don't have the fatal error.
For the kernal test Sam152 mentioned, perhaps we can test that states exist for all translations when enabling content moderation and running getModerationState(). So before running that we can assert false that there is no state existing for the translation (in particular conditions) then run getModerationState() and assert true that a state now exists for the translation (ie, prove that addTranslation() side effect of the method occurs).
That sound about right?
Comment #25
sam152 commentedSounds pretty good to me, but we should just use the public API and not worry about
getModerationState. Attached a kernel test which reproduces this.In the functional test, this is the section of code which causes the fatal error:
So in order to test the buggy part of
ModerationStateFieldItemListwe have to go through a long series of slow UI steps, which only happens to trigger our problematic code path because we're attempting to show the label of the current state in the sidebar. Problems with this approach are:The caveat is however that we won't have full end to end functional integration tests which cover this bug. In my opinion that's an acceptable tradeoff given the translation functionality in general does have this kind of coverage, so my preference would be to include only the kernel test with this patch.
Should we add a follow up to evaluate this condition and remove it?
Comment #27
sam152 commentedComment #28
sam152 commentedComment #29
berdirthis actually has the same "bug" now like the fix we're doing, should include $node->toArray() as second argument for consistency. Might not directly affect the test, but it's the correct thing to do.
Great to have the kernel test coverage but I think it would be useful to keep the browser test too. Even if we do change something in the future that would no longer trigger this specific problem, it makes sense to have end-to-end test coverage of it IMHO.
Comment #30
sam152 commentedGood catch!
Happy to defer to your judgement on the need for an end to end test. Uploading everything combined so it's reviewable.
Comment #31
amateescu commentedI also don't think it hurts to have the functional test coverage, showing the moderation state in Seven's sidebar is an important feature for site editors so it makes sense to have it working and tested in various scenarios :)
I only have a minor request for the patch itself:
We shouldn't use
t()in tests unless we're actually testing the translation system :)Comment #32
scott_euser commentedUpdated the test without the use of
t(). Re-ran the two test classes and they both correctly fail without core/modules/content_moderation/src/Plugin/Field/ModerationStateFieldItemList.php and both correctly pass with.Comment #33
sam152 commentedNice, looks good to me.
Comment #34
amateescu commentedLGTM as well :)
Comment #35
nashkrammer commentedPatch on #32 tested on core 8.5.6, works well to resolve mentioned issue. I had the same issue after applying the workflow to article content. Thank You.
Comment #36
larowlanAdding issue credits
Comment #38
larowlanCommitted 666388d and pushed to 8.7.x. Thanks!
Does not apply to 8.6.x
both modified: core/modules/content_moderation/tests/src/Kernel/ModerationStateFieldItemListTest.phpComment #39
sam152 commentedHm, I think this broke HEAD. Seems to be failing for me.
Comment #40
alexpottYep this broke HEAD
And reverting fixes it.
Comment #42
berdirWeird, requested a retest, This is the test we added here and it used to pass?
Comment #43
sam152 commentedIn #2943899: Moderation state field cannot be updated via REST, because special handling in ModerationStateFieldItemList we made "null" a valid value for the
moderation_statefield, so we broke our test case. Just need to reload the node after enabling moderation and it should be good again.Attaching the interdiff, test only patch, the patch and the backport.
Comment #45
sam152 commentedComment #51
sam152 commentedFixing the backport and reuploading the 8.7 patch for clarity.
Comment #52
sam152 commentedOkay, phew! Both branches are green.
Comment #53
alexpottSecond time lucky.
Committed 925b4b5 and pushed to 8.7.x. Thanks!
Committed 46ad14aa7c and pushed to 8.6.x. Thanks!
Comment #57
jigariusStrangely, when I apply this patch to Drupal 8.6.3 using composer.json, I get a new directory named "b/core/..." and "core/core/...". Has anyone else seen a problem like that? I have other core patches which get applied correctly. Only this one creates these 2 weird directories.
I'm talking about this patch: https://www.drupal.org/files/issues/2018-11-12/3000573-8-6-51_0.patch
Comment #58
alison@jigarius OMG that has been happening to me with a few patches once in a while -- also...
...this one: #2856967-62: Allow admins to select a default entity moderation state
...and this one: #2797583-62: Dynamically provide action plugins for every moderation state change
>> So far, I've only experienced it with core patches -- but, it did *not* happen with this one: #2693675-22: fix system_get_info(), do not discard info about the current installation profile
It's so freaking weird!!!!!!! But I figure it's me somehow, not Drupal -- but also, I have no clue how to troubleshoot.
Others on the thread: I know it's out of scope, so I understand if you shoot me down, buuuuuttttt have you seen this before, and/or do you know why/when/how a patch will have this effect or not?
Comment #59
junaidpvI am getting this error on a site migrated from D7. Site was having both Workbench Moderation and core Content Moderation modules installed. Uninstalling the Workbench Moderation module fixed the issue.
Comment #60
jsutta commented@junaidpv I'm running the same type of migration as you and your solution worked! Thank you so much for posting it!