Skip to content

fix(republish): preserve capability-gated content in scheduled republish#494

Open
enricobattocchi wants to merge 2 commits intotrunkfrom
210-scheduled-republish-strips-raw-html
Open

fix(republish): preserve capability-gated content in scheduled republish#494
enricobattocchi wants to merge 2 commits intotrunkfrom
210-scheduled-republish-strips-raw-html

Conversation

@enricobattocchi
Copy link
Copy Markdown
Member

Context

The Rewrite & Republish feature publishes scheduled copies via WP-Cron. WP-Cron runs without a logged-in user, which caused capability-gated post_content filters to treat the operation as untrusted: WordPress's kses, WPBakery Page Builder's wpb_remove_custom_html (which strips [vc_raw_html], [vc_raw_js] and [vc_gmaps] shortcodes when the current user lacks unfiltered_html), and other similar filters from third-party plugins. As a result, the inner content of WPBakery Raw HTML elements was being deleted from the original post when a Rewrite & Republish copy was scheduled to be published in the future, even though the user who scheduled the republish had unfiltered_html.

Summary

This PR can be summarized in the following changelog entry:

  • Fixes a bug where content inside WPBakery [vc_raw_html] (and similar capability-gated) shortcodes was deleted from the original post when a scheduled Rewrite & Republish was published via WP-Cron.

changelog: bugfix

Relevant technical choices:

  • Set the copy author as the current user before calling republish() and restore the previous user afterwards in a try/finally. This makes capability-gated filters evaluate against a real user the way they do for any normal save.
  • Setting the user via wp_set_current_user() also triggers WordPress's own kses_init through the set_current_user action, which adds or removes kses filters based on that user's actual capabilities. This makes the previous manual kses_remove_filters() / kses_init_filters() workaround unnecessary, so it has been removed.
  • The fix generalises beyond WPBakery: any plugin that gates post_content save filters on capability checks now behaves correctly during scheduled republish.
  • Added a unit test that covers the failure path (the try/finally correctly restores the previous user when republish() throws).
  • Added an integration test that simulates a capability-gated stripping filter and asserts the content survives the scheduled republish.

Test instructions

Test instructions for the acceptance test before the PR gets merged

This PR can be acceptance tested by following these steps:

  1. Set up a test site with WPBakery Page Builder installed (version 8.7.2 or similar recent).
  2. Create a page that contains a Raw HTML element with some content (e.g. a block of HTML with a <script> tag inside).
  3. Use Yoast Duplicate Post to create a Rewrite & Republish copy of that page.
  4. Edit the copy, change something innocuous (e.g. the title), and Schedule it for a couple of minutes in the future.
  5. Wait for the scheduled time to pass, then trigger WP-Cron (visit the front-end, or run wp cron event run --due-now with WP-CLI).
  6. Open the original page.
    • Without this PR: the Raw HTML element exists but the inner content is missing.
    • With this PR: the Raw HTML element retains its full content.
  7. Verify the immediate Republish flow is unchanged: create another Rewrite & Republish copy, click Republish without scheduling, and confirm the content is preserved.

Relevant test scenarios

  • Changes should be tested with the browser console open
  • Changes should be tested on different posts/pages/taxonomies/custom post types/custom taxonomies
  • Changes should be tested on different editors (Default Block/Gutenberg/Classic/Elementor/other)
  • Changes should be tested on different browsers
  • Changes should be tested on multisite

The bug is in the cron handler, so it should reproduce regardless of which editor was used to schedule the copy. Worth verifying both Block Editor and Classic Editor flows still schedule and republish correctly.

Test instructions for QA when the code is in the RC

  • QA should use the same steps as above.

Impact check

This PR affects the following parts of the plugin, which may require extra testing:

  • The scheduled Rewrite & Republish path (Post_Republisher::republish_scheduled_post). Immediate Republish (Block Editor REST and Classic Editor) is unchanged because it already runs in an authenticated user context.
  • Third-party plugins that hook wp_insert_post_data or content_save_pre with capability checks will now see the copy author as the current user during scheduled republish (previously: user 0). This is the intended behaviour and matches what those filters see during any normal save by the same user.

UI changes

  • This PR changes the UI in the plugin. I have added the 'UI change' label to this PR.

Documentation

  • I have written documentation for this change. For example, comments in the Relevant technical choices, comments in the code, documentation on Confluence / shared Google Drive / Yoast developer portal, or other.

Quality assurance

  • I have tested this code to the best of my abilities
  • I have added unittests to verify the code works as intended

Innovation

  • No innovation project is applicable for this PR.
  • This PR falls under an innovation project. I have attached the innovation label and noted the work hours.

Fixes #210

WP-Cron runs without a logged-in user, which caused capability-gated save
filters (kses, WPBakery's wpb_remove_custom_html, and similar) to strip
otherwise valid content during a scheduled Rewrite & Republish, removing
the inner content of shortcodes such as [vc_raw_html] from the original
post.

Run the republish as the copy author so those filters evaluate against
a real user, and restore the previous user afterwards in a try/finally.
Setting the user also triggers WordPress's own kses_init via the
set_current_user action, replacing the previous manual kses_remove_filters
/ kses_init_filters workaround.

Fixes #210
- Lower YOASTCS_THRESHOLD_ERRORS from 57 to 52 to match the new count
  after removing the kses_remove_filters / kses_init_filters block.
- Grant super-admin to the test user on multisite in
  test_republish_scheduled_post_preserves_raw_html_content. On multisite
  regular Administrators do not have the unfiltered_html capability, only
  Super Admins do, which caused the simulated stripping filter to strip
  content even when the fix was working correctly.
@coveralls
Copy link
Copy Markdown

Coverage Report for CI Build 25155582808

Warning

No base build found for commit d6a8d94 on trunk.
Coverage changes can't be calculated without a base build.
If a base build is processing, this comment will update automatically when it completes.

Coverage: 59.896%

Details

  • Patch coverage: 4 of 4 lines across 1 file are fully covered (100%).

Uncovered Changes

No uncovered changes found.

Coverage Regressions

Requires a base build to compare against. How to fix this →


Coverage Stats

Coverage Status
Relevant Lines: 2688
Covered Lines: 1610
Line Coverage: 59.9%
Coverage Strength: 7.42 hits per line

💛 - Coveralls

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WPBakeryRaw HTML element is deleted after scheduled Rewrite & Republish

2 participants