Problem/Motivation

When reverting to a previous revision of an item of content, the new revision created matches the moderation state of the revision being reverted.

While this keeps the integrity of the workflow intact, since content can only be recreated in a state that was valid at some previous point in time, it may not always be what users expect and it limits the utility of reverting in general, since certain revisions can only be recreated and immediately published or can only be recreated and reverted back to a draft.

Steps to reproduce

  1. Create a draft Node e.g. titled Test v1
  2. Create a new draft e.g. titled Test v2
  3. Publish Test v2
  4. Decide you prefer Test v1 but want to tweak it first before republishing
  5. Click revert: Test v1 is immediately published and tweaking it first is not possible

Proposed resolution

The proposed resolution is to modify the revision revert confirmation form to include a moderation state select list. This will allow the user to select the desired moderation state for the new revision being created. The list of available moderation states will be filtered to only include those that the user has the permission to transition to.

Remaining tasks

  1. ☑ Update the revision revert form to include the moderation state select list.
  2. ☑ Add logic to filter the list of available moderation states based on user permissions.
  3. ☑ Update the form submission logic to save the selected moderation state to the new revision.
  4. ☑ Ensure the available workflow transitions and permissions are taken into account per #74.
  5. ☑ Add and update tests to ensure the new functionality is working as expected.

But there is one outstanding comment whether this should be set up to more broadly cover moderated content beyond Nodes. Addressed in comment #86.

User interface changes

A new "Moderation state" select list will be added to the revision revert confirmation form.

revert state form

Introduced terminology

None

API changes

None

Data model changes

None

Release notes snippet

The confirmation screen for reverting Nodes when Content Moderation is enabled now let's the editor decide whether to revert to a particular moderation state. Previously reverting would always publish the older revision.

CommentFileSizeAuthor
#123 2906568-123.patch22.86 KBnitinkumar_7
#122 drupal_2906568__2026-05-19.patch22.86 KBopi
#120 gin.PNG385.4 KBrkoller
#120 claro.PNG378.5 KBrkoller
#107 mr.mp41.86 MBrkoller
#107 main_with_cm.mp41.78 MBrkoller
#107 main_without_cm.mp41.2 MBrkoller
#107 revert.jpg96.69 KBrkoller
#102 2906568-nr-bot_so2292e6.txt90 bytesneeds-review-queue-bot
#100 2906568-nr-bot_xk9cr8_0.txt90 bytesneeds-review-queue-bot
#99 Screenshot from 2025-08-18 08-21-56.png54.67 KBacbramley
#97 2906568-nr-bot_a2h4exy5.txt90 bytesneeds-review-queue-bot
#95 revert-to-draft-2906568-95.patch10.48 KBjlongbottom
#93 2906568-87-10.3.x.patch21.95 KBmstrelan
#91 revert-to-draft-2906568-91.patch23.26 KBjlongbottom
#90 revert-to-draft-2906568-90.diff23.18 KBjlongbottom
#89 revert-to-draft-2906568-89.patch23.08 KBjlongbottom
#87 drupal-core-2906568-MR5696-at-3ba5a1a1.patch21.99 KBericgsmith
#78 interdiff-2906568-76-77.txt3.57 KBacbramley
#78 2906568-77.patch22.16 KBacbramley
#76 interdiff-2906568-75-76.txt1.44 KBacbramley
#76 2906568-76.patch19.05 KBacbramley
#75 reroll_diff_65-75.txt5.96 KBravi.shankar
#75 2906568-75.patch18.51 KBravi.shankar
#65 2906568-65.patch18.7 KBnikitagupta
#64 interdiff_63-64.txt6.65 KBnikitagupta
#64 2906568-64.patch18.69 KBnikitagupta
#63 2906568-63.patch18.71 KBnikitagupta
#60 2906568-60.patch18.69 KBlarowlan
#50 interdiff-2906568-38-50.txt2.19 KBacbramley
#50 2906568-50.patch18.69 KBacbramley
#47 interdiff_38-47.txt983 bytesravi.shankar
#47 2906568-47.patch18.61 KBravi.shankar
#38 interdiff.txt8.73 KBsam152
#38 2906568-38.patch18.67 KBsam152
#34 2906568-34.patch16.94 KBsam152
#34 interdiff.txt3.82 KBsam152
#32 2906568-32.patch14.42 KBsam152
#32 interdiff.txt17.71 KBsam152
#28 2906568-8.7.patch9.18 KBalmunnings
#22 2906568-22.patch9.38 KBjhedstrom
#22 interdiff-2906568-21-22.txt1.81 KBjhedstrom
#21 2906568-21.patch9.35 KBjhedstrom
#21 interdiff-2906568-19-21.txt3.28 KBjhedstrom
#19 Screen Shot 2018-01-24 at 1.19.16 PM.png53.84 KBjhedstrom
#19 2906568-19.patch6.97 KBjhedstrom
#18 Screen Shot 2018-01-23 at 10.17.35 AM.png37.21 KBjhedstrom

Issue fork drupal-2906568

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

catch created an issue. See original summary.

sam152’s picture

Do we just need to match the default-ness of the revision being reverted to? Ie, if you revert to a non-default draft revision, it becomes the latest and pending revision? Huge UX head-scratcher either way!

catch’s picture

Category: Task » Bug report
cburschka’s picture

Okay, the specific condition under which this is a problem seems to require the following:

- There are two distinct states A, B; and B has the content_moderation setting default_revision.
- The node's default revision has workflow state A.
- The user reverts to a non-default revision with workflow state B.
- The user does not permission to change the state from A to B.

(In specific, this happens if you have a default Draft or Archived revision, and revert to an older Published revision. Also if you have a default Published revision and revert to an older Archived revision. It's not a problem if you revert to a Draft, because that simply creates a forward revision.)

cburschka’s picture

UX-wise, it would be ideal to allow access to the revision list and allow reverting in general, but check the specific case:

- If the target revision has a different state than the current default.
- And that state moves the default revision
- Find the transition corresponding to that state change
- And check that the user is permitted to access it.

We also need to show each revision's workflow state in the revision list, because otherwise it would be completely intransparent which revisions you can revert to. (Edit: #2612222: Provide a better way to alter entity revision overview impedes that part.)

cburschka’s picture

Update: If #2899719: Revision/version language on revision listing page is misleading with content moderation enabled is resolved by unconditionally setting the default revision, the transition will need to be checked regardless of whether the state would normally set the default revision.

sam152’s picture

- Find the transition corresponding to that state change
- And check that the user is permitted to access it.

I don't think we should be validating this. We are reverting to a revision which was previously already "allowed". The question of if the state of the current revision can transition to the state we're reverting to is kind of irrelevant right? Conceptually we're going back in time, not creating something new based on something that exists.

cburschka’s picture

The question of if the state of the current revision can transition to the state we're reverting to is kind of irrelevant right?

So would you say that reverting revisions simply trumps all the content moderation permissions, such that a reverter can restore the node to any workflow state it has previously been in?

For example, this would mean that you can't permanently Archive a node while still giving revert permissions to editors. Anyone could simply undo the Archive transition by reverting to the Published version.

sam152’s picture

To me that's what "revert permissions" is, restoring some previous state despite other circumstances. I understand what you're saying, but it seems like there could be a whole seperate set of issues to be able to replicate content_moderation semantics in another interface that is fundamentally concerned with something else, ie going back vs going forward.

My interests are mostly based in keeping things simple and reducing the surface area of edge cases that will need to be handled.

cburschka’s picture

That makes sense - if we take this approach, then I guess the issue would be works-as-designed, and we only need to add some documentation for it.

It's certainly simpler if we consider revision-reverting to happen on a level below business logic like moderation, since the moderation state is part of the revision. After all, we also don't revalidate any of the other fields either, which also might contain special logic that would prevent the old revision's values to be entered manually.

sam152’s picture

Right! One thing I'm unsure of though is reverting to a revision which was never default. That is, for a draft, should that create a new pending draft revision instead of a default draft revision? I haven't actually tested this but would be interested to see how it behaves. My understanding was based on the following from the IS:

you can revert to any revision of a node to make it published

cburschka’s picture

One thing I'm unsure of though is reverting to a revision which was never default. That is, for a draft, should that create a new pending draft revision instead of a default draft revision?

Currently, the decision whether or not the new revision becomes default seems to be the same for reverting as for editing. That is, reverting a never-published node to a draft revision sets it as a default, reverting a published node to a draft creates a pending revision, and reverting to a published revision makes it the new default. (This is what I've found, at least.)

I don't believe it is possible to revert to a never-default revision and make the new revision default. If it was never the default revision, then it was created as a pending revision. Reverting to it applies the same logic as before, so the new revision should be a pending revision as well.

you can revert to any revision of a node to make it published

As far as I understand, this refers to making the node published (again) by reverting to a revision that was already published once. (Please correct me if that's wrong, @catch)

sam152’s picture

Okay, perhaps if that's the case I misunderstood the IS and @catch's thoughts were aligned with #9.

In any case, if content_moderation is providing the defaultness-matching part, that should probably be moved to core. The more pending revisions work from an API level by themselves, the easier time we'll have with workspaces (or any other user of pending revisions) which will be utilising the same api without content_moderation necessarily being installed.

We can write a test for this using the isDefaultRevision, setNewRevision methods and see what happens when we do a revert.

Version: 8.5.x-dev » 8.6.x-dev

Drupal 8.5.0-alpha1 will be released the week of January 17, 2018, which means new developments and disruptive changes should now be targeted against the 8.6.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

sam152’s picture

#2891215: Add a way to track whether a revision was default when originally created might end up helping with this issue.

@catch, any thoughts on the approach to solving this?

catch’s picture

Issue tags: +Needs usability review

Adding the usability review tag.

A few ideas, I'm leaning towards the first option if it turns out to be viable, since it could then be acceptable for both workspaces, content moderation and any other module that's doing things with pending revisions.

- only allow reverts to default revisions (will depend on the new field being added) - assume that any module using pending revisions will have it's own, separate UI to make them public. Possibly have default and pending tabs on that page that show one or the other. This would probably be a straight change to that tab that just makes it more robust for different use cases.

- completely override the tab when content_moderation (and later workspaces) is installed.

- alter the access for the revert action to also require some kind of content_moderation permission

- leave it as is, but probably with some messaging (this seems like the worst option, I think we should probably do one of the first three).

jhedstrom’s picture

Issue summary: View changes
StatusFileSize
new37.21 KB

Could we simply add the moderation select list to the revert confirmation form?

That would inherit the current user's permissions to which transitions they could make, so when reverting to a previously published revision, if they didn't have that permission, they could only revert it to a draft, etc.

jhedstrom’s picture

Status: Active » Needs review
StatusFileSize
new6.97 KB
new53.84 KB

Here's a rough start on adding the moderation state to the revert form. Due to the way the NodeRevisionRevertForm saves the revision, a simple form alter would not work here.

I'm working on some tests now, but wanted to post this here to get feedback on the general approach.

timmillwood’s picture

This looks awesome so far. Would be good if you could demo it on the UX call on Tuesdays.

One issue I see, from a UX point of view is, why do I need to select a moderation state?

jhedstrom’s picture

StatusFileSize
new3.28 KB
new9.35 KB

One issue I see, from a UX point of view is, why do I need to select a moderation state?

Certainly. I think the UI here could use some work in terms of wording. For users that, for instance, didn't have permission to transition from a published state, the UI remains as it is now, but the reverted revision comes in as draft instead of published as it would now.

This adds a test for the current behavior of the patch.

jhedstrom’s picture

StatusFileSize
new1.81 KB
new9.38 KB

I realized that the approach above for determining allowed transitions was slightly flawed. Since the goal here is not to bypass content moderation, we want to check which transitions the current user has permission to from the default state of a new revision, rather than which transitions they have access to away from the revision to be reverted.

This patch makes that change.

jhedstrom’s picture

+++ b/core/modules/content_moderation/src/Form/RevisionRevertForm.php
@@ -0,0 +1,138 @@
+      // Valid transitions are determined not from the this revision, but
+      // rather, from the default state of a new revision. If a user has no
+      // permissions for any of these transitions, the form is hidden.
+      $new_entity = $this->revision->createDuplicate();

After some feedback, I'm thinking this isn't quite the correct approach either. For instance, if there is a published version, and somebody reverts and selects draft as the target state, then the published version goes away. Instead, available transition states should perhaps be calculated from the latest published version (if it exists), and falling back to the latest revision.

kevin.dutra’s picture

Status: Needs review » Needs work

Although the tests are still able to apply the patch, this now collides with work introduced in #2940899: Regression: _entity_form routes should not get load_latest_revision_flag by default: changes default behavior (= regression) while only Content Moderation needs it. (There are now two services with the same name.)

timmillwood’s picture

We should be able to update the patch here to make use of the same route subscriber.

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

almunnings’s picture

StatusFileSize
new9.18 KB

Rolling for 8.7
Just added the methods as before, but reusing the subscriber.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

sam152’s picture

Title: Revision revert screen allows users to bypass content moderation » Provide users with the option to select an appropriate moderation state when reverting to a previous revision
Assigned: Unassigned » sam152
Category: Bug report » Feature request
Issue summary: View changes

For instance, if there is a published version, and somebody reverts and selects draft as the target state, then the published version goes away. Instead, available transition states should perhaps be calculated from the latest published version (if it exists), and falling back to the latest revision.

Nice work on this @jhedstrom, but I'm not sure this approach makes sense either.

When reverting to a previous revision, we aren't transitioning any of the content from the latest revision, so I don't think the states available from that revision make sense. Imagine someone has the permission to use the "Publish approved content" transition. If the latest version is "Approved", they'd have access to use "Published" on that specific content, but it doesn't imply they'd have access to publish any content in the revision history.

For that reason, I don't think any of the current workflow transition permissions are appropriate for this use case. I think each workflow would need a new permission per state called "Revert as $state", where the default fallback when none of these permissions are granted is that the reverted revision matches the original state of that revision.

Using this implementation, any new behaviour is guarded behind new permissions and the existing behaviour is preserved if site builders do nothing. I believe it introduces the least amount of assumptions and provides the most safety for existing installations.

I'm going to rejig the issue summary and title to match the direction this issue is going in and I likely have some time to work on this. It might be that this implementation doesn't pass framework/product/usability reviews, in which case I think it'll be a great implementation for a contrib module and my vote would be to close the existing issue as "Works as designed".

sam152’s picture

Issue summary: View changes
sam152’s picture

Status: Needs work » Needs review
StatusFileSize
new17.71 KB
new14.42 KB

This is just a progress patch, I'm going to continue working on this. Currently still needs:

  • Additional testing on new service.
  • Probably need to overhaul ModerationRevisionRevertTest.
  • Reverting a pending revision seems to be behaving strangely.
sam152’s picture

Status: Needs review » Needs work
sam152’s picture

Status: Needs work » Needs review
StatusFileSize
new3.82 KB
new16.94 KB

Another progress patch, still needs some testing. I think I also need to validate that ::createRevision should be used on the node revert form. The translation affected status gets messed up otherwise. Seeing what the testbot thinks.

sam152’s picture

Status: Needs review » Needs work
mpp’s picture

I like the UI/UX part to select the moderation state.

However, when I revert a revision to 'draft', I'd expect the current published revision to remain published but now the node is unpublished. How can I make sure that forward revisions are enabled?

sam152’s picture

However, when I revert a revision to 'draft', I'd expect the current published revision to remain published but now the node is unpublished. How can I make sure that forward revisions are enabled?

I agree, if the patch isn't doing that, I believe something is not quite right. I will follow-up and hopefully add some tests along the way.

sam152’s picture

Status: Needs work » Needs review
StatusFileSize
new18.67 KB
new8.73 KB

Fixing tests and expanding the test coverage.

sam152’s picture

Assigned: sam152 » Unassigned
mpp’s picture

Status: Needs review » Needs work

Patch looks good, thank you!

+++ b/core/modules/content_moderation/src/Form/RevisionRevertForm.php
@@ -0,0 +1,85 @@
+    $instance->moderationInformation = $container->get('content_moderation.moderation_information');
+    $instance->validation = $container->get('content_moderation.state_revert_validation');

This should happen through the constructor.

sam152’s picture

Status: Needs work » Needs review

Using constructor injection requires the subclass to change when the constructor of the base class changes and is also more verbose. I don't really see the point if the class isn't being unit tested.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

acbramley’s picture

  1. +++ b/core/modules/content_moderation/src/Form/RevisionRevertForm.php
    @@ -0,0 +1,85 @@
    +      '#description' => $this->t('Select the moderation state that will be assigned to the content revision once it has been reverted. The state selected here may either immediately publish content or create a pending revision that may continue to be edited.')
    

    Do we want to mention here that it could immediately unpublish the content as well?

  2. +++ b/core/modules/content_moderation/src/Permissions.php
    @@ -32,8 +32,17 @@ public function transitionPermissions() {
    +          'description' => $this->t('For supported entity types assigned to the %workflow workflow, allows users to revert <em>any</em> content revision to the %state state. Reverting revisions will not consider any existing transition based permissions.', $args)
    

    Very nice description 👌

  3. +++ b/core/modules/content_moderation/tests/src/Functional/ModerationRevisionRevertTest.php
    @@ -86,6 +104,13 @@ public function testEditingAfterRevertRevision() {
    +    $node_storage = \Drupal::entityTypeManager()->getStorage('node');
    +    $revision = $node_storage->loadRevision($node_storage->getLatestRevisionId($node->id()));
    

    Should this not use $this->nodeStorage?

  4. +++ b/core/modules/content_moderation/tests/src/Functional/ModerationRevisionRevertTest.php
    @@ -104,4 +129,113 @@ public function testEditingAfterRevertRevision() {
    +  protected function assertLatestRevision($id, $moderation_state, $published, $default) {
    

    Nice work on these tests, this is testing that not only the revert is working, but that it's not overwriting the default revision state 👏

Only small nits from me so leaving in NR for now.

ryan.ryan’s picture

Patch applies cleanly and works in my testing. From a UX perspective, I may make an argument for it being part of the revision dropdown, but this is a good start.

sam152’s picture

Status: Needs review » Needs work

Thanks for the review @acbramley. Marking NW for those points.

ravi.shankar’s picture

Assigned: Unassigned » ravi.shankar

Working on this, it needs a re-roll also.

ravi.shankar’s picture

Assigned: ravi.shankar » Unassigned
Status: Needs work » Needs review
StatusFileSize
new18.61 KB
new983 bytes

Here I have added a re-roll of patch #38 and I have tried to address point no. 3 of comment #43.

Status: Needs review » Needs work

The last submitted patch, 47: 2906568-47.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

acbramley’s picture

Status: Needs work » Needs review

Patch #38 still applied fine, please ignore #47.

acbramley’s picture

StatusFileSize
new18.69 KB
new2.19 KB

Fixed #43

acbramley’s picture

@dpi brought up a very good point that this is Node centric at the moment. With #2350939: Implement a generic revision UI we could make this generic enough to apply to any entity type using content moderation. Do we want to block this issue on that?

dpi’s picture

benjifisher’s picture

Issue summary: View changes
Status: Needs review » Needs work
Issue tags: +Needs issue summary update

We looked at this issue at the #3173646: Drupal Usability Meeting 2020-09-29.

I am setting the status to NW and adding the tag for an issue summary update. Before we can give a proper usability review, please

  • Update the "proposed resolution" section with the approach used in the current patch.
  • Add screenshots under the "user interface changes" section. You can re-use the screenshots from Comments #18 and #19, if those reflect the current patch.
  • In the same section, mention what usability issues there are. Is it a question of the interface text or the use of a select list or something else?

I am also fixing a typo in the issue summary.

Version: 9.1.x-dev » 9.2.x-dev

Drupal 9.1.0-alpha1 will be released the week of October 19, 2020, which means new developments and disruptive changes should now be targeted for the 9.2.x-dev branch. For more information see the Drupal 9 minor version schedule and the Allowed changes during the Drupal 9 release cycle.

azinck’s picture

For all the sites I've built, I don't really want any of the things being asked for in this thread. In fact, I would ideally prefer the old core behavior (before this: #2809123: Reverting a revision doesn't keep moderation state) be restored.

Specifically I'd argue that "reverting" a revision should create a copy of that revision in the "default" moderation state (draft) so that it can go through the content moderation process again.

It avoids all the difficulties of having to figure out complicated permissions, and limits the number of weird edge-cases and additional content moderation considerations (consider that this is yet another code path for effectively "publishing"). Keep it simple: if someone is allowed to edit a node then they should be able to revert an old revision, making a new draft based on it.

But I assume I must be in the minority here. I just don't see a big upside to allowing reversion into a specific moderation state, and it seems to add a fair bit of complexity.

sam152’s picture

@azinck, the proposed approach solves your use case. You could only grant access to revert to draft and then it works in the same way before the bugfix you linked.

The problem with always assuming draft, is you're conflating access to revert something, with access to assigning the 'draft' state to a revision. A workflow or site may not be configured to allow the given user to create content in that state.

azinck’s picture

@Sam152: thanks for the response. Yes, I recognize my proposed solution is, indeed, different, with different trade-offs than what has been discussed on this thread so-far.

I'm suggesting that, from my experience actually implementing content moderation on a variety of sites as a consultant, the solution I described maps onto how my clients expect things to work, is relatively simple to implement, and resolves some of the more challenging UX problems and permissions edge-cases.

I agree that it is less flexible than what has been discussed here, and we are already doing what you suggest (limiting users to only being able to revert to draft) in order to achieve our goals. The work in this issue is probably far enough along that it's worth continuing, but I did want to put out there what my experience has been in the real world, and consider that as a possible way to resolve some of the trickier aspects of this problem.

sam152’s picture

The issue with implementing your simplified approach and not continuing work on the current approach is, it changes the behavior of existing installations, which I can say from experience is a good way to ruffle a lot of feathers.

oktboy’s picture

We are using Content Moderation with groups. I tried Patch 38 and 50 through 'composer update' but keep getting error on Drupal 9.1.0

Could not apply patch! Skipping. The error was: Cannot apply patch https://www.drupal.org/files/issues/2020-06-25/2906568-50.patch

Modules used as well:
Workflow
Group
Group Permissions
Content Moderation Notifications

Anyone can point me in the right direction to debug that would be great.

larowlan’s picture

StatusFileSize
new18.69 KB

reroll

sam152’s picture

@oktboy and any others looking at this issue, I would probably opt for the module implementation (https://www.drupal.org/sandbox/sam/3118829) over a patch if you need this feature today, since this is a fairly experimental patch that may not get signed off.

Version: 9.2.x-dev » 9.3.x-dev

Drupal 9.2.0-alpha1 will be released the week of May 3, 2021, which means new developments and disruptive changes should now be targeted for the 9.3.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

nikitagupta’s picture

Status: Needs work » Needs review
StatusFileSize
new18.71 KB

Rerolled patch #60.

nikitagupta’s picture

StatusFileSize
new18.69 KB
new6.65 KB

fixed drupal CI issue.

nikitagupta’s picture

StatusFileSize
new18.7 KB

fixed drupal CI issue.

kim.pepper’s picture

Issue tags: +#pnx-sprint
donquixote’s picture

Interesting stuff.

----

@azinck (#55), @Sam152 (#56):

I think the scenario described by azinck is quite common. A project I work on has a similar situation, where we want "Revert" to copy the revision as a new draft (= forward revision), which should then be published as a separate operation.

This said, I am happy with the solution proposed in this issue, if it can be configured to cover the described scenario - which seems to be the case according to Sam152 - so fine for me.

-----

For the available workflow states for the reverted revision, we should take into account whether this is the latest forward draft revision, or any other forward revision, or a past revision.

For a latest revision, I never want to "Revert", instead i want to "Publish". This should also be reflected in the button/operation label in the revision list.

Without this, the workflow described by @azinck in #55 would not work.
If "Revert" copies the revision as draft, and you are sent back to the revision list, you now need a button to actually publish that latest revision that was just created.

If there are multiple forward draft revisions, I am not really sure how we should handle the non-latest ones.
In my opinion, they should be treated the same as past revisions. A colleague I talked with had a different opinion, and thinks they should be treated the same as the latest revision, with a "Publish" button.

azinck’s picture

@donquixote: I disagree with the idea of putting a dedicated "publish" button anywhere on that revision list page. Publishing isn't always the only valid state for all latest revisions (it's not even always *a* valid state for some forward revisions in some workflows) .

In my mind, the behavior should be exactly the same for all non-current revisions regardless of whether they're forward revisions or not. This patch calls the action "revert and set to" which makes sense for past revisions, but not for the latest revision (which is what is tempting you to call it "publish", I think). I might suggest calling it something like "transition state", or "change state", or "clone to new state" or something similar that makes sense no matter which revision it is. Choosing that action should bring up the form provided in this patch for choosing from the valid destination states. Then the operation would clone the revision and set it to its destination state, and, if appropriate, make it the default (current) revision.

donquixote’s picture

Choosing that action should bring up the form provided in this patch for choosing from the valid destination states.

A site builder might want to narrow down the valid destination states through configuration, such that only "draft" is available.
But this would not be suitable for the latest revision. Otherwise, new redundant forward draft revisions will pile up.
After copying an older revision as a new forward draft, the user will end up on the revision list, and now they want an option to publish the latest revision - or ask somebody else to do it.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

azinck’s picture

@donquixote: Yeah, all of the confusion here is why I don't love the approach of allowing arbitrary transitions to occur via this UI. These aren't even "real" transitions...so all of the existing permissions and restrictions around transitions no longer apply. And you might have code that is responding to revisions entering a particular state that only expects a subset of possible source states, but this functionality completely breaks that assumption since it allows ANY source state to go to ANY destination state.

I think there are so many edge cases here that it's going to get super messy. That's why I still personally like my suggestion of "revert" merely allowing a revision to be copied as the latest revision and set to the default workflow state. Then all other workflow stuff gets accomplished and enforced via the normal means.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

fernly’s picture

Status: Needs review » Needs work

I used the patch from #60.
Changing the moderation state for a revision works, but it allows all states to be chosen. The workflow transitions and permissions are not taken into account. This way any editorial user can publish content without having the permission to do so.

ravi.shankar’s picture

StatusFileSize
new18.51 KB
new5.96 KB

Added reroll of patch #65 on Drupal 10.1.x, keeping the status needs work for comment #74.

acbramley’s picture

Status: Needs work » Needs review
Issue tags: -Needs issue summary update
StatusFileSize
new19.05 KB
new1.44 KB

Fixed the failure. I don't think #65 is valid considering we have tests covering this functionality.

Status: Needs review » Needs work

The last submitted patch, 76: 2906568-76.patch, failed testing. View results

acbramley’s picture

Status: Needs work » Needs review
StatusFileSize
new22.16 KB
new3.57 KB

Fixed comparison of strings vs FormattableMarkup, added return types.

manuvelasco’s picture

The patch on #78 works for me just for sites without translate content enabled, that is because the route for content with translation is node.revision_revert_translation_confirm so we should also add a form for these cases and assign it to the mentioned route.

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

smustgrave’s picture

Status: Needs review » Needs work
Issue tags: +Needs issue summary update

This would be a great feature to have. Can we update the issue summary with the agreed upon solution though?

Also have posted in the #ux for usability review so hopefully happens soon.

mstrelan made their first commit to this issue’s fork.

mstrelan’s picture

Re-rolled and created MR against 11.x

mstrelan’s picture

Had the same thought as #51, this is very specific to nodes and I'm not sure it needs to be.

acbramley’s picture

Re #85 - it doesn't need to be but we are quite a way off using the generic UI for Nodes.

ericgsmith’s picture

Adding patch of MR5696 at commit 3ba5a1a1 for anybody needing a stable patch against 10.2

jlongbottom’s picture

When I revert a revision to Draft, it is updating the published SQL table for my field (eg: paragraph__field_X), as well as creating a new row in the revisions table (paragraph_revision__field_X). This does not happen when you make a new Draft by normal editing of the node - only when you revert to a draft.

I noticed this because I have some custom code elsewhere that is manually querying the published SQL table for my field and I was surprised to find the reverted draft values instead of the published values.

It looks like we call prepareRevertedRevision which is hard-coded to $revision->isDefaultRevision(TRUE);. Is this correct? Seems like it should be false if reverting to a draft.

or maybe something like this

protected function prepareRevertedRevision(NodeInterface $revision, FormStateInterface $form_state) {
    $revision = parent::prepareRevertedRevision($revision, $form_state);
    if ($form_state->hasValue('revert_state')) {
      $state = $form_state->getValue('revert_state');
      $revision->moderation_state = $state;
      return $this->nodeStorage->createRevision($revision, $state !== 'draft');
    }
    return $this->nodeStorage->createRevision($revision);
  }
jlongbottom’s picture

StatusFileSize
new23.08 KB

That seemed to have fixed the issue for me. Attached is my patch.

jlongbottom’s picture

StatusFileSize
new23.18 KB

Patch updated with translation support

jlongbottom’s picture

StatusFileSize
new23.26 KB

Updated route subscriber to work on multilingual sites by checking for node.revision_revert_translation_confirm

opi’s picture

Anyone else struggling to patch against 10.3.0 ?

mstrelan’s picture

StatusFileSize
new21.95 KB

@opi I've attached the patch we're using for 10.3.0, but I have set it to hidden so as not to confuse this issue further. This is based on the patch in #87 as that's what we were previously using and I haven't reviewed 89-91, which don't have interdiffs and aren't in sync with the MR.

opi’s picture

@mstrelan Thanks a lot ! I wasn't brave enough to rebase a 21KB patch last evening <3

jlongbottom’s picture

StatusFileSize
new10.48 KB

re-rolled against 10.3.1, but I did not bother with the tests

scott_euser’s picture

Issue summary: View changes
Status: Needs work » Needs review

Updated issue summary. I believe there is one outstanding comment whether this should be set up to more broadly cover moderated content beyond Nodes.

needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new90 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

acbramley’s picture

Rebased the MR and hiding patches to not confuse the bot.

acbramley’s picture

Issue summary: View changes
Status: Needs work » Needs review
Issue tags: -Needs issue summary update
StatusFileSize
new54.67 KB
needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new90 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

scott_euser’s picture

Issue summary: View changes
Status: Needs work » Needs review
  1. Updated issue support to reflect the making generic is addressed in #86.
  2. Merged 11.x into the branch locally to resolve conflicts, think I got it right, but let's see test coverage. Test warnings for php8.5 are from 11.x as they also occur on the daily pipeline
needs-review-queue-bot’s picture

Status: Needs review » Needs work
StatusFileSize
new90 bytes

The Needs Review Queue Bot tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

Consult the Drupal Contributor Guide to find step-by-step guides for working with issues.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.

opi’s picture

MR patch file doesn't apply on latest Drupal core 11.3.2 ...

acbramley’s picture

Status: Needs work » Needs review

Rebased against main

opi’s picture

Thanks a lot @acbramley , works like a charm on 11.3.2 !

Seems to be RTBC

rkoller’s picture

StatusFileSize
new96.69 KB
new1.2 MB
new1.78 MB
new1.86 MB

We've discussed this issue at the usability meeting #3577382: Drupal Usability Meeting 2026-03-13 yesterday. While writing up the summary i did some more testing and that additional experimentation raised a few questions in regard to our conclusions during the meeting. So I just wanted to ask for clarification before finishing the write up of the summary.

1. on the main branch without content moderation active, when you revert to a revision, that revision becomes active and you are able to edit it on the node edit form (main_without_cm.mp4). if you do the same step with content moderation installed the current revision remains the last published revision before the revert (main_with_cm.mp4) - same for the merge request checked out (mr.mp4). was that a misconfiguration on our end or is it impossible to actively edit a reverted version of a node afterwards.out of scope for this issue, but just trying to understand the overall problem space, cuz at least myself i am not that familiar in depth with the content moderation module.

2. on the confirmation step, does revision moderation step refer to the moderation state the current node has or does it refer to the state the revision has the node will be reverted to? (*during the meeting we've understood "revision moderation state" would refer to the state of the current revision not the one the node is reverted to - but the additional experimentation made me question that assumption)

the confirmation page for reverting a node with the option to choose the state the reverted node should have

acbramley’s picture

re 1. IIRC with CM - if you have an active published revision and then revert an old revision to Draft you can then edit that new Draft.

re 2. it's the moderation state for the revision that is created by the revert, it does not change the state of the revision you are reverting from.

acbramley’s picture

Status: Needs review » Needs work

Rebased with main and we have a failure, also need to:

1. Remove service id and use class name for aliasing
2. Add return types to everything new
3. Add strict_types to new class
4. Remove boilerplate cruft in PHP docs

We can probably also remove the interface from the service.

rkoller’s picture

thanks for the rebase @acbramley and apologies for the delay. i went into a rabbit hole over the course of the last week experimenting and trying to understand the whole problem space of the content moderation module figuring out what threw us off last friday that lead us into question two in #107. I understand things now finally. I havent completely finished the write up but most likely will raise this issue for a second round tomorrow. cuz the recommendation we've agreed on for question one in #107 was based on a false assumption. so i would like to revisit this issue a second time - always better for more complex problems. will post the combined summary afterwards. but one small problem/bug i'Ve noticed function wise while experimenting. steps to reproduce are as follows:

  • create an article with the title "my blog post V1" set to "draft"
  • edit the node and change the title to "my blog post V2" set to "published"
  • revert to revision 1
  • go into the "moderated content" list view
  • in the list view the node title says "my blog post V1" (if you follow / click the link viewing the page shows the content of V1)
  • if you edit that node the title says "my blog post V2" instead - the actual V2 of the node is shown
acbramley’s picture

@rkoller what state did you select during the revert?

rkoller’s picture

woops forgot to mention that detail. i kept the default aka the state the revision i reverted to is in: draft

rkoller’s picture

The problem not only applies for that reverted revision example. also happens if you do:

  • create an article with the title "my blog post V1" set to "draft"
  • edit the node and change the title to "my blog post V2" set to "published"
  • edit the node and change the title to "my blog post V3" and set it "drafT"
  • go into the "moderated content" list view
  • in the list view the node title says "my blog post V3" (if you follow / click the link viewing the page shows the content of V3)
  • if you edit that node the title says "my blog post V3 instead - the actual currently published V2 of the node is shown

so maybe not related to the MR after the problem also applies if the confirmation step is not used? but in any way, editing a node on the moderated content list view is pointing to the wrong revision

acbramley’s picture

if you edit that node the title says "my blog post V3

This is correct behaviour, unless that's a typo?

Can you try to reproduce this with a clean install off main branch? what you've described in #110 would be a serious bug.

rkoller’s picture

yep you are right, that was a typo. your question about "if i am able to reproduce it on a clean install of the main branch" was gold, thank you! even though i did so already, it helped me to think things through again another time and do another round of testing - now i finally figured it out. some context, i am using the composer template by joachim for all of my testing in core and contrib. and i've dropped the database and installed the site again repeatedly the last view days and the problem persisted. so i first thought the problem might have something to do with the symlinking joachims template applies. so I did a clean install with just composer create-project targeting main-dev@dev and installed in addition only the content moderation and workflows modules, basically the most minimal setup. that way, the problem hasn't turned up. did the same now with the joachim composer template install, dropped the db, reinstalled, and also only installed the content moderation and workflows modules. the problem also not showed up. the sole difference to before on the joachim template install, i had more core modules in addition to the ones installed by the standard profile. and i was able to narrow it down to the root cause and i am able to reliably reproduce on the clean composer create project install as well as the joachim composer template after a fresh site install. in the following the steps to reproduce (and i guess best should be i'll create a follow up cuz this bug isnt directly related to this MR - i've just noticed it in the context of it, and it took quite a while to untwine it and get down to the root cause:

  1. ddev config --project-type=drupal12 --docroot=web
  2. ddev composer create-project drupal/recommended-project:main-dev@dev
  3. ddev launch
  4. in the web installer use the standard profile
  5. on admin/appearance, install the admin theme, and choose it as the administration theme
  6. on admin/modules and install content moderation and workflows
  7. on admin/config/workflow/workflows/manage/editorial?destination=/admin/config/workflow/workflows add the article content type to Content types in the this workflow applies to section and save
  8. go to admin/content
  9. create a new article node with the title CM & workflows V1, the state draft , add something to the body, and save
  10. edit the node, change the title to CM & Workflows V2, change the status to published, add some more text to the body to easier distinguish the two revisions, and hit save again.
  11. go to to the revisions list page for the node and revert to V1
  12. go to content/moderated, the title in the list says CM & workflows V1, when you edit that node the title of the edited node also says CM & workflows V1 (basically you are editing the newly created third revision from the revert)
  13. on admin/modules also install workspaces module alongside
  14. go to admin/content
  15. create a new article node with the title CM, workflows, workspaces V1, the state draft , add something to the body, and save
  16. edit the node, change the title to CM, workflows, workspaces V2, change status to published . add some more text to the body to easier distinguish the two revisions, and hit save again.
  17. go to to the revisions list page for the node and revert to V1
  18. go to content/moderated, the title in the list says CM, workflows, workspaces V1, when you edit that node the title of the edited node says CM & workflows V2 instead (you are now basically editing the published revision V2 instead of the intended/expected V1)

Same effect if you also install the workspaces ui module. so it looks like the problem is located within the workspaces module that causes the problem if it is installed alongside the content moderation module.

p.s i am just uncertain if the followup should be filed against content moderation or the workspaces module?

acbramley’s picture

Re - #115 thank you for all that detail! Definitely sounds like something strange with workspaces (and potentially integration with that moderated content view). I would create an issue in workspaces.

benjifisher’s picture

@acbramley, @rkoller:

The problem you have been discussing (Comments 110-116) may be a regression of #3037136: Make Workspaces and Content Moderation work together.

It adds to the confusion that, in reading the comments here, I keep confusing "workspaces" and "workflows". ;)

P.S. One of the issues linked to that one is #3541380: Node edit form always uses published revision when using content_moderation and workspaces, currently NR. I think that is exactly what you are looking for.

benjifisher’s picture

@acbramley:

We looked at this issue at #3578994: Drupal Usability Meeting 2026-03-20, but we ran into the buggy behavior because we were using a test site that also had workspaces enabled. So we did not get to a firm recommendation.

My personal opinion (not the consensus of the meeting) is that it is confusing to add additional form elements to a confirmation form. I would rather keep it as a 2-step process: revert, then change the moderation state. What I would do to improve the process is add options to the drop buttons on the Revisions page: in addition to Delete, Revert, and Set ass current revision, add links for each available moderation state. Of course, the links would need either CSRF tokens or their own confirmation forms.

If you do not like that idea, then my second preference is to replace the confirmation form with a custom form: do not extend ConfirmFormBase and do not make it look like a confirmation form.

opi’s picture

MR 5696 does not apply properly on Drupal 11.3.7 ; Any clue ? thx

rkoller’s picture

StatusFileSize
new378.5 KB
new385.4 KB

Apologies, the writeup of the review for this issue took a "little" bit longer than expected, but this issue spiraled down into the rabbit hole of Content Moderation as a whole, and summarizing everything into a single, hopefully comprehensive, summary was quite challenging.

During our explorations we ran into a few more issues out of the scope for this issue. For example the problem we've encountered during our testing outlined in #2906568-115: Provide users with the option to select an appropriate moderation state when reverting to a previous revision turned out to be two fold. With Content Moderation and Workspaces module installed there is an already existing bug covered in #3541380: Node edit form always uses published revision when using content_moderation and workspaces, while with just Content Moderation installed the label for the default CTA on the Operations drop button is misleading which is covered now in #3580791: The edit button for a node with forward revision(s) on admin/content does not communicate editing the latest revision instead of the expected current revision.

Usability review

We discussed this issue for the first time at #3577382: Drupal Usability Meeting 2026-03-13. The link to the recording: https://www.youtube.com/watch?v=YYERRtLcNe4 . The attendees at the usability meeting were @pallavi singh3013, @rkoller, @simohell, and @worldlinemine.

While testing MR5696 we've identified a few noteworthy details:

  • The h1 is using a sort of lengthy conversational styled micro copy. It is rather hard to skim, because the information that is providing the sole context about which revision the node is getting reverted to, is at the very end of the heading. Strictly speaking it is not even communicated at all which node the confirmation page is about. In addition to that, even in the context of the revision list page with the other revisions around it is difficult to keep the different revisions apart. The sole indicators is the current revision as the anchor and the position of the revisions among each other. Stand alone, within the heading, the date and time is not that helpful to clearly and easily identify a revision, in particular for people with a small working memory - it is a rather abstract piece of information without any cue about the revision of which node. In summary this does not meet WCAG2.2 SC2.4.2 . The problem can be easily demonstrated by opening revert revision confirmation pages for several different nodes each in their own tab, you won't be able to tell the difference at all switching between those tabs.
  • Comparing the confirmation page between claro and default_admin on a mobile device, there is another detail to note (first claro then default admin):
    the revert a revision confirmation form in claro on a mobile device
    the revert a revision confirmation form in default admin on a mobile device
    In claro the h1 is wrapped and the user is able to read the entire lengthy heading and deviate the context aka the node the revert is about from the breadcrumb. In contrast in default_admin the h1 remains a single line and the heading is getting truncated. That way the user is only presented with "Are you sure you want to revert to the revi..." and there is no proper breadcrumb trail available, there is only a "Back to site" link. That way the confirmation page becomes completely generic, due to the fact you have no information about context at all, neither the node nor the revision. The truncation in default_admin already got covered in #3570087: Page Title Ellipsis Accessibility Issue, while the wording of the h1 could be covered within this issue.
  • While the heading provides the gist about a revert of a revision, the body of the confirmation form should provide some more context and the situational awareness to the user. At the moment the only information available is the one buried inside the h1. A date and time stamp, something like Fri, 17 Apr 2026 - 14:14, alone is also not a very helpful cue to contextualize the reverted revision. It would be helpful to have in support of the heading in the body of the confirmation form some more verbose information, "where i am at" aka what is the revision I am reverting from and "where I will be going to" aka what is the revision I reverting to.
  • It is not clear at all what the Revision moderation state is referring to. During the meeting we've assumed it would reflect the state the revision "is coming from" and the select is setting the state the new revision is going to. While some of the attendees of the lean coffee table from the Drupal User Group Munich considered it the state of the new revision, and that its state could be altered with the select. @acbramley later confirmed that the attendees of the DUCMUC were right. But it illustrates that the microcopy and what it refers to is not clear at all but rather ambiguous and potentially misleading.

I've continued the exploration the next few days to gain a better understanding of the content moderation problem space in general as it turned out we had quite a few misconceptions during the first meeting and it doesn't looked like it made sense to discuss the confirmation page in isolation. Therefore we've revisited this issue for a second time at #3578994: Drupal Usability Meeting 2026-03-20. The link to the recording: https://www.youtube.com/watch?v=JKzih7-X6CU . The attendees at the usability meeting were @benjifisher, @pallavi singh3013, @rkoller, @simohell, @the_g_bomb, and @worldlinemine.

At first we've discussed the content moderation module in general to get an idea about the bigger picture. #3578994-12: Drupal Usability Meeting 2026-03-20 contains a section with the rest of the problems we ran into that are out of the scope for this issue, but we will create dedicated issues for them soon.

In regard to the germinal question about the ability to set the moderation state within the confirmation step, one idea was to drop the informational Revision moderation state and only keep the select element - the default state could be the state the reverted revision will have while the user still has the ability to modify that default state to one of the states available to them. @benjifisher suggested another option during the meeting he already voiced in #2906568-118: Provide users with the option to select an appropriate moderation state when reverting to a previous revision. We've discussed the germinal question another time in today's meeting #3584127: Drupal Usability Meeting 2026-04-17 - Benji will add a summary with the outcome of that discussion.

acbramley’s picture

Thanks a lot @rkoller

Regarding the h1 - that is not being changed in this issue, if we want to discuss changes to that I think it should be done in a separate issue?

opi’s picture

StatusFileSize
new22.86 KB

For those like me who are struggling to apply patch on a regular Drupal 11.3.9, here is a modified patch from MR 5696. The Only changed part is about content_moderation.services.yml which is different between Drupal 11.x and the "main" branch (base branch for the MR).

nitinkumar_7’s picture

StatusFileSize
new22.86 KB

Here's an updated patch. Adds a StateRevertValidation service with new per-state revert permissions (revert {workflow} revisions to {state}) independent of transition permissions — sites are unaffected until permissions are explicitly granted. The moderation state select is injected into the node revision revert form via the existing route subscriber. Currently Node-only with a @todo for #2350939. Needs usability review on the dropdown label/description text.