Skip to content

Commit

Permalink
Footnotes: store in revisions (#52686)
Browse files Browse the repository at this point in the history
  • Loading branch information
ellatrix authored Jul 19, 2023
1 parent 0ad4169 commit 92be30f
Show file tree
Hide file tree
Showing 2 changed files with 210 additions and 2 deletions.
126 changes: 126 additions & 0 deletions packages/block-library/src/footnotes/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,129 @@ function register_block_core_footnotes() {
);
}
add_action( 'init', 'register_block_core_footnotes' );

add_action(
'wp_after_insert_post',
/**
* Saves the footnotes meta value to the revision.
*
* @param int $revision_id The revision ID.
*/
function( $revision_id ) {
$post_id = wp_is_post_revision( $revision_id );

if ( $post_id ) {
$footnotes = get_post_meta( $post_id, 'footnotes', true );

if ( $footnotes ) {
// Can't use update_post_meta() because it doesn't allow revisions.
update_metadata( 'post', $revision_id, 'footnotes', $footnotes );
}
}
}
);

add_action(
'_wp_put_post_revision',
/**
* Keeps track of the revision ID for "rest_after_insert_{$post_type}".
*
* @param int $revision_id The revision ID.
*/
function( $revision_id ) {
global $_gutenberg_revision_id;
$_gutenberg_revision_id = $revision_id;
}
);

foreach ( array( 'post', 'page' ) as $post_type ) {
add_action(
"rest_after_insert_{$post_type}",
/**
* This is a specific fix for the REST API. The REST API doesn't update
* the post and post meta in one go (through `meta_input`). While it
* does fix the `wp_after_insert_post` hook to be called correctly after
* updating meta, it does NOT fix hooks such as post_updated and
* save_post, which are normally also fired after post meta is updated
* in `wp_insert_post()`. Unfortunately, `wp_save_post_revision` is
* added to the `post_updated` action, which means the meta is not
* available at the time, so we have to add it afterwards through the
* `"rest_after_insert_{$post_type}"` action.
*
* @param WP_Post $post The post object.
*/
function( $post ) {
global $_gutenberg_revision_id;

if ( $_gutenberg_revision_id ) {
$revision = get_post( $_gutenberg_revision_id );
$post_id = $revision->post_parent;

// Just making sure we're updating the right revision.
if ( $post->ID === $post_id ) {
$footnotes = get_post_meta( $post_id, 'footnotes', true );

if ( $footnotes ) {
// Can't use update_post_meta() because it doesn't allow revisions.
update_metadata( 'post', $_gutenberg_revision_id, 'footnotes', $footnotes );
}
}
}
}
);
}

add_action(
'wp_restore_post_revision',
/**
* Restores the footnotes meta value from the revision.
*
* @param int $post_id The post ID.
* @param int $revision_id The revision ID.
*/
function( $post_id, $revision_id ) {
$footnotes = get_post_meta( $revision_id, 'footnotes', true );

if ( $footnotes ) {
update_post_meta( $post_id, 'footnotes', $footnotes );
} else {
delete_post_meta( $post_id, 'footnotes' );
}
},
10,
2
);

add_filter(
'_wp_post_revision_fields',
/**
* Adds the footnotes field to the revision.
*
* @param array $fields The revision fields.
*
* @return array The revision fields.
*/
function( $fields ) {
$fields['footnotes'] = __( 'Footnotes' );
return $fields;
}
);

add_filter(
'wp_post_revision_field_footnotes',
/**
* Gets the footnotes field from the revision.
*
* @param string $revision_field The field value, but $revision->$field
* (footnotes) does not exist.
* @param string $field The field name, in this case "footnotes".
* @param object $revision The revision object to compare against.
*
* @return string The field value.
*/
function( $revision_field, $field, $revision ) {
return get_metadata( 'post', $revision->ID, $field, true );
},
10,
3
);
86 changes: 84 additions & 2 deletions test/e2e/specs/editor/various/footnotes.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
*/
const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' );

async function getFootnotes( page ) {
async function getFootnotes( page, withoutSave = false ) {
// Save post so we can check meta.
await page.click( 'button:text("Save draft")' );
if ( ! withoutSave ) {
await page.click( 'button:text("Save draft")' );
}
await page.waitForSelector( 'button:text("Saved")' );
const footnotes = await page.evaluate( () => {
return window.wp.data
Expand Down Expand Up @@ -278,4 +280,84 @@ test.describe( 'Footnotes', () => {
},
] );
} );

test( 'works with revisions', async ( { editor, page } ) => {
await editor.canvas.click( 'role=button[name="Add default block"i]' );
await page.keyboard.type( 'first paragraph' );
await page.keyboard.press( 'Enter' );
await page.keyboard.type( 'second paragraph' );

await editor.showBlockToolbar();
await editor.clickBlockToolbarButton( 'More' );
await page.locator( 'button:text("Footnote")' ).click();

await page.keyboard.type( 'first footnote' );

const id1 = await editor.canvas.evaluate( () => {
return document.activeElement.id;
} );

await editor.canvas.click( 'p:text("first paragraph")' );

await editor.showBlockToolbar();
await editor.clickBlockToolbarButton( 'More' );
await page.locator( 'button:text("Footnote")' ).click();

await page.keyboard.type( 'second footnote' );

const id2 = await editor.canvas.evaluate( () => {
return document.activeElement.id;
} );

// This also saves the post!
expect( await getFootnotes( page ) ).toMatchObject( [
{
content: 'second footnote',
id: id2,
},
{
content: 'first footnote',
id: id1,
},
] );

await editor.canvas.click( 'p:text("first paragraph")' );

await editor.showBlockToolbar();
await editor.clickBlockToolbarButton( 'Move down' );

// This also saves the post!
expect( await getFootnotes( page ) ).toMatchObject( [
{
content: 'first footnote',
id: id1,
},
{
content: 'second footnote',
id: id2,
},
] );

// Open revisions.
await editor.openDocumentSettingsSidebar();
await page
.getByRole( 'region', { name: 'Editor settings' } )
.getByRole( 'button', { name: 'Post' } )
.click();
await page.locator( 'a:text("2 Revisions")' ).click();
await page.locator( '.revisions-controls .ui-slider-handle' ).focus();
await page.keyboard.press( 'ArrowLeft' );
await page.locator( 'input:text("Restore This Revision")' ).click();

expect( await getFootnotes( page, true ) ).toMatchObject( [
{
content: 'second footnote',
id: id2,
},
{
content: 'first footnote',
id: id1,
},
] );
} );
} );

0 comments on commit 92be30f

Please sign in to comment.