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
When enabling moderation on a bundle the moderation state is not set on existing entities, therefore it gets the default state, which is often 'draft'.
Proposed resolution
When enabling moderation on a bundle the moderation state should be set to an unpublished state on unpublished revisions, and a published state on published revisions.
Remaining tasks
User interface changes
API changes
Data model changes
Comment | File | Size | Author |
---|---|---|---|
#47 | 2817835-47.patch | 14.26 KB | Sam152 |
#47 | interdiff.txt | 729 bytes | Sam152 |
#45 | 2817835-45.patch | 14.27 KB | timmillwood |
#45 | interdiff-2817835-45.txt | 3.44 KB | timmillwood |
Comments
Comment #2
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedWe'd possibly be guessing which state is appropriate if we didn't ask the user. Would it make sense to have a "default published" and a "default unpublished" state and make all the initialisation decisions based on two statuses instead of one?
Comment #3
timmillwoodWe have a default state, which is generally unpublished, but yes, maybe we should have a default published and default unpublished.
Comment #4
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedHm, it's tricky because even with an unpublished/published default, we'd still need to collect which state brand new items of content start their life out as. 3 states does perhaps feel overkill. Maybe these are configuration options that only appear when:
...and aren't stored and don't show up beyond that.
Comment #5
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commented#4 doesn't account for entity created programatically in a published or unpublished state that don't set a moderation state (see #2839371: Programatically creating a published entity doesn't result in a published entity.). For other modules that interact with entities using EntityPublishedInterface, we will want content moderation to work and not squash those decisions, hence doing this as a once off when moderation is enabled is a no-go.
I don't think there much getting around needing to store three default states:
I think this hasn't really been addressed before because the mental model is that content moderation takes over all things publish-related, but that of course isn't true of entities manipulated programatically without knowledge of CM or when CM is enabled for the first time.
If we can agree on that, I think we can move forward with a code solution.
Comment #6
alexpottI don't think we need to cope with
... this is not possible. Or if it is should be considered a bug.
I agree that we need a default published and default unpublished state to handle what happens to existing entities. I think these should be stored in the content moderation workflow because it allows for a new workflow to be deployed from a site with no entities to one with.
Comment #7
timmillwoodJust a quick example of how this could look in the config.
Comment #9
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedWhat about new entities that are created after CM is enabled with a publish status and no moderation status? Does the relative status get applied to those?
edit: for clarity
Comment #10
alexpott@Sam152 you can't actually do that :)...
This passes when added to the end of
\Drupal\Tests\content_moderation\Kernel\ContentModerationStateTest::testBasicModeration
Basically whilst saving the node with no moderation status it'll just do what it should and ignore the prior nonsense of setting it to published because once you've enable moderation it wins.
Comment #11
timmillwoodWhat I think @Sam152 means in #9 is a case such as:
This fails on the final assertion. Meaning the node is published with a draft moderation state.
Comment #12
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedThe case in #11 makes sense, but I was considering the case of programatically setting the status of the node after moderation was already enabled.
Before the workflow component split, this used to work and published content would stay published, so my expectations were that EntityPublishedInterface would still be meaningful outside the context of moderation. When this behaviour changed it prompted me to open #2839371: Programatically creating a published entity doesn't result in a published entity..
Having read the opinions of others, I do now agree with the conclusion that CM should totally restrict and control the publishing status for safety, but it does "break" any other code that wants to publish or unpublish something.
The reason this all relates to this issue is that the relatives states could be applied any time something manages to publish or unpublish, so both CM and users of EntityPublishedInterface could continue to work, but as others have already stated, sounds dangerous :)
Good point re: not requiring a third state, two states sound good.
Comment #13
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedThis relates to #2843083: Select entity type / bundle in workflow settings in the sense we'll need a whole workflow settings form for these settings.
Comment #14
timmillwoodGetting around to finishing this off. Thoughts?
Comment #16
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedLooking pretty good. Comments/questions as follows.
I think we're still missing the UI to actually select these right? Sounds like it would be good to ensure the discussions in #2843083: Select entity type / bundle in workflow settings are aware the plugin-based workflow settings form is likely to grow to at least two more inputs.
It feels a bit wrong for the workflow entity to know about content publishing and entities in this manner. Perhaps this should delegate the initial state to the workflow plugins instead, that way it can all be contained in the content_moderation plugin?
Comment #17
timmillwood#16.1 - ah yes, working in code and test I forgot about the UI. Currently I've put these in type_settings, but maybe they should be in the main schema and therefore the main UI.
#16.2 - Maybe the plugin is the best place for this, but not sure it should be content_moderation specific.
Comment #18
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedRe: #16.2, why is that? As far as I know workflows makes no assumptions that a workflow entity is controlling an item of content or even has anything to do with other entities at all. AFAIK content moderation is the coupling between content entities/publishing and the workflow config entity.
Comment #19
timmillwoodI was going to continue working on this this morning, but I think it needs some more thought.
Firstly I want to retract my comment in #17, I think this should be content_moderation specific, and I think getInitialState() should be moved to the plugin.
The UI should also be in the plugin, like the UI for the published and default revision checkboxes.
Comment #20
timmillwoodMoving
getInitialSate()
to the workflow type plugin, but still not implemented the UI, will have a think about where this should go.The problem with
getInitialSate()
being in the plugin is we now need to pass in the workflow as a param.Comment #23
timmillwoodPostponing on #2844594: Default workflow states and transitions
Comment #24
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedHere is an idea for a different approach. Not totally convinced but it solves a few problems. Namely:
One downside is the extra complexity, the other approach didn't have to save any entities.
Comment #25
alexpottIf we decouple Workflow entity from Workflow Type plugin - then the approach in #20 becomes way more via as an overload of the initialState method that is already on the workflow type plugin. Just need to do the not small task of #2849827: Move workflow "settings" setters and getters to WorkflowTypeInterface
Comment #26
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedYeah, I the simpler approach is probably preferable, provided it doesn't behave different to entities with a valid content_moderation_state counterpart in too many scenarios. Views is one of those, but perhaps not a deal breaker.
Comment #27
alexpott@Sam152 you're right - we need to actually do all the ContentModerationState content entity creating... nice start. I.e. views is a deal breaker.
Comment #28
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedOkay, I can have a look at resolving the @todos and polishing this up of the approach is agreed upon.
Comment #29
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedHere is a patch that resolves the @todos and adds test coverage for the issues #24 had. I think this is ready, but a possible follow up could be connecting the queue items to a batch in the UI after saving a workflow, however this just gets normally processed on cron if the workflow entity is updated with a config import or programatically.
Comment #30
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedThis was a bit of a different approach to this problem. Doing this in the query would require a tag and some altering to connect it to the content_moderation_state entity. Given it's not the default case that there will be lots of entities already under moderation when enabling a bundle, it seems okay to queue all entities in that bundle and filter out the ones with an existing state during the processing.
Comment #31
alexpottDo we need to remove the queue on uninstall? Also why do we need to create the queue on install? Aggregator doesn't appear to do this. In fact I think this is unnecessary - i think queues are created on demand. What's a little amusing is that i'm not sure queue tables are removed.
I think we need to also do this on workflow update too.
$entity->getEntityTypeId() can make this a bit shorter.
This looks like it'll blow when there are lots and lots of entities. This is the advantage of @timmillwood's approach. Maybe there is a way to make it work with views. The scale of the problem when you enable a workflow on a bundle with 1000s of entities already scares me. But for content_moderation to work somehow we have to solve this.
Comment #32
alexpottMore on point 4. I think we need 2 queues to do this properly at scale. One queue to save this entity type/bundle ID and another to store the entities to process. The entity type/bundle ID will be responsible for adding entities to the other queue. It'll need to store where it is at - and the highest entity ID when it was created.
Comment #33
timmillwoodUsing a queue seems a bit overkill when we can dynamically get the initial state. It's shame the plugin doesn't know about the entity, and therefore we'd have to pass it in.
Comment #34
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedRe: #33, the approach was to ensure views support works.
Two questions before addressing the feedback and continuing with this:
Comment #35
timmillwood@Sam152 - ok, this makes sense Re: Views.
Comment #36
timmillwoodComing back to this because it's a must-have on #2755073: WI: Content Moderation module roadmap. I think the biggest kicker is still views, I am no views expert but I believe if it's not in the database then views can't query against it. Therefore some form of queue to generate the ContentModerationState entities seems to be the only choice.
My concern with a queue is if there are many entities to process it will take a number of cron runs to get through the queue, therefore if there is a view of archived content it'll only show a sub-set of results. Maybe we need an admin form somewhere to process all entities?
Comment #37
amateescu CreditAttribution: amateescu for Pfizer, Inc. commentedI think Views support could be fixed with some
COALESCE()
trickery. Not sure that it will actually work for this complex case where two different values (moderation states) have to be returned based on the value of an entity field, but it's worth a shot.What would definitely break are entity queries (e.g. an entity query for: "give me a list of article nodes that are in a 'draft' state"). However, 'moderation_state' is a computed field and I don't think EQ support querying on those, so we should be fine in this regard.
Comment #38
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedI think #37 is perhaps more complicated based on the fact it's not based on a field, but an interface. We could assume something about what's going on behind the scenes based on entity keys, but it's not perfect.
In any case, I'd be interested in seeing what it looks like.
Comment #39
timmillwoodI did a little research and I think the
COALESCE()
might just work.Comment #40
timmillwoodHere's a re-work of #20.
Wondering if we could add the views stuff as a follow up?
I think workflows / content moderation views integration needs a lot of love, so might be good to get an initial fix of this in first.
Comment #41
timmillwoodThis is a "must-have" in #2755073: WI: Content Moderation module roadmap
Comment #42
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedFew nits and things. Otherwise, as per IRC, lets get this in as a first pass and look at creating some kind of batch to make views work as a follow up.
Old doc comment?
Can probably reduce a level of nesting here with:
return $workflow->getState($entity->isPublished() ? 'published' : 'draft');
Old comment?
Maybe a better name for this? InitialStateTest?
Stray newline
Lets just overload $entity in the ContentModeration type plugin. Passing workflow is a bit annoying but necasary for the current structure I think.
Comment #43
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedCreated #2867312: Create a batch process to ensure entities that exist for newly moderated entity types get a corresponding ContentModerationState entity, so that views works..
Comment #44
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedComment #45
timmillwoodFixing #42
Comment #46
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedLets not pass $entity to parent, otherwise RTBC.
Comment #47
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedFixing tiny nit, then can RTBC if green.
Comment #48
Sam152 CreditAttribution: Sam152 as a volunteer and at PreviousNext commentedComment #49
timmillwood+1
Comment #51
timmillwoodSeems like an unrelated test fail, so back to RTBC.
Comment #53
timmillwoodComment #54
alexpottCommitted and pushed 469003d to 8.4.x and 013edec to 8.3.x. Thanks!
Backported to 8.3.x because content_moderation and workflow are both experimental.
Coding standards fixes on commit.