Problem/Motivation
Currently 'draft' is the default moderation state for all new content. There are two kinds of defaults when it comes to content moderation:
- The default state in the select list, the one that appears as the selected option on the entity form.
- The default state of the entity, the state that is chosen as the starting point to validate which transitions an entity may use to undergo a state change and the value of
$entity->moderation_statebefore any state is assigned.
The default entity state will be the same as the default widget state as long as the logged in user has access to a transition that allows the default state to be selected. In effect, it's possible to configure a starting state that may never be returned to and will thus never show up in the select list. Under those circumstances, the selected state will revert to the first one in the list.
Proposed resolution
Allow admins to configure the starting entity state. The widget state will also default to this value, provided the user has access to an appropriate transition that allows it.
Remaining tasks
- Tests for setting the default.
- We should not allow deleting the default state.
User interface changes
Additional settings form on moderation workflows.
API changes
A configurable starting state.
Data model changes
Additional config schema value for the content moderation workflow type.
| Comment | File | Size | Author |
|---|---|---|---|
| #62 | 2856967-54-drupal-8-6-x-reroll.patch | 12.76 KB | gnuget |
| #54 | 2856967-54.patch | 12.98 KB | alexpott |
| #54 | 52-54-interdiff.txt | 1.17 KB | alexpott |
| #52 | 2856967-52.patch | 13.05 KB | sam152 |
| #52 | interdiff.txt | 1.17 KB | sam152 |
Comments
Comment #2
timmillwoodThe initial state for new content comes from \Drupal\workflows\Entity\Workflow::getInitialState, which is simply the first item in the array of states.
Currently editing an entity the default state is the first valid transition, but this may change in #2753717: Add select field to choose moderation state on entity forms to be current state.
Maybe in the workflow we should have a way to set the initial state.
Comment #3
amateescu commentedWouldn't this be solved by #2817835: When enabling moderation apply a relative state?
Comment #4
timmillwood@amateescu - No, this wouldn't solve the issue. #2817835: When enabling moderation apply a relative state is returning the "correct" state for already saved entities depending on if they're published or not, when they don't already have ContentModerationState entities.
What this issue is asking for is a way for "Published" to be the default state when creating a new entity, currently the default state is "draft". So we need a way within the Workflow edit form to set a default state.
Not sure how much of a priority this should be, guess we can wait for a patch or more demand for this.
Comment #5
nlisgo commentedCould this possibly be achieved by allow the moderation state to be overridden for each entity. This would solve my problem and potentially allow for different setups were the majority of content could adhere to a default moderation state but particular entities could override that default.
Comment #8
timmillwood@nlisgo - Do you still have a need for this?
Currently in 8.6.x the initial state for Content Moderation workflows is determined in
\Drupal\content_moderation\Plugin\WorkflowType\ContentModeration::getInitialState(). It uses "published" for published entities, otherwise "draft". For entity types which are not publishable we just get the first state in the list, ordered by weight, label, then key.Comment #9
bkosborneSee also #2927408: Default value not correctly set in the ModerationStateWidget., which has a patch to set the ModerationStateWidget (the form widget that's used on the main entity form for moderated entities) default value to the current state if available.
If a patch is developed for this issue, I suggest getting the mentioned issue in first, since they'll touch the same code.
Comment #10
berdirAlso struggling with a related issue.
The problem is that
It uses "published" for published entities, otherwise "draft"is not quite correct. It's actually published *and* not-new, which is exactly the problem here. It means that all new content is forced to being draft, despite being set to published.In our case, we have a distribution with a lot of default content (using default_content module) that is published. We're trying to start using content_moderation. All our default content is however changed to a draft because of the !isNew() check there. Why does that exist? What problem is that trying to solve?
With that, I either need to implement some kind of default_content hook/event to overrride or change all our default content to include the moderation state explicitly.
And it looks like the UI is actually not respecting that, at least not in 8.5 (didn't test with #2927408: Default value not correctly set in the ModerationStateWidget. for example), that simply picks the first item from the select which is then based on the states weight.
Comment #11
berdirSecond problem.. who says that my states are called published or draft? I can name them whatever I want?
Don't really understand what that whole snippet is trying to solve, didn't check the issue that added it :)
Comment #12
timmillwoodThe second problem is an easy one. There are two states content moderation enforces, published and draft. So we always know these exist.
It sounds like your use case @berdir is a valid issue. So we need to assume new entities are published. I wonder if this is an entity system issue rather than content moderation. Are new entities defaulted to unpublished, and the status checkbox defaults to checked?
Comment #13
berdirHm, didn't know about those being enforced, why is that? What if I want to have a different state be the default like the parent method supports simply by picking the first?
And if they are enforced, should we have constants for them or so?
The entity system doesn't really care about the (default value of the) status field, that's up to the configured default value of the field, e.g. node stores overridden base field definitions for each bundle to set the default value. so articles might be published by default while pages are not. Either way, you *can* rely on isPublished(), it shouldn't matter if an entity is new or not.
Comment #14
johnchqueAdding a patch where we remove the isNew check, want to see how many tests it might break.
Comment #15
johnchqueComment #17
johnchqueFixing tests.
Comment #19
sam152 commentedThey aren't quite constants, but they are keys on the workflow type annotation:
Here is some background on the issue: #2817835: When enabling moderation apply a relative state. I believe the original intentions can be summarised as: once moderation has been enabled on an entity type, the published status can no longer be used to control the state of content during normal operations. I think it doesn't work for the same reasons we wouldn't expect to be able to go:
So we don't expect to be able to programatically control moderation using the 'status' field. I think it's perhaps strange to leverage this as a feature for setting the initial state too, for the same reasons? It looks like we already discussed this exact semantic some 2 years ago: #2839371: Programatically creating a published entity doesn't result in a published entity. :)
Note: selecting the initial state from status is really only there to facilitate transitioning old/existing content to a model where moderation is enabled. It's a best guess to ensure there is no data integrity issues and I don't think is a precedent for making 'status' directly impact 'moderation_state'. I also think it might be tricky from a BC perspective. The initial state will change to something different for any users who had 'published' configured as the default for whatever reason.
I think we should do the following:
ContentModeration@WorkflowTypeto allow admins to configure a default moderation state, (with an upgrade path to set it to draft).I think that is the least disruptive and should account for the 90% use case. It doesn't exactly solve the requirement in the issue summary:
Existing content will always continue to be based on the publishing status, while that transition is made.
Updating the IS. If anyone sees any issues with this approach or disagrees with the analysis, we can shuffle it back.
Comment #20
sam152 commentedComment #21
sam152 commentedComment #22
berdirI thought that the default behavior is already defined based on the order, e.g. the first in the list is the default? So not sure if we need a new setting, actually? That order logic AFAIK also already works, there's just no way to have a different logic for edit/add content there.
To repeat, see #10, we're not that interested in the different default for edit/add directly, we just have the problem with default_content all ending up unpublished and want to get rid of that isNew() check there. While I agree that the logic there is mostly about existing content, it also applies to any content created through the API where the moderation state is not explicitly set, then I expect that it respects the initial status, otherwise sites might have to rewrite a lot custom code because content_moderation interferes with that?
That said, I think it's also not really related to this issue, so might be better to open a new one just for that?
Problem is that at least in 8.5, you can't explicitly set the moderation state on REST/default_content because of #2943899: Moderation state field cannot be updated via REST, because special handling in ModerationStateFieldItemList, so we also don't have the option to explicitly update our default content, at least not without adding yet another core patch.
Comment #23
sam152 commentedWIP patch to make this configurable. Outstanding tasks are:
Comment #24
sam152 commentedComment #25
alison(a) This functionality is important to me, thank you for working on it! Just wanted to chime in that, y'know, I want to have it :) :)
(b) I'm adding a related issue, I hope it feels related to y'all, it certainly does to me.
(I can't test the patch right now b/c I'm running stable 8.5.6.)
Comment #26
alison(Sorry for the extra comment)
FWIW I'm on 8.5.6 -- when I create a new node, the order of options in the "states" dropdown is correct (Published, Draft -- corresponds with the weighting on the editorial workflow settings screen), the option selected by default is "Draft" -- I want it to default to "Published."
(That's what you're changing in this issue, right?)
Comment #27
sam152 commentedLet me update the issue summary to be clearer about 'default' in this case, since it's a bit confusing. Let me know if that clears it up.
Comment #28
sam152 commentedComment #29
sam152 commentedComment #30
sam152 commentedAdding functional and kernel tests and ensuring users don't accidentally delete a state that is configured to be the default. If this goes green, I think it'll be good for a review.
Comment #31
alisonGreat title change, thank you! Good idea with the safety feature in latest patch.
Comment #32
johnchqueUploading patch #30 for 8.5.6. :)
Comment #33
nuuou commentedI'm getting an error running update.php when applying Patch #32 onto a Drupal 8.5.6 install.
PHP Fatal error: Call to undefined method Drupal\Core\DependencyInjection\ClassResolver::update() in /drupal-site/web/core/modules/content_moderation/content_moderation.post_update.php on line 103The code appears to be working, but I imagine these fatal errors aren't right. I only have one Workflow created, and it's applied to one Content Type.
Comment #34
merilainen commentedI tested the patch for 8.5.6 and it works nicely. I needed to have Published as default state, but there is an "external editor" role who can only save content as Draft. So I added a "Save as draft" transition (Published -> Draft) and now external editors can see only that. For other roles the Published is selected by default but they can also change it to Draft.
Edit 1: But it seems that if content is published and then turned back to Draft, it seems to stay published anyway even if the state changes. Is this by design? The UI doesn't allow me to make Draft default revision, so maybe a new revision is created which is unpublished but it's not the default revision. This sounds like a different issue.
Edit 2: It seems that the 8.5.6 patch fails on database update: Error: Call to undefined method Drupal\Core\DependencyInjection\ClassResolver::update() in content_moderation_post_update_set_default_moderation_state() (line 103 of content_moderation.post_update.php).
I suppose it's because ConfigEntityUpdater only exists for 8.6.x: https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Config%21...
Any solution for 8.5.x?
Comment #35
merilainen commentedIn the end I decided to go with a more simple approach for 8.5.6 because I suppose new features will not be back-ported even to 8.6.x
In hook_form_alter:
This will use the first item in the drop-down as default selection for new content, but will not affect existing content which already has a state.
Comment #36
johnchqueChanged a bit to follow previous code style as per #2949630: New service to make updating configuration entities easier inside update hooks. :)
Note: This is for 8.5.6 only. :)
Comment #37
nuuou commentedPatch #36 is working nicely for me now on Drupal 8.5.6. I was previously having the same issue as @mErilainen with Patch #32.
Comment #38
sam152 commentedIf the patch is working, any chance someone could review #30?
Comment #39
jibranHaven't found anything to complain about so RTBC for #30. Can you please make it the latest patch by reuploading it?
Comment #40
sam152 commentedSure. Thanks for the review.
Comment #42
jibranComment #43
larowlanshouldn't the update hook ensure this is always set? i.e. no need for the isset call?
same here
ah, because we could have workflows other than content moderation? which are the only ones updated in the update hook - if that's the reason, ignore my previous comments
Comment #44
sam152 commented@larowlan thanks for the review!
That is designed to cover the problem described in issues like #2543150: Document consequences of contrib changing config schema without core's API supporting config version tracking and #1677258: Configuration system does not account for API version compatibility between modules and was a critical bit of feedback I received on #2871354: Improve "allowed_html" to provide a better config diff.
If a module has exported a content moderation workflow, without the updated schema, we don't want to break any functionality once 8.7 is released. All config created before the patch should behave the same after the patch, regardless of config being processed by the update hook. In that sense the only changes allowed to config schema are additive ones and we must support updated an non-updated config alike.
Comment #45
larowlanCan we get a change record here, showing the UI option in a screenshot.
Cheers
Comment #46
sam152 commentedI have added a change record here: https://www.drupal.org/node/3001134.
Comment #48
larowlanCommitted a6b0f1d and pushed to 8.7.x. Thanks!
Published change record
Comment #49
tacituseu commentedThis is failing on PHP 5.5:
https://www.drupal.org/pift-ci-job/1074246
Comment #51
larowlanThanks @tacituseu
Rolled this back
Comment #52
sam152 commentedFixing! Thanks for the reviews everyone.
Comment #53
sam152 commentedPassing on 5.5.
Comment #54
alexpottPHPUnit has nice asserts for arrays meaning we don't have to deal with isset() differences between PHP5 and PHP7 at all.
Comment #55
alexpottAdding issue credit for people who help frame the issue.
Comment #56
sam152 commentedOh even better, thanks @alexpott!
Comment #57
alexpottCommitted b6a29fd and pushed to 8.7.x. Thanks!
Comment #60
sam152 commentedComment #61
sam152 commentedComment #62
gnugetIt seems that #3000573: After enabling Workflows and Content Moderation there is a fatal error when trying to edit content translation broke this patch for
Drupal 8.6.x.I just rerolled it just in case someone needs this feature at
Drupal 8.6.x.Comment #63
alisonTHANK YOU @gnuget !!!!!!!!!!!
Comment #64
alisonAnyone getting a fatal error when updating core from 8.6.x to 8.7.x? (specifically for me, it's 8.6.16 to 8.7.3)
When I go to update.php > "Continue" button (aka /update.php/selection), I get the following Fatal error on a white screen:
Fatal error: Cannot redeclare content_moderation_post_update_set_default_moderation_state() (previously declared in /path/to/web/core/modules/content_moderation/content_moderation.post_update.php:104) in /path/to/web/core/modules/content_moderation/content_moderation.post_update.php on line 187(line 104 is function content_moderation_post_update_set_default_moderation_state(), added in this patch)
(I did use the 8.6.x-reroll patch back in Feb; @gnuget have you updated to 8.7.x since then?)
Comment #65
gnugetHello @alisonjo2786
Yup, this happened to me.
I had my patch in the "patches" section of my composer.json file so when I updated to 8.7 the fix was already part of the core and when my patch was applied it added the method again triggering the same error that you pasted.
In my case I just had to remove my patch from the composer.json file and execute composer again (to re-download a version of the core without my patch but with the fix) and that made the trick.
Comment #66
alisonThank you @gnuget -- worked for me on my dev env in the cloud, it's just my local site that's still broken, I think maybe some of the DB updates ran before the failure, and I wasn't smart enough to make a DB backup of my local before running updb, siiiigh. Probably that's all it is.
Sorry -- to be clear -- your steps definitely fixed the redeclare error, but I still can't run updb, it throws:
I've cleared caches and whatnot, but, no dice. I'll prob just restart this local site from the cloud :)
Thank you!
Comment #67
alisonFigured it out! Just in case it helps someone else -- I had some old config crud still hanging around in my broken local site DB, **such as....**
system.action.moderate_to_published
...which included, among other things...
Ding ding ding!
A few
drush cdel _______commands later, I was all set.