Skip to content

Added bulk moderation actions for publishing content from the content listing. - #282

Merged
AlexSkrypnyk merged 1 commit into
developfrom
feature/bulk-publish
Aug 3, 2026
Merged

Added bulk moderation actions for publishing content from the content listing.#282
AlexSkrypnyk merged 1 commit into
developfrom
feature/bulk-publish

Conversation

@AlexSkrypnyk

Copy link
Copy Markdown
Member

Every node bundle on this site runs under the civictheme_editorial content moderation workflow. Drupal core forbids the core Publish content / Unpublish content bulk actions on moderated entities, so selecting nodes on /admin/content and applying Publish content produced a No access to execute Publish content on the Content <title> error per row, for every account including uid 1. This adds drupal/moderated_content_bulk_publish, which supplies bulk action plugins that apply a workflow transition instead of writing to the status field directly.

The module also ships optional pin / unpin actions that only toggle sticky, duplicating core's node_make_sticky_action / node_make_unsticky_action, so those were not exported. Provisioning here is VORTEX_PROVISION_TYPE=database (database import plus config import, never a fresh profile install), so the module's config/optional is never reprocessed and they will not reappear.

Checklist before requesting a review

  • Subject includes ticket number as [#123] Verb in past tense.
  • Ticket number #123 added to description
  • Added context in Changed section
  • Self-reviewed code and commented in commented complex areas.
  • Added tests for fix/feature.
  • Relevant tests run and passed locally.

Changed

  1. Added drupal/moderated_content_bulk_publish ^2.0 (resolved to 2.0.52, security-advisory covered, ~11.5k sites) to composer.json, regenerated composer.lock, and enabled the module in config/default/core.extension.yml.
  2. Added action config for the new bulk operations: system.action.publish_latest.yml, system.action.unpublish_current.yml and system.action.archive_current.yml for nodes, plus system.action.media_publish_latest.yml and system.action.media_unpublish_current.yml for media, since media bundles are moderated by the same workflow.
  3. Added config/default/moderated_content_bulk_publish.settings.yml: disabled the confirmation dialog on the node edit form (enable_dialog_node_edit_form: false), since it would otherwise prompt on every routine published-to-published re-save of a live page, kept it enabled on /admin/content bulk operations (enable_dialog_admin_content: true) as a confirmation guard, disabled the toolbar language switcher (disable_toolbar_language_switcher: true, site is single-language), and turned off revision-info retention (retain_revision_info: false).
  4. Granted moderated content bulk publish, moderated content bulk unpublish and moderated content bulk archive to the civictheme_site_administrator and civictheme_content_approver roles in their exported config, matching the roles that already hold the corresponding civictheme_editorial publish and archive transitions. civictheme_content_author was left out, matching its existing transition set; administrator already inherits everything via is_admin: true.
  5. Documented the failure and its replacement actions as a project FAQ in docs/faqs.md.

Screenshots

N/A - composer, configuration and documentation change only, no template, style or display changes.

Before / After

BEFORE: /admin/content -> "Publish content"
┌──────────────────────────────────────────────────────────┐
│ [x] Project "Website Redesign"   (draft)                 │
│ [x] Project "Mobile App"         (draft)                 │
│                                                          │
│ Action: Publish content                                  │
│         -> Apply to selected items                       │
│                                                          │
│ Drupal core blocks it two ways:                          │
│  - actionInfoAlter() swaps in ModerationOptOutPublish,   │
│    whose access() = forbidden                            │
│  - entityFieldAccess() forbids edit on the moderated     │
│    "status" field                                        │
│                                                          │
│ Result:                                                  │
│  x "No access to execute Publish content on the          │
│     Content Website Redesign."                           │
│  x Same error, once per selected row.                    │
│                                                          │
│ Nothing published. Fails for every account, incl. uid 1. │
└──────────────────────────────────────────────────────────┘

AFTER: /admin/content -> "Publish latest revision"
┌───────────────────────────────────────────────────────┐
│ [x] Project "Website Redesign"   (draft)              │
│ [x] Project "Mobile App"         (draft)              │
│                                                       │
│ Action: Publish latest revision                       │
│         -> Apply to selected items                    │
│                                                       │
│ moderated_content_bulk_publish applies a workflow     │
│ transition instead of writing the "status" field:     │
│  - publish_latest_revision_action: draft -> published │
│                                                       │
│ Result:                                               │
│  v New published revision per node (e.g. vid 2573),   │
│    attributed to the acting user, revision log        │
│    "Bulk operation publish revision".                 │
│    Prior draft revision preserved.                    │
│                                                       │
│ Both nodes now published (status = 1).                │
└───────────────────────────────────────────────────────┘

… listing.

Content moderation covers every node bundle, so core forbids the 'Publish content' and 'Unpublish content' bulk actions on all of them. Added 'drupal/moderated_content_bulk_publish', which supplies 'Publish latest revision', 'Unpublish current revision' and 'Archive current revision' actions that apply a workflow transition instead of flipping the 'status' field.

Granted the new permissions to the roles that already hold the matching 'civictheme_editorial' transitions. Dropped the module's 'pin' and 'unpin' actions, which duplicate the core sticky actions, and turned off its node form confirmation dialog, which prompts on every save that targets the published state.
@AlexSkrypnyk AlexSkrypnyk added the AUTOMERGE Pull request has been approved and set to automerge label Aug 3, 2026
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 2 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 6f9c5318-b6f3-42bf-957b-ced52607541f

📥 Commits

Reviewing files that changed from the base of the PR and between f39af39 and 7e1425d.

⛔ Files ignored due to path filters (1)
  • composer.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • composer.json
  • config/default/core.extension.yml
  • config/default/moderated_content_bulk_publish.settings.yml
  • config/default/system.action.archive_current.yml
  • config/default/system.action.media_publish_latest.yml
  • config/default/system.action.media_unpublish_current.yml
  • config/default/system.action.publish_latest.yml
  • config/default/system.action.unpublish_current.yml
  • config/default/user.role.civictheme_content_approver.yml
  • config/default/user.role.civictheme_site_administrator.yml
  • docs/faqs.md

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

Code coverage (threshold: 80%)

  Classes: 52.94% (9/17)
  Methods: 70.27% (26/37)
  Lines:   91.13% (370/406)
Per-class coverage
Drupal\do_content_api\EventSubscriber\JsonApiWriteGateSubscriber
  Methods:  66.67% ( 2/ 3)   Lines:  88.89% (  8/  9)
Drupal\do_content_api\Hook\EntityCreateAccessHook
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  9/  9)
Drupal\do_content_api\Hook\ModerationPolicyHook
  Methods:  50.00% ( 1/ 2)   Lines:  93.75% ( 15/ 16)
Drupal\do_content_api\Routing\RouteSubscriber
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  6/  6)
Drupal\do_feed\FeedUrlBuilder
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 18/ 18)
Drupal\do_feed\Form\FeedSettingsForm
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 15/ 15)
Drupal\do_feed\Hook\EntityDeleteHook
  Methods:  50.00% ( 1/ 2)   Lines:  92.31% ( 12/ 13)
Drupal\do_feed\Hook\EntityPresaveHook
  Methods: 100.00% ( 4/ 4)   Lines: 100.00% ( 54/ 54)
Drupal\do_feed\Hook\PreprocessParagraphHook
  Methods: 100.00% ( 2/ 2)   Lines: 100.00% ( 14/ 14)
Drupal\do_feed\Hook\PreprocessViewsViewRowRssHook
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  3/  3)
Drupal\do_feed\Hook\ViewsPreViewHook
  Methods:  50.00% ( 1/ 2)   Lines:  96.43% ( 27/ 28)
Drupal\do_generated_content\Generator\CaseMatrix
  Methods: 100.00% ( 3/ 3)   Lines: 100.00% ( 12/ 12)
Drupal\do_generated_content\Generator\RelativeDate
  Methods: 100.00% ( 1/ 1)   Lines: 100.00% (  1/  1)

@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 85.84%. Comparing base (f39af39) to head (7e1425d).

Additional details and impacted files
@@           Coverage Diff            @@
##           develop     #282   +/-   ##
========================================
  Coverage    85.84%   85.84%           
========================================
  Files           18       18           
  Lines          226      226           
========================================
  Hits           194      194           
  Misses          32       32           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@AlexSkrypnyk AlexSkrypnyk added the Needs review Pull request needs a review from assigned developers label Aug 3, 2026
@AlexSkrypnyk
AlexSkrypnyk merged commit db29116 into develop Aug 3, 2026
10 checks passed
@AlexSkrypnyk
AlexSkrypnyk deleted the feature/bulk-publish branch August 3, 2026 10:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AUTOMERGE Pull request has been approved and set to automerge Needs review Pull request needs a review from assigned developers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants