Needs work
Project:
Drupal core
Version:
main
Component:
content_moderation.module
Priority:
Major
Category:
Task
Assigned:
Unassigned
Issue tags:
Reporter:
Created:
9 Sep 2016 at 07:25 UTC
Updated:
2 Feb 2026 at 11:19 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #2
naveenvalechaComment #3
dawehnerHere is a module which provides such an action plugin.
It turns out checking access is not entirely trivial.
Let's assume the following situation:
* We have a published version
* We have a new draft in review
* The user wants to archive a content on
/admin/content*
\Drupal\workbench_moderation\Plugin\Validation\Constraint\ModerationStateValidator::validatefor example will check for review -> archive* The user though archived the published content, at least according to their user interaction.
Comment #4
dawehnerJust pushed a more complete work on https://github.com/dawehner/workbench_moderation_actions
Comment #5
naveenvalechaI rewritten it the completely written for content_moderartion after forking from daniel repo
Here's the repo https://github.com/naveenvalecha/content_moderation_actions
Comment #7
timmillwoodI think this would be good to get into Content Moderation rather than being a separate module.
Comment #8
dawehnerI totally agree, for core an additional module doesn't make sense.
Comment #10
xjmThe bug in the IS doesn't seem to happen (although "complains" is not particularly specific). :P Assuming the original bug has been fixed in the past year, is this just a feature request now?
Comment #11
timmillwoodWhen running through the steps in the IS I get a warning "Article content items were skipped as they are under moderation and may not be directly published.".
So yes, this is not technically a bug, because we mitigate an issue by displaying a warning, but it would be nice to provide actions for bulk moderating content.
Comment #12
xjmAh, the reason I did not run into this in the first place is that the STR were incomplete. Updating the IS with steps that will actually reproduce it.
However, I don't think this is just a feature request; the issue described in the summary is a usability issue.
Comment #13
xjmComment #14
jibranComment #15
jibranWhen content moderation is enabled site admin can't perform bulk update for publishing/unpublishing the nodes so I think this qualifies as a major bug. Also, I'm working on the patch.
Comment #16
jibranThis is just action plugin and deriver. Need tests and schema. If this goes in before #2902187: Provide a way for users to moderate content then we can update the new view there if not then we can update that view here. I think we don't need operations.
Comment #17
jibranCorrected some spelling mistakes and text.
Comment #18
sam152 commentedNot manually tested, just code review right now.
This isn't used anywhere?
An empty array is already falsey.
I thought we had a fancy new service for this ;)
This should be removed. moderation_state field already sets it correctly.
What about simply Workflow::load. Can remove dependency on entity type manager.
Is this missing cacheability metadata?
We trust parent:: to return $this->derivatives for us? Seems like a break in encapsulation.
Comment #19
sam152 commentedComment #20
jibranFixed some trivial fails also addressed #18. Thanks for the review @Sam152.
\Drupal\block_content\Plugin\Derivative\BlockContent::getDerivativeDefinitions.Do we want to save a new action when new moderations state is created?
Do we want to delete the corresponding action when the moderations state is deleted?
Comment #22
jibranPatch provides a deriver to create the action for all the states in all the workflow for all the moderated entities. Core has no action plugin which uses deriver. Only flag has one in contrib. The only dynamic plugin in core is role add/remove action but it configures action plugin instance via settings form using action configuration settings instead of deriver.
Now, I'm confused which one should I implement core way i.e. role add/remove role or flag way i.e. flag/unflag entity.
Personally, I think we should create deriver to add the entity type id to action plugin and use configuration settings form to create it per state per workflow but that means we have to use ajax to update the configuration settings form i.e. first select workflow if there are more than one for this entity type and then ajax will load the states in that workflow.
Comment #23
Chris Gillis commentedJibran, a quick glance at the code looks like you are hardcoding a set of transitions/states? It's supposed to be dynamic right? Like people can create a workflow with whatever states they like? Have I misunderstood your patch?
Comment #24
jibranPatch is incomplete. Right now, it is adding a functionality to create actions for all the states in all workflows. It is incomplete because we are not creating the instances of those actions. I only added the instances for default states in the editorial workflow.
Either we can add hook_workflow_insert/update to add/remove the instances of the actions or we can add/remove action instances in
\Drupal\workflows\Form\WorkflowStateEditForm::saveand\Drupal\workflows\Form\WorkflowStateDeleteForm::submitFormComment #25
Chris Gillis commentedWhat about using a deriver as in #5?
Comment #26
jibranYes, this patch has a deriver like #5 which is making all the action plugin instances available for all the states in all the workflows. I was talking about actually making those action plugin instances available for use.
Comment #27
suranga.gamage commentedLooking at this it would seem that the core implementation of the bulk forms bypasses the ActionPluginmanager completely which would bloat the implementation of this feature in a pretty bad way.
This core patch: https://www.drupal.org/node/2912390
Should make it a lot simpler (by allowing the already existing transition entities to generate plugins directly without a need to add extra "meta" config entities to muddy the water.
At the cost of being slightly more tied to following up any advances the core action interface makes.
Proposed solution
- Add derived plugins based on the config directly (see core patch) Which then plays nice with the deriver from #5
Optionally nice to have: Should it be possible to define which transitions appear in the bulk actions etc via the transition config? From a workflow perspective it might be odd to have them blanket allowed.
Comment #28
jibranI had a chat with @tim.plunkett and @Sam152 at DC Vianna we agreed on following things.
typeproperty to the plugin defination using deriver.Comment #29
jibranDone.
Done.
Comment #31
jibranDone.
Comment #32
jibranRemoved default action instances we can add them in follow-up. This should be green.
Comment #33
jibranMoving to optional should also work.
Comment #36
jibranNR for #32.
Comment #37
jibranInterdiff is based on #32.
Comment #39
jibranAdded unit test for the execute and access methods. This is ready for review.
Comment #40
tim.plunkettReviewing per @jibran's request
nit:
static::classworks too$this->t()
This should not use setValue(), but set(). Putting raw unsanitized user input into form values is bad
Nit: It's my understanding that t() is no longer used like this in tests.
Why the ->trustData() calls in some and not others? If they are necessary, they deserve a comment.
Nit: Ideally this would be using Prophecy based mocks
Comment #41
jibranThanks, for the review @tim.plunkett.
t.trustData.Comment #42
sam152 commentedSome general things I noticed testing this manually:
Code review as follows:
Is this a CS violation?
If we're manually using the first option as a "default" in the form, is there no way to just return this in
defaultConfigurationso this is setup for us, or should that always be static values?Maybe NULL is a better way to indicate "missing" than empty string?
If we have
$state_optionswhy not make the other variable$workflow_optionsContent moderation workflows have 2 required states, should never be empty.
Should this be "State", we're selecting just one right?
Trailing newline.
If we're checking if
$objectis a valid entity, why not go all the way and checkinstanceof ContentEntityInterface?The workflow is valid, just not necessarily for this specific entity right?
I don't think the entity is a dependency here, no change in the entity itself can impact if a workflow is assigned to it or not right? That config exists in the workflow only.
If the first part is based on
$workflow, shouldn't the else be based on the same thing?Can we describe what is taking place in a more formal way or not at all?
This seems like an access bypass if it doesn't take
\Drupal\content_moderation\Permissions::transitionPermissionsinto consideration?Other doc comments reference moderation_state.
Missing one of the params.
Is our dependency on content_moderation implicit?
Should we just fold the coverage for these two things into the same test?
Is this comment needed, already implied by
drupalPostForm.If the block above lands on the configuration page already, can't we just
submitForm?XYZ?
Don't the functional tests already blow up if the schema isn't right, or does this make assertions beyond that?
Unless it's important this method is called once, this is testing the implementation.
Where is the actual testing that "foobar" is assigned as the moderation state?
Wow, this is a long test. Can we break it up so the bits which are repetitive use a dataProvider instead?
Again, why is it important this is only called once?
Comment #43
jibranThanks, for the excellent review @Sam152.
\Drupal\system\Plugin\views\field\BulkForm::viewsFormSubmit. We can create a follow-up to have a larger discussion.defaultConfigurationso NULL is fine in this case.canTransitionTo. Fixed it.Comment #44
tim.plunkett#2911806: Remove unnecessary Crypt::hashBase64() call from Action UI went in, so you can stop using Crypt::hashBase64() now!
Comment #45
jibranFixed #44.
Sorry, I didn't get your point here. Can you please explain?
Fixed.
Done.
Can you please explain this as well?
Comment #47
sam152 commentedIn these examples, you're creating a mock node and saying
idwill only ever be called one time. To actually deliver the feature, why is it important id isn't called twice? Is there some implementation or additional feature that exists which is a) completely valid and b) calls id a second time? I would probably think so. Hence these kind of assertions aren't actually testing any kind of outcome, but are just duplicating details of the authors implementation in the test. So the code has no more actual test coverage but is now more brittle and harder to maintain.Comment #48
jibranThanks, for the explanation @Sam152. Addressed #47.
Comment #49
jibranThis is blocked by #2816861: Action configuration form does not support #ajax
Comment #50
timmillwood@jibran - can you provide some more information on why this is blocked by #2816861: Action configuration form does not support #ajax? As it is blocked by that issue shouldn't we have tests failing?
Comment #51
jibranI thought title gave it away. :P
The action configuration form in this issue is using AJAX and it is not saving the action configurations because of #2816861: Action configuration form does not support #ajax.
I have worked on some failing tests which I can upload here.
Comment #52
timmillwoodAwesome, thanks!
Comment #53
jibranHere you go.
Comment #54
jibran:/
Comment #56
jibranFixed #54.
Comment #58
larowlanBlocker is in
Comment #59
jibranFixed the phpcs issue.
Comment #60
rosk0Comment #61
dpiManual UI review,
And some integrity issues:
Comment #62
jibranThanks, for the review.
I think it is a good idea to move default value above. Also, I added an empty value check for state select.
Very good point. Fixed it.
We decided in #28 to move the workflow to the action configuration instead of keeping it in deriver. Yes, you can navigate to add action link but as you said so you can't save the add action form anyway so IMO it's fine. We can add warning message with
admin/config/workflow/workflowslink when there is no workflow on config form.OTOH if deriver removes the plugin once workflow doesn't apply anymore then we'll start getting PluginNotFoundException. We can properly fix it after #2873287: Dispatch events for changing content moderation states.
See #28.3. Unfortunately, we can't do that until #2873287: Dispatch events for changing content moderation states.
As discussed above we can add warning message with a link to update the workflow. We can also hide the state select if workflow select has no valid option.
Fixed it.
Comment #63
larowlanshould this add the workflow_list cache tag?
Agree
The config dependency API should handle this, which indicates we need to add integration and tests for that
That is a major UX issue, I think we should resolve that too
Comment #64
jibranThis interdiff solve the following problems:
Now we have the following problem:
PluginNotFoundException. This means we need an event to be fired when a workflow is removed from entity type. AFAICS #2873287: Dispatch events for changing content moderation states doesn't handle that.There are multiple ways to change the workflow configuration:
At DrupalCon Vienna @tim.plunkett, @Sam152 and I agreed that we aren't going to support the 3rd option. If you are messing with raw config then you have to make sure you update the action plugin instances as well. #2873287: Dispatch events for changing content moderation states only handles the 1st option and for the 2nd option, we have to implement config import event.
It all feels like out of scope here so we decided to move it to followup in #28.3. If we want to proceed with that in this issue then #2873287: Dispatch events for changing content moderation states becomes a hard blocker for this issue.
Comment #65
jibranAs @Sam152 corrected me today, #2873287: Dispatch events for changing content moderation states deals with content moderation state change not with workflow entity settings change.
To update and delete of the action instance should be done:
hook_workflow_insert,hook_workflow_updateandhook_workflow_deletewhen a workflow entity create/update/delete operation happens.\Drupal\Core\Config\ConfigEvents::IMPORTevent when workflow entity is updated during config import.\Drupal\Core\Config\ConfigEvents::SAVEand\Drupal\Core\Config\ConfigEvents::DELETEwhen raw config is changed duringhook_update_N.We also need functional tests for all the above cases and some kind of user notifications to inform about action instance changes. #2902187: Provide a way for users to moderate content is also fixed so we can update the
moderated_contentadmin view with state change actions added here.I have updated the IS with remaining tasks also re-titled and re-categorized the issue because we are adding whole new functionality and not fixing any buy.
I'm not actively working on this issue anymore so feel free to pick this up.
Comment #66
sam152 commentedI wonder if a fallback plugin in the action system could allow us to side-step the complicated things in #65. Views and blocks do this already I think, the downside being it would create orphan action config entities.
Another possible alternative would be to try make the plugins work without requiring a config entity to exist. That is, all the context required to execute the action would be computed as part of a derivative and the config entity wouldn't be required. That could just shift the problem elsewhere, other systems like views wouldn't be able to add actions as config dependencies.
Another option would possibly be returning TRUE in
workflowStateHasDataandworkflowHasDataif action config entities exist for a given state. That would require the user go clean up the config entities first, much like they have to clean up their content before deleting a state. We would need to do some follow up to signal to users exactly how to resolve these lingering data issues.Comment #67
sam152 commentedAnother option I've thought of (probably like this one the most so far) is have a single action plugin called "Change moderation state". We do what
CancelUserand set a customconfirm_form_route_name. Then we simply present the user a list of states they are able to use, then have them hit confirm.This could simplify some of the access stuff and make the dependency/orphan thing a non-issue.
Comment #68
jibran#67 sounds like https://www.drupal.org/project/bulk_update_fields in core.
Comment #69
sam152 commentedI had https://www.drupal.org/project/views_bulk_edit in mind, but yeah, it would borrow heavily from the concepts in those, but obviously heavily reduced in scope and moderation specific.
Comment #71
jibran@Sam152 graciously explained all 5 options.
We also have broken ER selection plugin. I like this one. As @Sam152 said, It avoids fatal errors. And it allows the user to fix it or remove it. It is not a new pattern we have been doing things this way since we had ctools plugins in contrib.
I have no idea how this can be built neither does @Sam152.
I don't like this at all. Historically, term data is used for content and action is a config entity and blocking the changes of a config entity on other config entity doesn't seem right.
This makes life so much easier but it would be UX nightmare. It will be hugely different from what we have right now in HEAD, select content select action(publish/unpublish) apply.
Given the scope of changes I explained in #65 I re-categorized the issue as a task but it's a regression nonetheless so I believe it should be fixed in core.
Comment #73
miroslavbanov commented#62 still works quite well on Drupal 8.5.6.
One problem is that the author, and revision dates are not set. Also, since we are creating new revisions, it could be useful to generate a log message for example:
Bulk moderation change: Published -> Archived. See for example how it is done inNodeRevisionRevertForm::submitForm.Comment #74
david4lim commentedAccording with @miroslavbanov, here is a patch for creating a new revision with its own message when the action is executed.
Comment #75
miroslavbanov commented@david4lim
Looking at the last patch. Overall looks good but there are some issues.
There is no interdiff. Please make interdiff to help reviewers.
You are not assuming that setChangedTime method is available, but you are assuming that getChangedTime is available. That's strange.
You should just use the result of the
time()call for both create and update times, and get rid of getChangedTime call. It's just good design to make things more simple, rather than convoluted or rely on side effects.Comment #76
awm commentedworking with @david4lim
#74 works fine but not if the selected content is a translation. if the selected content is a translation then the execute function only apply the action on the original entity and the translation remain intact.
attaching a patch and an interdiff
Comment #77
awm commentedComment #78
awm commentedapologies forgot a return statement in #76
Comment #79
awm commentedI am still seeing issues when trying to bulk publish/archive a node with translations. if you have a node and 2 translations and attempt to bulk publish all of them, the moderation state gets updated but the status for one translation fails to get updated. See screenshot: https://i.imgur.com/K8b4Joe.png
Comment #80
awm commentedupdating patch to address translation issues.
Comment #81
awm commentedComment #82
awm commentedComment #83
awm commentedComment #84
awm commentedComment #85
awm commentedComment #87
johnchqueFixing most tests, not sure about the entityTypeManager when create a new ModerationStateChange object in tests. Let's see how it fails.
Comment #89
johnchqueOops, didn't find this before. :)
Comment #91
awm commentedThere still remain some workflow design issues that need to be worked out. As of now, on the /admin/content page:
So far only #2 seems a bit confusing.
Comment #92
johnchqueAgree, #2 is confusing. What would it be the best way to indicate that a node has been changed to Draft? Do we have to show an indication in admin/content that the node is a Draft even though we have that info already in admin/content/moderated?
Comment #93
johnchqueWas discussing this with @Berdir, wondering if would be good to add bulk operations by default in the moderated content view. Then the second point on #91 would be clearer since we would have it in a separated view.
Comment #94
joelpittet@yongt9412 re #93That would be good and looks to be part of the Issue summary proposal.
If possible though to keep this issue reviewable that may be better suited as a follow-up issue, what do you think?
Comment #95
coffeymachine commentedWhile we're waiting on this to make it into Drupal 8 core, as a workaround, you can use the Views Bulk Edit module along with VBO to bulk edit the entities moderation state.
Comment #96
askibinski commentedFor anyone experimenting with this, here is a reroll of #89 for 8.6.x.
Comment #98
joseph.olstadbeen searching for while for a solution to this, I tried the above patch (the latest), it does work well once it is configured, requires adding a views bulk operation field to the admin/content/moderated view (make sure to remove the 'node bulk operation' field if there is one. Our client has a desire for a solution on the admin/content page rather than the admin/content/moderated page so while this core patch does work and provide a working solution our client wants something easier to use and that is what they will get (because that's what they want).
We're going to do a custom solution (should be easy enough to do).
Otherwise, I did try some other possible solutions, there is a contrib module that appears to do this.
https://www.drupal.org/project/workbench_moderation_actions/releases/8.x...
However (maybe I did not configure it correctly?) I tried it and got an error:
No access to execute Set Content as Draft on the Content
or
No access to execute Set Content as Archived on the Content
Then I tried views_bulk_edit but it seemed to conflict with our configuration.
So, overall this core patch seemed like the best way to go for a non custom solution.
However my client wants a simpler interface on the admin/content view so I'm going to create a custom solution for that which will with one button click for unpublishing ALL revisions (in our case two step, to archived (for current revision) then to draft so that no published revisions remain.
and a button click to set the latest revision to 'published. two options is all my client wants.
And we have a javascript solution for a confirmation.I will share if there is time.
Comment #99
johnpitcairn commentedI feel your pain...
Comment #100
joseph.olstadOk, massive code blitz, here is what our client wants and what I delivered to them.
yml file: cool_admin/config/optional/system.action.unpublish_current.yml
yml file: cool_admin/config/schema/cool_admin.schema.yml
yml file: cool_admin/config/optional/system.action.publish_latest.yml
Filename cool_admin/src/AdminModerate.php
?>
file: cool_admin/src/Plugin/Action/PublishLatestRevisionAction.php
file: cool_admin/src/Plugin/Action/UnpublishCurrentRevisionAction.php
file: cool_admin/cool_admin.info.yml
Now that wasn't so hard now was it ?
Comment #101
joseph.olstadto make my example work in drupal core would have to find out (add logic to ) dynamically determine which moderation states set the current revision state. In our case we have archived, published and draft but others might have needs-review , draft and published, the state that sets the current revision state is configurable , so to make the code work the logic would have to find that. there's only two states that can ever do this, one for publish (obvious) and the other (not so obvious but could make some logic to find out)
These are really the only two things anyone really gives a hoot about for bulk operation, the rest is done when editing a node. like really think about the workflow and process, it's not that complicated, the essentials is they need some way to take something or a bunch of items out of publication quickly or to vice versa publish something easily in a way that makes sense to most people, that's why we put this on the admin/content page .
Comment #102
joseph.olstad*** EDIT *** confirmed this patch reroll is needed for 8.7.x
patch 96 needed a reroll for 8.7.x
Comment #104
anavarrePatch applies correctly but it doesn't seem to fix the issue for me FYI.
Comment #105
uxmfdesign commentedJust tried this patch. We had the same result. It applies but does not appear to resolve any issues. Are there any thoughts on next steps or workarounds for this?
Comment #106
mirsoft commentedI was successful by applying the code snippet in #100, which does what I expected. Just make sure the files are placed into the proper folders and the class in src folder is named "AdminModeration.php" and the use statement is removed (perform a basic code check on that). Then enable the module and you will see two new options in the Actions window: "Publish latest revision" and "Unpublish current revision". Both are performing the job as expected with content moderation enabled. No additional patch needed.
Perhaps it makes sense to make a contrib module from that in the meantime?
Comment #107
joseph.olstadhi @mirsoft, yes I agree, I will take that code and make a contrib module out of it.
What should I call the module ? moderated_content_bulk_publishing? (Moderated content bulk publishing)?
once I decide on a name, I can create the project and publish a release, would be nice as a contrib module.
NOTE: I have an updated version of that code, been hardenned with production use and feedback improving it. Just need to decide on a contrib module name.
Comment #108
mirsoft commentedThanks @joseph.olstad, yes, the name looks good to me.
Comment #109
amanire commented@joseph.olstad I also have a use case for this functionality and would prefer to use a contrib module. You could just drop "ing" from the module name to make it (slightly) shorter, i.e.
moderated_content_bulk_publish. Hoping to see a sandbox soon!Comment #110
joseph.olstadOk I have created the project, will soon (maybe this weekend) add the source code and make a build.
https://www.drupal.org/project/moderated_content_bulk_publish
If you want to help maintain and develop this please let me know and I will grant the permissions for this.
Comment #111
amanire commentedAwesome @joseph.olstad! Please push the source code at your earliest convenience. I tried the example code in #100 but it does not check if the state transitions are valid so an entity can be published from any moderation state. I'm wondering if the revisions in
moderated_content_bulk_publishinclude that check and would be happy to move the discussion over there.Comment #112
joseph.olstad@amanire and @mirsoft, I have pushed the source code and published a release of the new module.
I have not tried this new module yet, but it is based off of a working custom module.
It 'should' work 'knock on wood'.
If you want to join in and make improvements or help get it ready for a beta then RC (release candidate) , please let me know and I will grant co-maintainer priviledges.
Thanks,
Joseph.Olstad
Comment #113
nateb commented@joseph.olstad, thank you!
I'd been trying for hours to set and save a moderation state on unpublished content after installing Content Moderation on an existing site. I was not about to manually set 662 unpublished nodes to Draft. While the system would display Draft as a moderation state for unpublished content in lists, it was superficial. I couldn't track moderated content anywhere. After running Unpublish current revision now I can actually see all the unpublished the admin/content/moderated.
Comment #114
joseph.olstadHi @nateB , glad you like my module! I hope others find it useful as well.
You can thank northern taxpayers for this module, they paid for it. Now due to open source, we have residual value from what we spend on taxes!
Comment #115
alisonA detail I didn't realize (may or may not be helpful to you, @anavarre + @uxmfdesign): After applying the patch, you have to add the actual actions -- i.e.
I thought "dynamically provide action plugins" mean that the actual per-moderation-transition actions would automatically exist after I patch the module, but I guess what it actually meant was that (from the site builder's perspective) new "types of advanced actions" become available to me, for each moderation transition.
(...or, I think that's what it actually meant! 😁 )
Comment #116
alison...I'm probably missing something, but, the new "moderation state transition) advanced actions I created are available to me on /admin/content, and they seem to work totally fine.......? (I may have misunderstood other commenters, but I thought I'd only be able to use these new advanced actions on /admin/content/moderated )
Comment #117
joseph.olstadJust to clarify for others, if you prefer you can just use the contrib module I created instead of patching. The contrib module adds a views bulk operation plugin on the admin/content page for 'publish current revision' and 'unpublish current revision' in bulk.
Comment #118
rob230 commentedIs there some way the module could do it dynamically? We have a lot of transitions between states other than just publish/unpublish, so it seems unwieldy if we would have to create an Action plugin for all of them.
Comment #119
joseph.olstad@Rob230 , please submit a patch to moderated_content_bulk_publish :
The reason for the plugin is because you can have several revisions in different moderation states. The reason for these plugins is if you want to ensure that the current revision is placed into the desired state as indicated by the label of the plugin.
So most use cases someone would want to publish the newest revision even if a previous revision is already published
same thing for unpublish, to make sure that all revisions are unpublished the plugin does just that and puts the current revision into draft state ensuring that no other revision is published. The plugin allows bulk.
So, if you wanted to make a bulk operations plugin for lets say put the current revision into needs review, you could create a patch with that functionality and push it to the module.
The module also provides bulk sticky/pin and bulk unsticky/unpin
You could create a plugin that bulk sets current revision to a configured/desired state. The tricky part is, anyone can define their own moderation states, so if you want some special state it would have to be configurable so you'd have to also add a configuration form and retrieve that configuration and put it into the plugin, enable/disable configuration for the custom moderation state(s).
Please submit the patch(es) to the project and I will review.
Comment #120
rob230 commentedAren't states and transitions quite specific though? We have things like "submit for approval", "reset to draft" and "stand down". I I don't think they are very relevant to other people. Surely there needs to be a solution which will work for any arbitrary state transition that is created in content moderation workflow, as they can be created in the UI at any time.
Comment #121
sastha commented@alisonjo315 your comment #115 is a great help. Thanks!
Comment #122
alison@sastha Oh I'm so glad, you're welcome!
Comment #124
thejimbirch commentedThe patch in #102 applies cleanly to drupal/core (8.7.10)
With the Actions module enabled, new options appear in the advanced action list at /configuration/system/actions:
The add action form lists available workflows:
Once saved, the actions appear in the list and function as expected:
I'd mark RTBC, but the patch has failing tests that I assume need to be addressed.
Comment #125
texas-bronius commentedWOW, thank you @thejimbirch! Just what I had hoped to see here. My first comment here overlapped with your excellent screenshots.
[edit]
Ok yes patch works as advertised: I had to enable core module Action to get it going. I had hope this would expose the same set of Advanced Actions to Views Bulk Operations, but it does not. Is there something about either this patch or its newly created contrib module that would help expose its generated Advanced Actions to VBO? In VBO, I can add the root
ModerationStateChangeaction, and it works as it should, but it's not as seamless an end user experience as the Advanced Actions provided by this patch.When I use core Bulk Actions in my custom View, my new Advanced Action does work perfectly.
[/edit]
Comment #126
texas-bronius commentedNow that (with this patch in #112) we can publish/unpublish and even set other moderation state changes, I found the need to change the patched Action to call
$entity->validate()like:./core/modules/content_moderation/src/Plugin/Action/ModerationStateChange.phpbecomes
This allows content moderation state change-blocking entity validations (like fields required for publish but found empty or other entity constraints) to be considered before, say, pushing this entity live to the world.
Comment #127
texas-bronius commentedComment #128
texas-bronius commentedTwo things:
While I've greatly enjoyed the use of patch in #102, looks like #85 introduced some failing tests due to
\Drupal::entityTypeManager()which I think should be easy enough to mock up swap out.. then we can know if these additional patches are helping or hurting! :DAnd the action provided by this patch needs to act on the latest revision of the translation of the entity passed into it. I've updated the patch with the following to do just that (see interdiff). Before it was loading latest revision and then loading translation, not getting latest revision of the translation.
Comment #129
texas-bronius commentedI'm sorry for the noise. Corrected patch here. Note: Still module unit tests fail.
Comment #130
malcomio commentedComment #131
vuilI set the issue status back to Needs work because of #129:
Thank you!
Comment #132
matroskeenComment #134
damienmckennaThe last test failures are weird because it's saying that the underlying Selenium system from Drupal\FunctionalJavascriptTests\WebDriverTestBase::assertSession() doesn't support status codes. But it should. I'm rerunning the tests.
Comment #137
matroskeenAs I understand, it's not weird due to #2827014: Throw an exception when testing status code or response headers in functional JavaScript tests.
I'm adding a new patch trying to remove
statusCodeEquals().Comment #138
matroskeenComment #140
matroskeenComment #142
matroskeenComment #144
matroskeenComment #146
matroskeenComment #147
matroskeenComment #149
matroskeenComment #150
matroskeenComment #151
matroskeenYey! Finally, ready for the review!
Sorry for spam :)
Comment #152
amanire commentedI went through the following steps in the UI with a fresh Drupal install:
1. Enabled the Editorial workflow for the Article content type as specified in the issue description.
2. Applied the patch in #148 and enabled the core Action module.
3. Added "Change moderation state to Published" and "Change moderation state to Archived" advanced actions as described in #124
4. On the content administration view, successfully published several unpublished articles using the new "Change moderation state to Published" bulk action.
5. On the content administration view, successfully unpublished several published articles using the new "Change moderation state to Archived" bulk action.
Additionally, after re-reading the issue description, I checked the moderated_content admin view and:
1. Added the Node operations bulk form field to the view (with the previously added content moderation actions).
2. Successfully applied the "Change moderation state to Published" to bulk publish draft and archived articles.
3. Added the "Change moderation state to Draft" advanced action and successfully applied the "Change moderation state to Draft" to bulk update Archived articles. This doesn't seem terribly important, but it's a conceivable use case.
Note: it's probably worth mentioning (and restating) a few odd scenarios:
- You would most likely never want to enable a bulk moderation state action for changing nodes to a draft state, since this will create an empty new draft revision while preserving the published revision. This could confuse content administrators if they were expecting the node to become unpublished and revert to a draft state.
- "Change moderation state to Archived" is useful for archiving published nodes on
admin/contentbut is unhelpful on themoderated_contentview since it does not display nodes with latest revision in the published state. The default workflow does not allow transitions from draft to archived and results in a "No access to execute Change moderation state to Published message.I haven't had a chance to check actions on translated content yet.
Anyway, the unit tests are passing. Nice work!
Comment #153
hmdnawaz commentedI applied the patch in #148.
I have 3 states. draft, published and unpublished.
I can bulk update state of the nodes from draft to published.
But I can't change the state of the published nodes.
Comment #154
cayriawill commented@hmdnawaz Have you applied the appropriate transitions from published? Transition states on how I have them setup.
I have tested this on 8.7.11 test site and following comment #124 with the patch from #149. It is working, will test later with newer builds as well.
Comment #155
sam152 commentedThere was some feedback in #65 onward that needs to be addressed with the entire deriver approach that has been implemented.
The tl;dr is, a dependency on the workflow is insufficient for anything that deals with states, since states can be deleted without deleting the whole workflow. That is, there is no way to add a dependency on an individual workflow state and have that handled throughout the lifecycle of the workflow config.
The only viable implementation I can think of would be a single plugin called "Moderate content", which would work something like:
Comment #156
johnpitcairn commented@Sam152: A single plugin actually sounds more useful and flexible to me. Presumably that would mean you could, for example, archive some content and publish other content in the same user action.
From a content admin's point of view that's fewer steps to screw up, they can start by selecting all the things that need a moderation change, and only need to make that selection once.
A dynamic plugin per state change has the slight gotcha of the user being able to select a group of content for which no single state change is globally applicable, right? So you wind up having to make multiple selections and process them separately per starting state.
One use case to support this would be when a the wrong content has gotten published. The content admin can select that, also select the correct but unpublished content, and fix it in one operation.
A single plugin would also result in less clutter in the operations list for sites with many possible workflow states.
Comment #157
azinck commentedIs there any chance of being able to act on non-latest revisions? I haven't looked at the API, so perhaps there's a limitation there, but it strikes me that it would be useful. We, for example, have a need to remind content editors to review their published content, even while they might have forward draft revisions in existence. So we'd have this situation:
* Revision 2: Draft
* Revision 1: Published
And we want to push revision 1 to "Published, needs review" to remind the person who owns the content that they need to maintain their content (we require they update it every 6 months) or else we're going to unpublish it. Unfortunately there's currently no way for us to do that since these plugins can only operate on the latest revision.
Comment #159
gurpreet.kaur commentedHi,
I am using Drupal Version 8.7.12, I tried with #102 and #116. But there is no luck to fix this issue. I am looking for assistance from the one who has fixed issue for publish/unpublish action plugin for every moderation state change.
Comment #160
naveenvalecha@gurpreet.kaur
We're using the https://github.com/naveenvalecha/content_moderation_actions on a client project.
Comment #161
joseph.olstad@naveenvalecha, thanks, I will look review your module and look into perhaps bringing some of your ideas into moderated_content_bulk_publish
Comment #162
bernardm28 commentedAfter adding patch #149 and following the instructions on #124 and #152.
I found that although it worked. It only work if my user was the last one to save the node revision. As an administrator, I had to go into each node and save it again. Currently, the patch seems to only let you change bulk actions if you are the last user who saves the lastest node revision.
It be great if as an administrator I can do bulk actions regardless.
Comment #163
joseph.olstadFYI, for those using moderated_content_bulk_publish, I just released
8.x-1.0-beta42.0.0 today, if you're on a previous beta I encourage you to upgrade.Comment #164
anruether@bernardm28 Are you sure your transition permissions are set properly?
I did not run into this issue (I tested with admin + user with and without bypass access permissions). For me everything works as expected, I tested patch #149 (explanation in #124 definitely helped :)
Comment #165
demma10 commentedWhen testing this patch I found that it didn't work with the latest stable version of Groups, more specifically with the groups content moderation integration. This patch uses the core access checks to do the permissions checks, which groups doesn't hook into anymore. If a user has permission to perform the status transition for content within their group but not the global (outside of groups) status transition permission the user receives an access denied.
To get this to work on my site I re-rolled the patch to instead use Content Moderation's StateTransitionValidation service to perform the permissions check. This service not only checks if the transition is valid, it also checks if the user account has permission to perform the transition. Groups overrides this service and adds its permissions checks in there. I went this route because it seemed more straightforward to update this patch rather than update groups to hook into core's hasPermission call. The downside of this approach is that the valid status transition check is now performed twice (though the code can be refactored to remove that second check).
Comment #166
azinck commentedThanks for this, Demma10. We ran into the same issue but hadn't yet had the chance to look deeply into the problem. Thanks for the fix. I'll test it and report back.
Comment #167
azinck commentedThe patch in 165 is malformed a bit (includes itself in the diff). Here's a fixed version.
Comment #169
azinck commentedI should add: in my initial testing #167 does appear to fix the problem.
Comment #170
demma10 commentedThanks so much for fixing the patch azinck. My git patching skills aren't the best (I think I ended up creating a patch of a patch).
Comment #171
ravi.shankar commentedFixed failed tests of patch #167.
Comment #173
ravi.shankar commentedTried to fix failed tests of patch #171.
Comment #174
ravi.shankar commentedTrying to fix tests.
Comment #175
ravi.shankar commentedMistakenly added a patches twice.
Comment #179
manuel.adanComment #181
manuel.adanReplaces calls to drupalPostForm deprecated in 9.2.x by submitForm in tests.
Comment #182
manuel.adanFiles added but not processed due page refresh
Comment #186
volegerThe wrong method was used to fill the Textarea field. Back to NR
Comment #187
azenned commentedHello,
First, Nice job .
But you need to reset storage latestRevisionIds cache after every save / before calling getLatestTranslationAffectedRevisionId
Something like this :
Comment #188
agentrickardWorks as expected, yet with or without the change in #187, if I use the transition on a node with Translations, I get:
Non-translatable fields can only be changed when updating the original language.Likely because the code is trying to update all translations separately?
Comment #191
AMDandy commentedWhat happened on this one? There was a lot of good work on it and it looks like it was solved but nothing has happened for about a year. Is there a different issue where this is actively being worked or is it just in limbo for now?
Comment #192
sershevchykI tried to use the patch on the project with Drupal 9.3.3 but I still can't unpublish node with bulk operation https://prnt.sc/26rft3y
Comment #193
abelassI finally understood that you have to create custom actions for your specific workflows in order to make it work.
Comment #194
anna d commentedThank you. #185 works for me (Drupal Core 9.3.9)
Some notice:
needs to install Views bulk operations module and add field: Global: Views bulk operations instead of Node bulk operations to admin/content view;
Then 'Change moderation state of Content' will be available;
Comment #197
yogeshmpawarComment #198
jonathan1055 commentedHi @yogeshmpawar, we already had a MR on this issue. Can you let us know what your new one is / why it is required. I can see you added 9.4 to the name. We can't tell if yours is just a "re-roll" or not, but the failed tests imply something else is required. Is MR176 now completely dead and should be closed?
Comment #199
yogeshmpawarHi @jonathan1055 - I have just created new branch from 9.4.x & cherry-picked previous commits because previous MR is not mergeable. If we fix & updated the target branch for https://git.drupalcode.org/project/drupal/-/merge_requests/176#note_7149 as this branch is almost 801 commits behind. so I will close my MR & will stick to https://git.drupalcode.org/project/drupal/-/merge_requests/176#note_7149.
I am trying to fix test failures.
Comment #200
xurizaemonAs Gitlab MR URLs are subject to unexpected changes (ref #3204538), I'm attaching a patch copy of MR !176 as at 5adde9 (comment #185).
Comment #201
johnpitcairn commented@xurizaemon: Thanks for that reference. I'm sure there are many devs aside from me who are entirely unaware that the advice around using MR urls to patch has changed.
Comment #202
pere orga@Anna D you don't need the Views bulk operations module or to alter the view at all. Just go to 'admin/config/system/actions' and create the actions (you may need to enable the Actions module, part of core)
Comment #204
yivanov commentedIs there a working version of a patch or MR for 9.3* version of Drupal core? The above recent patches doesn't seem to work.
Comment #205
joseph.olstad@yivanov you would probably have to port the latest MR to 9.3.* yourself.
Alternatively without patching you can try the moderated_content_bulk_publish module, check the README.txt for setup instructions.
Comment #206
azinck commentedI'm trying to understand why we have this check in \Drupal\content_moderation\Plugin\Action\ModerationStateChange::execute:
The code goes on to make edits to the revision regardless and, in my testing, a new revision ends up being implicitly created anyway (though I'll admit I didn't fully track down exactly where that's happening and I do have some custom code that may be complicating things). Indeed, I see it as fundamental to Drupal's philosophy that any edits should create a new revision, so that's appropriate here. Failing to call
$storage->createRevision($revision);seems to have the net effect thathook_ENTITY_TYPE_revision_createis never invoked, which is a very bad thing.Can others verify: if the transition you execute has the same state as both its origin and target do you still get a new revision? And in that instance is
hook_ENTITY_TYPE_revision_createnever invoked?If my observations are accurate I have 2 thoughts:
$storage->createRevision($revision);$storage->createRevision($revision);not being invoked by whatever code in core is implicitly deciding to create a new revision?Again, I'll admit I have enough customizations and complexity to my codebase that I may have some confounding factors, so I'm really curious to hear what others observe. I'll also see if I can test this in a simplified environment.
Comment #207
steyep commentedI have rerolled the patch from #102 for compatibility with Drupal 9 to care for the deprecated methods on the ModerationInformation interface.
Comment #208
steyep commentedI accidentally modified the core version of this issue when I submitted the patch. Reverting it to 9.5.x
Comment #211
tijsdeboeckPatch #208 is working great for us on production 👍
Comment #212
vuilThe patch #207 does not work for us, D9.4.x & PHP 7.4 with enabled "content moderation" and manually created "moderation states".
Comment #213
smustgrave commented@steyep for #207 how come you went back to #102 ?
Took a look at #200 and know that's a copy of the MR. Uploading for 10.1 after fixing deprecated functions. Lets start there.
Both PRs are outdated so they should be updated or closed to avoid confusion.
Functionally appears to be working but still needs a code review
Comment #215
smustgrave commentedFixed failing test.
Comment #216
_pratik_hi,
Patch #207 Not working for 9.5.1,
i am able to publish/unpublish specifically, but not able to do using bulk operations.
any working patch available ?
thanks
Comment #217
smustgrave commentedSelf review
For Drupal 10 applying the patch no longer seeing the options appear.
Issue summary may need to be updated for how to get to show.
Comment #218
klidifia commentedAny info regarding the issues in 9.5.1 / 10? Have only tested in 9.4.10 just now -- I applied #215 and it works, however:
I am not sure why there is an access check for the user having edit permissions for the node in question. The permission controlling this functionality should be solely the transition permissions within Content Moderation, like it is with the Content Moderation form that appears on a node (the user does not need edit permissions in order to use that form and transition the node using that form).
Comment #219
daniel kortePatch #218 not working for 9.5.4
Comment #220
beunerd commentedConfirming that the patch in #218 works on Drupal 9.5.8.
Not sure what might have been going on for @daniel-korte with 9.5.4.
Comment #221
daniel korteYup, disregard #219. It was a configuration issue on my end.
Comment #223
parashram commentedCan someone pls confirm if #218 works on Drupal 10 or any other update around for D10?
Comment #224
joseph.olstad@Parashram, the moderated_content_bulk_publish module allows similar functionality and is compatible with Drupal 10.
Brief explanation; if you have an out of the box workflow, use 2.0.x, however if you have a custom workflow try 3.0.x and adjust the configuration as necessary to fit your workflow.
Check the readme 2x and the project page for more information.
Comment #225
ec-adam commentedI can confirm that the patch in #218 works very well in Drupal 10.1.1, PHP 8.1.8 for standard state changes (did not test anything custom)!
I tested as an admin and as a user who does not have permission to change to the state they tried to bulk update to. The only thing that seemed a little strange (to me at least) was that the access denied message for the user without permissions came after the bulk operation looked like it had run. Seems like a little better UX to have the message appear before the running the operation (perhaps when the user clicks Apply on the moderation state action screen).
Comment #226
dewalt commentedComment #227
needs-review-queue-bot commentedThe Needs Review Queue Bot tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".
This does not mean that the patch 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.
Comment #228
pradhumanjain2311 commentedTry to fix deprecation errors.
Comment #229
smustgrave commentedAccording to the issue summary this still needs to be done or marked complete.
Comment #230
andypostIs there need to keep configurable actions in core? In context of #3427549: [PP-1] Move configurable user actions to the actions module
Comment #231
demonde commentedWill this issue be fixed anytime soon?
I have a request where I would like to use content moderation but I cannot do this without bulk operations in the content admin page.
Comment #234
joegraduateCreated a new MR based on 11.x using the patches from #218 and #228 to trigger GitLab CI checks and to make addressing the remaining tasks easier.
Leaving status as "Needs work" for #229.
Comment #235
altcom_neil commentedHi,
Thanks for everyone's work on this so far.
Just creating a fixed diff file from the last commit 2643761b so we can test this in multiple development environments (Drupal 10.3.5 / PHP 8.2.18).
Comment #236
gaurav_manerkar commentedHi,
I have moved update function
content_moderation_update_110001from.installfile tocontent_moderation.post_update.phpfile to prevent any conflict withhook_update_Nimplementations in future.Comment #238
jsutta commentedWhen I tried the version in the
2797583-11.xbranch, the action failed to run on translations with an error stating "This value should not be null." I looked into this and was able to find that the translation was being validated even though itsisValidationRequiredflag was FALSE. Given this, my assumption is that the validation check doesn't always need to be run. However, if this is incorrect please let me know and we can reassess.For now, I've pushed an update to the branch that checks the
isValidationRequiredflag and only performs the validation if it returns TRUE. Otherwise, it sets$violationsto0. It also checks to make sure$violationsis an instance ofEntityConstraintViolationListbefore attempting to use thecount()method.With this code in place, I'm able to use the action on both untranslated and translated nodes.
Comment #239
chamilsanjeewa commentedI attempted to apply the latest diff to Drupal 10.4, but it was unsuccessful. Do you have any plans to address this?
Comment #241
jsutta commented@chamilsabjeewa I had the same problem the other day. I created a 10.4.x branch and opened a merge request for it. You can get a working patch from there. It's working well for me so far on 10.4.2.
Comment #242
chamilsanjeewa commentedHi @jsutta
I am using Drupal 10.4.3, and this patch does not solve the problem.
Comment #243
grimreaperHi,
Uploading patch from MR https://git.drupalcode.org/project/drupal/-/merge_requests/11205 for Composer usage.
Thanks everyone for the work done here.
Comment #244
kgatzby commentedI tried using this most recent patch and this didn't work for me unfortunately.
Comment #245
kgatzby commented@grimreaper I tried using this most recent patch and this didn't work for me unfortunately.
Comment #246
jennypanighetti commentedI've applied the patch from MR11205 and it did not fix it for me either.
Do other modules have to make their module compatible to this? For example, the Workbench Moderation module?
Comment #248
sker101 commentedI run into config import issue in our CI environment after exporting the action config created by the patch. The problem happens during the site install from existing config process.
It seems that the insert hook introduced in the patch tries to create the corresponding action config whenever a content moderation workflow is being created, which will also happen during the config import process from existing config. Later, when it gets to import the actual action config file, the site install will fail because the config has already been created by the insert hook and Drupal tries to delete and recreate the same action config due to having different UUID.
I’ve just pushed a change to the MR to prevent the "content_moderation_save_workflow_actions()" function from running when "\Drupal::isConfigSyncing()" returns true. I don't think we need it for the "content_moderation_workflow_update" hook since it's unlikely to trigger that hook during site install.
Here's a standalone patch for anyone who prefers not to use the patch from the MR.
Comment #249
dmitry.korhovRe-rolled for 11.1.x
Comment #253
ericgsmith commentedHave rebased it.
Now that content_moderation.module file has been removed in 11.x we needed somewhere to move what was added as
content_moderation_save_workflow_actions- I moved this to the hooks class. Would probably be better to move to a service.I also don't think it needs to be exposed.
Install hook - probably should be post update? As the hook logic is now internal I've modified that somewhat. I see this was actually done previously but only as a patch and not to the MR.
Unit test - seems flaky - the test looks to have been failing for a while - we are just asserting something by telling to mock to provide that thing... I dunno, seems like its really complicated to read that unit test.
Still needs work -
https://git.drupalcode.org/issue/drupal-2797583/-/jobs/7427863/viewer
Also - some changes have been added to 10.4.x instead of the main 11.x branch - I can try tidy this up tomorrow. I'm not so keen to keep battling with the unit test though.
Comment #255
tstoecklerAs far as I can tell what was requested in #229 has since been done, so removing that from the issue summary. The action plugins are created/updated whenever the workflow changes and there is an update path to do that for existing workflows. It does not change anything during config import, but that is actually correct (and important!).
Agreed that adding this functionality to the moderation view is a great idea, and also that functional tests would be good, so leaving the other todos.
Comment #256
tstoeckler