Problem/Motivation

If you have the content moderation state filter in an OR group, then it adds a bundle condition to that group which bypasses the moderation state filter itself.

  1. Enable content moderation, workflows, and views UI.
  2. Enable the workflow for at least one content type at /admin/config/workflow/workflows/manage/editorial.
  3. Edit the moderated content view at /admin/structure/views/view/moderated_content.
  4. Set up the conditions so that it matches the following example structure:
    1. Default group is an And group with Content revision: Is Latest Translation Affected Revision.
    2. Group 2 is an Or group with Content revision: Moderation state <> Published and (Get the actual content from a content revision.) Content: Changed > -1 day.
    3. The logical operator between the Default group and Group 2 is And.

    Picture of the Views conditions just described

  5. Preview the query.

You end up with something like the following where clause:

WHERE
  ((node_field_revision2.nid IS NULL) AND (node_field_revision.revision_translation_affected = '1')) AND
  ((node.type IN ('article', 'page')) OR
  ((content_moderation_state.workflow = 'editorial') AND (content_moderation_state.moderation_state <> 'published')) OR
  ((node_field_data_node_field_revision.changed > 1581335239-86400)))

Rather than showing content that is an article and page and is in the correct moderation state, it will show me content that is either an article/page or is not in the published state. I think that's because the bundle condition is added directly to the group:

$this->query->addWhere($this->options['group'], "$entity_base_table_alias.{$entity_type->getKey('bundle')}", $moderated_bundles, 'IN');

Which contrasts to how the workflow and state IDs are handled further down:

    // The values are strings composed from the workflow ID and the state ID, so
    // we need to create a complex WHERE condition.
    $field = new Condition('OR');
    foreach ((array) $this->value as $value) {
      list($workflow_id, $state_id) = explode('-', $value, 2);

      $and = new Condition('AND');
      $and
        ->condition("$this->tableAlias.workflow", $workflow_id, '=')
        ->condition("$this->tableAlias.$this->realField", $state_id, $operator);

      $field->condition($and);
    }

    $this->query->addWhere($this->options['group'], $field);

Proposed resolution

Ensure an AND is used between the bundle condition and the workflow/state conditions (rather than just inheriting the current group's).

Remaining tasks

  1. Review the patch
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

AndyF created an issue. See original summary.

AndyF’s picture

Status: Active » Needs review
Issue tags: +Needs tests
FileSize
1.78 KB

Attached a small patch that groups all the content moderation state filter conditions together with explicit conjunctions, no tests yet.

AndyF’s picture

Issue summary: View changes
AndyF’s picture

Here's a test - I've duplicated the test view test_content_moderation_state_filter_base_table and put the two moderation state filters each into an OR filter group (ie each group has only one filter, so the OR logic shouldn't actually change the behaviour).

I'm not sure if it's worth also doing the same with a revisions table view?

Status: Needs review » Needs work

The last submitted patch, 4: interdiff_2-4.patch, failed testing. View results

AndyF’s picture

Status: Needs work » Needs review

Oops, I gave the interdiff a .patch extension so testbot dutifully ran and failed that - the actual patches passed and failed as expected.

Baysaa’s picture

Tested the patch against latest 8.9.x, applies cleanly. Looks good to me +1.

The moderated content view's SQL query before patch:

WHERE (
  (node_field_revision2.nid IS NULL) 
  AND (node_field_revision.revision_translation_affected = '1') 
  AND (
    (node_field_data_node_field_revision__node_field_revision.changed > 1581522875-86400)
  )
) 
AND (
  (node.type IN ('article', 'page')) 
  OR (
    (content_moderation_state.workflow = 'editorial') 
    AND (content_moderation_state.moderation_state <> 'published')
  )
)

and after patch:

WHERE (
  (node_field_revision2.nid IS NULL) 
  AND (node_field_revision.revision_translation_affected = '1') 
  AND (
    (node_field_data_node_field_revision__node_field_revision.changed > 1581525171-86400)
  )
) 
AND (
  (node.type IN ('article', 'page')) 
  AND (
    (content_moderation_state.workflow = 'editorial') 
    AND (content_moderation_state.moderation_state <> 'published')
  )
)

The `node.type IN` clause which had an OR operator now has an AND operator.

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.

Sam152’s picture

Status: Needs review » Reviewed & tested by the community

Wow, really nice analysis and patch. The explanation and reasoning for this, as well as the tests all look great. I would have perhaps just gone the verbose route of adding a new test method and copied the relevant parts from testStateFilterViewsRelationship, but the complexity added by the single foreach seems okay to me as well.

Patch still applies to 9.1.x.

SpadXIII’s picture

Can confirm this patch fixes the issue. It even works when nesting the filter in a group with an OR-operator.
The whole part of this filter (bundle + workflow + moderation_state) is wrapped within braces.

This was the issue that threw off my query; all nodes were found because they were matched on content type (bundle-part of this filter), effectively ignoring the rest of the filters in the filter-group.

+1 for a merge (patch also still applies to 8.9)

  • catch committed e337176 on 9.1.x
    Issue #3112433 by AndyF, Baysaa, Sam152, SpadXIII: Content moderation...
catch’s picture

Status: Reviewed & tested by the community » Fixed

Committed/pushed to 9.1.x and cherry-picked back to 8.9.x, thanks!

  • catch committed 79b0853 on 9.0.x
    Issue #3112433 by AndyF, Baysaa, Sam152, SpadXIII: Content moderation...

  • catch committed 650d699 on 8.9.x
    Issue #3112433 by AndyF, Baysaa, Sam152, SpadXIII: Content moderation...

  • catch committed cf65ed3 on 9.1.x
    Revert "Issue #3112433 by AndyF, Baysaa, Sam152, SpadXIII: Content...

  • catch committed d8fee2b on 9.0.x
    Revert "Issue #3112433 by AndyF, Baysaa, Sam152, SpadXIII: Content...
catch’s picture

Status: Fixed » Needs work

Looks like this caused https://www.drupal.org/pift-ci-job/1787683, rolling back.

Think it might be missing a use statement in the 9.1.x patch that was previously there when the test passed.

  • catch committed b433ffd on 8.9.x
    Revert "Issue #3112433 by AndyF, Baysaa, Sam152, SpadXIII: Content...
AndyF’s picture

Status: Needs work » Needs review
FileSize
845 bytes
11.62 KB

Thanks @catch! I think it got caught in the wake of #3130655: Deprecate creating an instance of the class Drupal\Core\Database\Query\Condition with the new keyword, here's an updated patch for 9.1, I think the old patch should be fine for < 9.1?

Baysaa’s picture

Status: Needs review » Reviewed & tested by the community

The change in #19 makes good sense and lgtm. RTBC +1

  • catch committed 5552c61 on 9.1.x
    Issue #3112433 by AndyF, Baysaa, catch, Sam152, SpadXIII: Content...

  • catch committed 35b4ce8 on 9.0.x
    Issue #3112433 by AndyF, Baysaa, catch, Sam152, SpadXIII: Content...
catch’s picture

Status: Reviewed & tested by the community » Fixed

OK let's try that again.

  • catch committed d717b24 on 8.9.x
    Issue #3112433 by AndyF, Baysaa, catch, Sam152, SpadXIII: Content...

Status: Fixed » Closed (fixed)

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