Skip to content

fix(validation): validate required fields on pending, future, and private saves#518

Open
faisalahammad wants to merge 1 commit into
WordPress:trunkfrom
faisalahammad:fix/197-pending-validation
Open

fix(validation): validate required fields on pending, future, and private saves#518
faisalahammad wants to merge 1 commit into
WordPress:trunkfrom
faisalahammad:fix/197-pending-validation

Conversation

@faisalahammad

@faisalahammad faisalahammad commented Jul 22, 2026

Copy link
Copy Markdown

Summary

A contributor who lacks publish_* capability can submit a custom post type for review (status pending), schedule it (future), or make it private (private) without required SCF fields being filled. The save_post gate in ACF_Form_Post only checked for publish, so the acf_validate_save_post() call was skipped on every other status that moves the post out of the author's hands.

This PR widens the gate to the statuses where the post leaves the author (publish, pending, future, private) by extracting the check into a small should_validate_post() helper. Intermediate statuses (draft, auto-draft, inherit, trash) keep saving without enforcement, matching today's behaviour.

Closes #197

Changes

includes/forms/form-post.php

Adds a should_validate_post() method that returns true for the validating status set:

public function should_validate_post( $post ) {
    $validating_statuses = array( 'publish', 'pending', 'future', 'private' );
    return in_array( $post->post_status, $validating_statuses, true );
}

And replaces the inline if ( 'publish' === $post->post_status ) in save_post() with:

if ( $this->should_validate_post( $post ) ) {
    if ( ! acf_validate_save_post() ) {
        return;
    }
}

tests/php/includes/forms/test-form-post.php

Two new tests on Test_Form_Post:

  • test_should_validate_post_for_publishing_statuses — asserts publish, pending, future, private all return true.
  • test_should_validate_post_for_drafting_statuses — asserts draft, auto-draft, inherit, trash return false.

Why this is enough

  • Statuses that demand validator scrutiny now include pending (Submit for Review — the bug), future (scheduled, required fields matter), and private (visible only to authors/editors but still exits draft mode).
  • Statuses for a work-in-progress (draft, auto-draft) keep saving without enforcement so authors can keep iterating.
  • inherit (revisions) and trash already short-circuit earlier in allow_save_post().

The frontend validation paths (assets/src/js/_acf-validation.js) remain unchanged: they already fire AJAX validation on Save/Submit clicks. The classic-editor and Gutenberg useValidation flags are a separate UX concern (which transitions trigger JS validation) and are out of scope for this server-side safety net.

The other save paths I checked (includes/Datastore/REST_Save.php, includes/forms/form-front.php, includes/forms/form-gutenberg.php, options page, attachment, comment, blocks, internal post types) do not go through this gate for unrelated reasons and are not affected.

How to test

  1. Activate this build on a wp-env install.
  2. Create a custom post type (events) and a SCF field group attached to it with one required text field (event_location).
  3. Create a user role with read + edit_events but not publish_events.
  4. As that role, go to Events → Add New, leave event_location empty, click Submit for Review.
    • Expected: form blocked with a required-field error on event_location. Status does not advance to pending.
  5. Fill event_location, click Submit for Review again.
    • Expected: post saves with status pending.
  6. Click Save Draft with event_location empty.
    • Expected: draft saves successfully (regression guard).

A manual test plan is included in TESTING_INSTRUCTIONS.md (in this PR's commit message thread, not committed to the repo).

Automated tests

Run with wp-env running:

composer test:php -- --filter=Test_Form_Post

14 tests pass (2 new + 12 existing), 26 assertions.

Use of AI Tools

AI tooling was used to navigate the codebase, run targeted static analysis and tests, and draft the PR text. The change itself was implemented, reviewed, and validated by the contributor.

The save_post gate in ACF_Form_Post checked only for the 'publish'
status, so a contributor who lacked publish capability could submit a
custom post type for review (status 'pending') with required SCF fields
empty, or schedule a post (status 'future') without required data.
Scheduled and private posts share the same risk class: the post is
leaving the author's hands.

Extract a should_validate_post() helper that returns true for the
statuses where the post leaves the author (publish, pending, future,
private) and keep draft / auto-draft / inherit / trash saving as
before so authors can keep iterating.

Adds two PHPUnit tests covering both the validating and the
non-validating status sets.

Fixes WordPress#197
@github-actions

Copy link
Copy Markdown

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props faisalahammad, yanmetelitsa.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

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.

"Submit for Review" no check for required SCF fields

1 participant