diff --git a/packages/block-library/src/footnotes/index.php b/packages/block-library/src/footnotes/index.php index 9815033820406..712cd571c8b0e 100644 --- a/packages/block-library/src/footnotes/index.php +++ b/packages/block-library/src/footnotes/index.php @@ -212,3 +212,76 @@ function wp_get_footnotes_from_revision( $revision_field, $field, $revision ) { return get_metadata( 'post', $revision->ID, $field, true ); } add_filter( 'wp_post_revision_field_footnotes', 'wp_get_footnotes_from_revision', 10, 3 ); + +/** + * The REST API autosave endpoint doesn't save meta, so we can use the + * `wp_creating_autosave` when it updates an exiting autosave, and + * `_wp_put_post_revision` when it creates a new autosave. + * + * @since 6.3.0 + * + * @param int|array $autosave The autosave ID or array. + */ +function _wp_rest_api_autosave_meta( $autosave ) { + // Ensure it's a REST API request. + if ( ! defined( 'REST_REQUEST' ) || ! REST_REQUEST ) { + return; + } + + $body = rest_get_server()->get_raw_data(); + $body = json_decode( $body, true ); + + if ( ! isset( $body['meta']['footnotes'] ) ) { + return; + } + + // `wp_creating_autosave` passes the array, + // `_wp_put_post_revision` passes the ID. + $id = is_int( $autosave ) ? $autosave : $autosave['ID']; + + if ( ! $id ) { + return; + } + + update_post_meta( $id, 'footnotes', $body['meta']['footnotes'] ); +} +// See https://github.com/WordPress/wordpress-develop/blob/2103cb9966e57d452c94218bbc3171579b536a40/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php#L391C1-L391C1. +add_action( 'wp_creating_autosave', '_wp_rest_api_autosave_meta' ); +// See https://github.com/WordPress/wordpress-develop/blob/2103cb9966e57d452c94218bbc3171579b536a40/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php#L398. +// Then https://github.com/WordPress/wordpress-develop/blob/2103cb9966e57d452c94218bbc3171579b536a40/src/wp-includes/revision.php#L367. +add_action( '_wp_put_post_revision', '_wp_rest_api_autosave_meta' ); + +/** + * This is a workaround for the autosave endpoint returning early if the + * revision field are equal. The problem is that "footnotes" is not real + * revision post field, so there's nothing to compare against. + * + * This trick sets the "footnotes" field (value doesn't matter), which will + * cause the autosave endpoint to always update the latest revision. That should + * be fine, it should be ok to update the revision even if nothing changed. Of + * course, this is temporary fix. + * + * @since 6.3.0 + * + * @param WP_Post $prepared_post The prepared post object. + * @param WP_REST_Request $request The request object. + * + * See https://github.com/WordPress/wordpress-develop/blob/2103cb9966e57d452c94218bbc3171579b536a40/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php#L365-L384. + * See https://github.com/WordPress/wordpress-develop/blob/2103cb9966e57d452c94218bbc3171579b536a40/src/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php#L219. + */ +function _wp_rest_api_force_autosave_difference( $prepared_post, $request ) { + // We only want to be altering POST requests. + if ( $request->get_method() !== 'POST' ) { + return $prepared_post; + } + + // Only alter requests for the '/autosaves' route. + if ( substr( $request->get_route(), -strlen( '/autosaves' ) ) !== '/autosaves' ) { + return $prepared_post; + } + + $prepared_post->footnotes = '[]'; + return $prepared_post; +} + +add_filter( 'rest_pre_insert_post', '_wp_rest_api_force_autosave_difference', 10, 2 ); diff --git a/packages/core-data/src/actions.js b/packages/core-data/src/actions.js index 824e1159286ed..bfe4a4a185f3f 100644 --- a/packages/core-data/src/actions.js +++ b/packages/core-data/src/actions.js @@ -551,9 +551,12 @@ export const saveEntityRecord = data = Object.keys( data ).reduce( ( acc, key ) => { if ( - [ 'title', 'excerpt', 'content' ].includes( - key - ) + [ + 'title', + 'excerpt', + 'content', + 'meta', + ].includes( key ) ) { acc[ key ] = data[ key ]; } diff --git a/packages/editor/src/store/selectors.js b/packages/editor/src/store/selectors.js index d8be5d8e00af6..ae62492e79042 100644 --- a/packages/editor/src/store/selectors.js +++ b/packages/editor/src/store/selectors.js @@ -611,8 +611,8 @@ export const isEditedPostAutosaveable = createRegistrySelector( return true; } - // If the title or excerpt has changed, the post is autosaveable. - return [ 'title', 'excerpt' ].some( + // If title, excerpt, or meta have changed, the post is autosaveable. + return [ 'title', 'excerpt', 'meta' ].some( ( field ) => getPostRawValue( autosave[ field ] ) !== getEditedPostAttribute( state, field ) diff --git a/test/e2e/specs/editor/various/footnotes.spec.js b/test/e2e/specs/editor/various/footnotes.spec.js index ad3ab149095d2..b96d4530cb499 100644 --- a/test/e2e/specs/editor/various/footnotes.spec.js +++ b/test/e2e/specs/editor/various/footnotes.spec.js @@ -360,4 +360,44 @@ test.describe( 'Footnotes', () => { }, ] ); } ); + + test( 'can be previewed when published', async ( { editor, page } ) => { + await editor.canvas.click( 'role=button[name="Add default block"i]' ); + await page.keyboard.type( 'a' ); + + await editor.showBlockToolbar(); + await editor.clickBlockToolbarButton( 'More' ); + await page.locator( 'button:text("Footnote")' ).click(); + + await page.keyboard.type( '1' ); + + // Publish post. + await editor.publishPost(); + + await editor.canvas.click( 'ol.wp-block-footnotes li span' ); + await page.keyboard.press( 'End' ); + await page.keyboard.type( '2' ); + + const editorPage = page; + const previewPage = await editor.openPreviewPage(); + + await expect( + previewPage.locator( 'ol.wp-block-footnotes li' ) + ).toHaveText( '12 ↩︎' ); + + await previewPage.close(); + await editorPage.bringToFront(); + + // Test again, this time with an existing revision (different code + // path). + await editor.canvas.click( 'ol.wp-block-footnotes li span' ); + await page.keyboard.press( 'End' ); + await page.keyboard.type( '3' ); + + const previewPage2 = await editor.openPreviewPage(); + + await expect( + previewPage2.locator( 'ol.wp-block-footnotes li' ) + ).toHaveText( '123 ↩︎' ); + } ); } );