Skip to content

Drop trap, acf_after_title #1029

Description

@rdelbem

Describe the bug
ACF renders a hidden, empty #acf_after_title-sortables meta box container on every block editor post screen, even when no field group uses the acf_after_title position. Because WordPress' postboxes.js connects all .meta-box-sortables elements as jQuery UI sortable drop targets, users can drag non ACF meta boxes into this invisible container. The drop is persisted by postboxes.save_order() into the user's meta-box-order_{$screen} user meta under an acf_after_title bucket.

On the next load, WP core re-registers those box IDs into the acf_after_title context via add_meta_box( $id, null, null, $screen, $box_context, 'sorted' ) (wp-admin/includes/template.php:1324–1332). Gutenberg only renders side/normal/advanced visibly, so the box disappears for that user, the corruption is per-user, not site wide, and is permanent because ACF's existing self repair never runs in this scenario.

To Reproduce

  1. Install ACF 6.8.9 with no field group set to the "High (after title)" position.
  2. Open any post in the block editor with at least one classic meta box present (e.g. a plugin meta box).
  3. Inspect the hidden .metabox-base-form form rendered by the_block_editor_meta_boxes(), it contains an empty <div id="acf_after_title-sortables" class="meta-box-sortables"></div> output by ACF_Form_Post::edit_form_after_title()do_meta_boxes( get_current_screen(), 'acf_after_title', $post ) (includes/forms/form-post.php:206).
  4. Start dragging a meta box in the editor. assets/build/css/acf-input.css (body.is-dragging-metaboxes #acf_after_title-sortables { outline: 3px dashed; min-height: 60px; }) paints the invisible container as a visible drop target (assets/src/sass/_postbox.scss:63).
  5. Drop the box into the acf_after_title drop zone.
  6. Reload the editor. The dropped box no longer appears anywhere on the screen, and get_user_option( 'meta-box-order_{$screen}' ) now contains an acf_after_title bucket with the box ID.

Expected behavior
With no field groups registered at the acf_after_title position, the hidden form should contain no #acf_after_title-sortables container, so there is no droppable target and no way to corrupt the user's meta box order. Field groups actually positioned acf_after_title must continue to render, sort, and save as before, and the hidden acf_form_data() inputs (nonce/screen/post_id) must still render on every post edit screen.

Screenshots or Video
N/A

Code
No field group or block code is involved, the bug reproduces with zero ACF field groups at the acf_after_title position and any other plugin registered meta box.

Version Information:

  • WordPress Version 6.9.x and later
  • PHP Version 8.2
  • ACF Version 6.8.9
  • Browser Chrome, Safari, Firefox

Additional context
Why the existing repair never fires, WP core's the_block_editor_meta_boxes() (wp-admin/includes/post.php):

  1. Applies filter_block_editor_meta_boxes (~line 2375), at this point the acf_after_title bucket only contains boxes registered at that position (i.e. ACF field groups with position acf_after_title). With none present, ACF_Form_Gutenberg::filter_block_editor_meta_boxes() never attaches its modify_user_option_meta_box_order repair filter (includes/forms/form-gutenberg.php:107–149).
  2. ACF's hidden-fields render (block_editor_meta_box_hidden_fieldsACF_Form_Post::edit_form_after_title()) is where the first do_meta_boxes() call happens core re-registers box IDs found under acf_after_title in the user's saved order.
  3. Only side/normal/advanced render visibly afterwards, then the box is gone.

Root causes:

  • includes/forms/form-post.php, ACF_Form_Post::add_meta_boxes() unconditionally adds add_action( 'edit_form_after_title', ... ) (~line 171), and edit_form_after_title() calls do_meta_boxes( ..., 'acf_after_title', ... ) with no guard for whether anything exists in that context. do_meta_boxes() always outputs the <div id="{context}-sortables" class="meta-box-sortables"> wrapper, even when empty.
  • includes/forms/form-gutenberg.php, block_editor_meta_box_hidden_fields() re-runs the same unguarded render inside the hidden form.

Suggested fix (render gating): in ACF_Form_Post::edit_form_after_title(), guard the do_meta_boxes( ..., 'acf_after_title', ... ) call on isset( $wp_meta_boxes[ get_current_screen()->id ]['acf_after_title'] ), keeping acf_form_data() unconditional. This removes the empty drop target while preserving rendering when the context genuinely has content, and works for both the classic and block editors since both paths call the same method.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions