Problem/Motivation

Currently 'draft' is the default moderation state for all new content. There are two kinds of defaults when it comes to content moderation:

  • The default state in the select list, the one that appears as the selected option on the entity form.
  • The default state of the entity, the state that is chosen as the starting point to validate which transitions an entity may use to undergo a state change and the value of $entity->moderation_state before any state is assigned.

The default entity state will be the same as the default widget state as long as the logged in user has access to a transition that allows the default state to be selected. In effect, it's possible to configure a starting state that may never be returned to and will thus never show up in the select list. Under those circumstances, the selected state will revert to the first one in the list.

Proposed resolution

Allow admins to configure the starting entity state. The widget state will also default to this value, provided the user has access to an appropriate transition that allows it.

Remaining tasks

  1. Tests for setting the default.
  2. We should not allow deleting the default state.

User interface changes

Additional settings form on moderation workflows.

API changes

A configurable starting state.

Data model changes

Additional config schema value for the content moderation workflow type.

Comments

nlisgo created an issue. See original summary.

timmillwood’s picture

Issue tags: +Workflow Initiative

The initial state for new content comes from \Drupal\workflows\Entity\Workflow::getInitialState, which is simply the first item in the array of states.

Currently editing an entity the default state is the first valid transition, but this may change in #2753717: Add select field to choose moderation state on entity forms to be current state.

Maybe in the workflow we should have a way to set the initial state.

amateescu’s picture

timmillwood’s picture

@amateescu - No, this wouldn't solve the issue. #2817835: When enabling moderation apply a relative state is returning the "correct" state for already saved entities depending on if they're published or not, when they don't already have ContentModerationState entities.

What this issue is asking for is a way for "Published" to be the default state when creating a new entity, currently the default state is "draft". So we need a way within the Workflow edit form to set a default state.

Not sure how much of a priority this should be, guess we can wait for a patch or more demand for this.

nlisgo’s picture

Could this possibly be achieved by allow the moderation state to be overridden for each entity. This would solve my problem and potentially allow for different setups were the majority of content could adhere to a default moderation state but particular entities could override that default.

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

Drupal 8.4.0-alpha1 will be released the week of July 31, 2017, which means new developments and disruptive changes should now be targeted against the 8.5.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.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.

timmillwood’s picture

@nlisgo - Do you still have a need for this?

Currently in 8.6.x the initial state for Content Moderation workflows is determined in \Drupal\content_moderation\Plugin\WorkflowType\ContentModeration::getInitialState(). It uses "published" for published entities, otherwise "draft". For entity types which are not publishable we just get the first state in the list, ordered by weight, label, then key.

bkosborne’s picture

See also #2927408: Default value not correctly set in the ModerationStateWidget., which has a patch to set the ModerationStateWidget (the form widget that's used on the main entity form for moderated entities) default value to the current state if available.

If a patch is developed for this issue, I suggest getting the mentioned issue in first, since they'll touch the same code.

berdir’s picture

Also struggling with a related issue.

The problem is that It uses "published" for published entities, otherwise "draft" is not quite correct. It's actually published *and* not-new, which is exactly the problem here. It means that all new content is forced to being draft, despite being set to published.

In our case, we have a distribution with a lot of default content (using default_content module) that is published. We're trying to start using content_moderation. All our default content is however changed to a draft because of the !isNew() check there. Why does that exist? What problem is that trying to solve?

With that, I either need to implement some kind of default_content hook/event to overrride or change all our default content to include the moderation state explicitly.

And it looks like the UI is actually not respecting that, at least not in 8.5 (didn't test with #2927408: Default value not correctly set in the ModerationStateWidget. for example), that simply picks the first item from the select which is then based on the states weight.

berdir’s picture

Second problem.. who says that my states are called published or draft? I can name them whatever I want?

Don't really understand what that whole snippet is trying to solve, didn't check the issue that added it :)

timmillwood’s picture

The second problem is an easy one. There are two states content moderation enforces, published and draft. So we always know these exist.

It sounds like your use case @berdir is a valid issue. So we need to assume new entities are published. I wonder if this is an entity system issue rather than content moderation. Are new entities defaulted to unpublished, and the status checkbox defaults to checked?

berdir’s picture

Hm, didn't know about those being enforced, why is that? What if I want to have a different state be the default like the parent method supports simply by picking the first?

And if they are enforced, should we have constants for them or so?

The entity system doesn't really care about the (default value of the) status field, that's up to the configured default value of the field, e.g. node stores overridden base field definitions for each bundle to set the default value. so articles might be published by default while pages are not. Either way, you *can* rely on isPublished(), it shouldn't matter if an entity is new or not.

johnchque’s picture

StatusFileSize
new893 bytes

Adding a patch where we remove the isNew check, want to see how many tests it might break.

johnchque’s picture

Status: Active » Needs review

Status: Needs review » Needs work

The last submitted patch, 14: 2856967-14.patch, failed testing. View results

johnchque’s picture

Status: Needs work » Needs review
StatusFileSize
new5.1 KB
new3.72 KB

Fixing tests.

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.

sam152’s picture

Issue summary: View changes

And if they are enforced, should we have constants for them or so?

They aren't quite constants, but they are keys on the workflow type annotation:

 * @WorkflowType(
 *   id = "content_moderation",
 *   label = @Translation("Content moderation"),
 *   required_states = {
 *     "draft",
 *     "published",
 *   },

Here is some background on the issue: #2817835: When enabling moderation apply a relative state. I believe the original intentions can be summarised as: once moderation has been enabled on an entity type, the published status can no longer be used to control the state of content during normal operations. I think it doesn't work for the same reasons we wouldn't expect to be able to go:

$node = Node::load(1);
$this->assertEquals('published', $node->moderation_state->value);
$node->setUnpublished();
$this->assertEquals('draft', $node->moderation_state->value);

So we don't expect to be able to programatically control moderation using the 'status' field. I think it's perhaps strange to leverage this as a feature for setting the initial state too, for the same reasons? It looks like we already discussed this exact semantic some 2 years ago: #2839371: Programatically creating a published entity doesn't result in a published entity. :)

Note: selecting the initial state from status is really only there to facilitate transitioning old/existing content to a model where moderation is enabled. It's a best guess to ensure there is no data integrity issues and I don't think is a precedent for making 'status' directly impact 'moderation_state'. I also think it might be tricky from a BC perspective. The initial state will change to something different for any users who had 'published' configured as the default for whatever reason.

I think we should do the following:

  • Leave the behaviour of forcefully allocating draft/published for legacy content items in place.
  • Add a new configuration option to the ContentModeration @WorkflowType to allow admins to configure a default moderation state, (with an upgrade path to set it to draft).

I think that is the least disruptive and should account for the 90% use case. It doesn't exactly solve the requirement in the issue summary:

I would like to allow a default moderation state of published for new content and draft for moderations to existing content.

Existing content will always continue to be based on the publishing status, while that transition is made.

Updating the IS. If anyone sees any issues with this approach or disagrees with the analysis, we can shuffle it back.

sam152’s picture

Status: Needs review » Needs work
sam152’s picture

Title: Allow different default moderation state for adding or editing content » Allow admins to select a default starting moderation state
berdir’s picture

I thought that the default behavior is already defined based on the order, e.g. the first in the list is the default? So not sure if we need a new setting, actually? That order logic AFAIK also already works, there's just no way to have a different logic for edit/add content there.

To repeat, see #10, we're not that interested in the different default for edit/add directly, we just have the problem with default_content all ending up unpublished and want to get rid of that isNew() check there. While I agree that the logic there is mostly about existing content, it also applies to any content created through the API where the moderation state is not explicitly set, then I expect that it respects the initial status, otherwise sites might have to rewrite a lot custom code because content_moderation interferes with that?

That said, I think it's also not really related to this issue, so might be better to open a new one just for that?

Problem is that at least in 8.5, you can't explicitly set the moderation state on REST/default_content because of #2943899: Moderation state field cannot be updated via REST, because special handling in ModerationStateFieldItemList, so we also don't have the option to explicitly update our default content, at least not without adding yet another core patch.

sam152’s picture

StatusFileSize
new7.71 KB

WIP patch to make this configurable. Outstanding tasks are:

  1. Tests for setting the default.
  2. We should not allow deleting the default state.
sam152’s picture

Status: Needs work » Needs review
alison’s picture

(a) This functionality is important to me, thank you for working on it! Just wanted to chime in that, y'know, I want to have it :) :)

(b) I'm adding a related issue, I hope it feels related to y'all, it certainly does to me.

(I can't test the patch right now b/c I'm running stable 8.5.6.)

alison’s picture

(Sorry for the extra comment)

FWIW I'm on 8.5.6 -- when I create a new node, the order of options in the "states" dropdown is correct (Published, Draft -- corresponds with the weighting on the editorial workflow settings screen), the option selected by default is "Draft" -- I want it to default to "Published."

(That's what you're changing in this issue, right?)

sam152’s picture

Issue summary: View changes

Let me update the issue summary to be clearer about 'default' in this case, since it's a bit confusing. Let me know if that clears it up.

sam152’s picture

Issue summary: View changes
sam152’s picture

Issue summary: View changes
sam152’s picture

StatusFileSize
new5.26 KB
new12.98 KB

Adding functional and kernel tests and ensuring users don't accidentally delete a state that is configured to be the default. If this goes green, I think it'll be good for a review.

alison’s picture

Great title change, thank you! Good idea with the safety feature in latest patch.

johnchque’s picture

StatusFileSize
new11.83 KB

Uploading patch #30 for 8.5.6. :)

nuuou’s picture

I'm getting an error running update.php when applying Patch #32 onto a Drupal 8.5.6 install.

PHP Fatal error: Call to undefined method Drupal\Core\DependencyInjection\ClassResolver::update() in /drupal-site/web/core/modules/content_moderation/content_moderation.post_update.php on line 103

The code appears to be working, but I imagine these fatal errors aren't right. I only have one Workflow created, and it's applied to one Content Type.

merilainen’s picture

I tested the patch for 8.5.6 and it works nicely. I needed to have Published as default state, but there is an "external editor" role who can only save content as Draft. So I added a "Save as draft" transition (Published -> Draft) and now external editors can see only that. For other roles the Published is selected by default but they can also change it to Draft.

Edit 1: But it seems that if content is published and then turned back to Draft, it seems to stay published anyway even if the state changes. Is this by design? The UI doesn't allow me to make Draft default revision, so maybe a new revision is created which is unpublished but it's not the default revision. This sounds like a different issue.

Edit 2: It seems that the 8.5.6 patch fails on database update: Error: Call to undefined method Drupal\Core\DependencyInjection\ClassResolver::update() in content_moderation_post_update_set_default_moderation_state() (line 103 of content_moderation.post_update.php).
I suppose it's because ConfigEntityUpdater only exists for 8.6.x: https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Config%21...
Any solution for 8.5.x?

merilainen’s picture

In the end I decided to go with a more simple approach for 8.5.6 because I suppose new features will not be back-ported even to 8.6.x
In hook_form_alter:

if ($form_state->getFormObject() instanceof EntityForm) {
    if ($form_state->getFormObject()->getOperation() == 'default') {
      if (isset($form['moderation_state']['widget'][0]['state']['#options'])) {
        // For new content, take the first value from moderation state options
        // as default value because the order can be changed in workflow UI.
        $state_options = $form['moderation_state']['widget'][0]['state']['#options'];
        reset($state_options);
        $form['moderation_state']['widget'][0]['state']['#default_value'] = key($state_options);
      }
    }
  }

This will use the first item in the drop-down as default selection for new content, but will not affect existing content which already has a state.

johnchque’s picture

StatusFileSize
new12.3 KB
new1.7 KB

Changed a bit to follow previous code style as per #2949630: New service to make updating configuration entities easier inside update hooks. :)
Note: This is for 8.5.6 only. :)

nuuou’s picture

Patch #36 is working nicely for me now on Drupal 8.5.6. I was previously having the same issue as @mErilainen with Patch #32.

sam152’s picture

If the patch is working, any chance someone could review #30?

jibran’s picture

Status: Needs review » Reviewed & tested by the community

Haven't found anything to complain about so RTBC for #30. Can you please make it the latest patch by reuploading it?

sam152’s picture

StatusFileSize
new12.98 KB

Sure. Thanks for the review.

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 40: 2856967-30.patch, failed testing. View results

jibran’s picture

Status: Needs work » Reviewed & tested by the community
larowlan’s picture

  1. +++ b/core/modules/content_moderation/src/Form/ContentModerationConfigureForm.php
    @@ -128,6 +129,21 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
    +      '#default_value' => isset($workflow_type_configuration['default_moderation_state']) ? $workflow_type_configuration['default_moderation_state'] : 'draft',
    

    shouldn't the update hook ensure this is always set? i.e. no need for the isset call?

  2. +++ b/core/modules/content_moderation/src/Plugin/WorkflowType/ContentModeration.php
    @@ -306,11 +306,11 @@ public function getInitialState($entity = NULL) {
    +    return $this->getState(!empty($this->configuration['default_moderation_state']) ? $this->configuration['default_moderation_state'] : 'draft');
    

    same here

  3. +++ b/core/modules/content_moderation/tests/src/Kernel/ModerationStateFieldItemListTest.php
    @@ -281,4 +282,36 @@ public function moderatedEntityWithExistingIdTestCases() {
    +    // Test a node for a workflow that hasn't been updated to include the
    +    // 'default_moderation_state' setting. We must be backwards compatible with
    +    // configuration that was exported before this change was introduced.
    

    ah, because we could have workflows other than content moderation? which are the only ones updated in the update hook - if that's the reason, ignore my previous comments

sam152’s picture

@larowlan thanks for the review!

That is designed to cover the problem described in issues like #2543150: Document consequences of contrib changing config schema without core's API supporting config version tracking and #1677258: Configuration system does not account for API version compatibility between modules and was a critical bit of feedback I received on #2871354: Improve "allowed_html" to provide a better config diff.

If a module has exported a content moderation workflow, without the updated schema, we don't want to break any functionality once 8.7 is released. All config created before the patch should behave the same after the patch, regardless of config being processed by the update hook. In that sense the only changes allowed to config schema are additive ones and we must support updated an non-updated config alike.

larowlan’s picture

Status: Reviewed & tested by the community » Needs review
Issue tags: +Needs change record

Can we get a change record here, showing the UI option in a screenshot.

Cheers

sam152’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs change record

I have added a change record here: https://www.drupal.org/node/3001134.

  • larowlan committed a6b0f1d on 8.7.x
    Issue #2856967 by yongt9412, Sam152: Allow admins to select a default...
larowlan’s picture

Status: Reviewed & tested by the community » Fixed

Committed a6b0f1d and pushed to 8.7.x. Thanks!

Published change record

tacituseu’s picture

This is failing on PHP 5.5:
https://www.drupal.org/pift-ci-job/1074246

Fatal error: Cannot use isset() on the result of a function call (you can use "null !== func()" instead) in /var/www/html/core/modules/content_moderation/tests/src/Functional/DefaultModerationStateUpdateTest.php on line 33
PHP Fatal error:  Cannot use isset() on the result of a function call (you can use "null !== func()" instead) in /var/www/html/core/modules/content_moderation/tests/src/Functional/DefaultModerationStateUpdateTest.php on line 33
FATAL Drupal\Tests\content_moderation\Functional\DefaultModerationStateUpdateTest: test runner returned a non-zero error code (255).

  • larowlan committed 6b663aa on 8.7.x
    Revert "Issue #2856967 by yongt9412, Sam152: Allow admins to select a...
larowlan’s picture

Status: Fixed » Needs work

Thanks @tacituseu

Rolled this back

sam152’s picture

Status: Needs work » Needs review
StatusFileSize
new1.17 KB
new13.05 KB

Fixing! Thanks for the reviews everyone.

sam152’s picture

Status: Needs review » Reviewed & tested by the community

Passing on 5.5.

alexpott’s picture

StatusFileSize
new1.17 KB
new12.98 KB

PHPUnit has nice asserts for arrays meaning we don't have to deal with isset() differences between PHP5 and PHP7 at all.

alexpott’s picture

Adding issue credit for people who help frame the issue.

sam152’s picture

Oh even better, thanks @alexpott!

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed b6a29fd and pushed to 8.7.x. Thanks!

  • alexpott committed b6a29fd on 8.7.x
    Issue #2856967 by Sam152, yongt9412, alexpott, Berdir, larowlan,...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

sam152’s picture

Title: Allow admins to select a default starting moderation state » Allow admins to select a default entity starting moderation state
sam152’s picture

Title: Allow admins to select a default entity starting moderation state » Allow admins to select a default entity moderation state
gnuget’s picture

StatusFileSize
new12.76 KB

It seems that #3000573: After enabling Workflows and Content Moderation there is a fatal error when trying to edit content translation broke this patch for Drupal 8.6.x.

I just rerolled it just in case someone needs this feature at Drupal 8.6.x.

alison’s picture

THANK YOU @gnuget !!!!!!!!!!!

alison’s picture

Anyone getting a fatal error when updating core from 8.6.x to 8.7.x? (specifically for me, it's 8.6.16 to 8.7.3)

When I go to update.php > "Continue" button (aka /update.php/selection), I get the following Fatal error on a white screen:
Fatal error: Cannot redeclare content_moderation_post_update_set_default_moderation_state() (previously declared in /path/to/web/core/modules/content_moderation/content_moderation.post_update.php:104) in /path/to/web/core/modules/content_moderation/content_moderation.post_update.php on line 187

(line 104 is function content_moderation_post_update_set_default_moderation_state(), added in this patch)

(I did use the 8.6.x-reroll patch back in Feb; @gnuget have you updated to 8.7.x since then?)

gnuget’s picture

Hello @alisonjo2786

Yup, this happened to me.

I had my patch in the "patches" section of my composer.json file so when I updated to 8.7 the fix was already part of the core and when my patch was applied it added the method again triggering the same error that you pasted.

In my case I just had to remove my patch from the composer.json file and execute composer again (to re-download a version of the core without my patch but with the fix) and that made the trick.

alison’s picture

Thank you @gnuget -- worked for me on my dev env in the cloud, it's just my local site that's still broken, I think maybe some of the DB updates ran before the failure, and I wasn't smart enough to make a DB backup of my local before running updb, siiiigh. Probably that's all it is.

Sorry -- to be clear -- your steps definitely fixed the redeclare error, but I still can't run updb, it throws:

Post updating system
Failed: Drupal\Component\Plugin\Exception\PluginNotFoundException: The "moderation_state_change:node" plugin does not exist. 
Valid plugin IDs for Drupal\Core\Action\ActionManager are: [blah blah blah long list] in
Drupal\Core\Plugin\DefaultPluginManager->doGetDefinition() (line 53 of /path/to/web/core/lib/Drupal/Component/Plugin/Discovery/DiscoveryTrait.php).

I've cleared caches and whatnot, but, no dice. I'll prob just restart this local site from the cloud :)

Thank you!

alison’s picture

Figured it out! Just in case it helps someone else -- I had some old config crud still hanging around in my broken local site DB, **such as....**

system.action.moderate_to_published

...which included, among other things...

id: moderate_to_archived
label: 'Change moderation state to Archive'
type: node
plugin: 'moderation_state_change:node'

Ding ding ding!

A few drush cdel _______ commands later, I was all set.