Skip to content

Editor: Restore revisions on the server - #82158

Closed
ellatrix wants to merge 1 commit into
trunkfrom
restore-revisions-on-the-server
Closed

ellatrix wants to merge 1 commit into
trunkfrom
restore-revisions-on-the-server

Conversation

@ellatrix

@ellatrix ellatrix commented Aug 28, 2026 •

Copy link
Copy Markdown
Member

What?

Follow up to #78249, which turned Visual Revisions off whenever classic meta boxes are registered, as a way around the restore bug in #78130.

Restoring a revision now happens on the server, and Visual Revisions works again when classic meta boxes are present.

Why?

The editor restored a revision by applying it as edits and saving the post. That has two problems:

  1. The meta boxes overwrote the restore. Their form is submitted after the REST save, carrying whatever the DOM still holds, so meta owned by a classic meta box stayed at the newer value. Even skipping that one submission would not be enough: the fields would still show the old values and put them back on the next save.
  2. Only the fields the editor knows about were restored. Meta that is not exposed to the REST API, and anything plugins restore on wp_restore_post_revision, were left alone. The classic screen restores all of it.

How?

  1. A route to restore a revision. POST /wp/v2/<base>/<id>/revisions/<id>/restore runs wp_restore_post_revision(), the same function revision.php uses. It is added to Gutenberg_REST_Revisions_Controller, and to a new Gutenberg_REST_Template_Revisions_Controller so templates keep working.
  2. The editor loads again after a restore. There is no save, so no meta box submission, and the meta boxes render from PHP with the restored values. The confirmation notice is carried across the load.
  3. disableVisualRevisions is removed, along with the classic screen links on the two revisions controls, the deep link redirect, and the autosave notice branch that read it.
  4. Tests. PHPUnit covers the route, including revisioned meta and permissions. An e2e test fills a classic meta box, saves twice, restores, and checks the field holds its older value.

Testing Instructions

  1. Activate the gutenberg-test-plugin-revisions-meta-box test plugin, or any plugin with a classic meta box that saves meta registered with revisions_enabled.
  2. Create a post, fill the meta box field, and save. Change both the content and the field, then save again.
  3. Open Revisions in the sidebar, slide back to the older revision, and press Restore.
  4. The editor loads again with the older content, the meta box field holds its older value, and a notice confirms which revision was restored.
  5. Save again and confirm the restored value stays.

Also worth checking on a post type without meta boxes, and on a template in the site editor.

Testing Instructions for Keyboard

Tab to Revisions in the sidebar and press Enter, move through the revisions with the slider arrow keys, then tab to Restore and press Enter.

Screenshots or screencast

Before After
The classic revisions screen the Revisions control sends you to today Visual Revisions open on a post that has a classic meta box

After restoring, the classic meta box field holds the restored value:

The editor after a restore, with the meta box field back to its older value and a notice confirming the restore

Use of AI Tools

Authored with Claude Code assistance.

Restoring a revision applied it as edits and saved the post, which left
values that classic meta boxes save through their own form submission at
the newer version, and resubmitted them on the next save. It also only
covered the fields the editor knows about.

Restoring now runs wp_restore_post_revision through a new REST route and
loads the editor again, the way the classic revisions screen works, so
everything a revision holds comes back and the meta boxes render with
the restored values.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Warning: Type of PR label mismatch

To merge this PR, it requires exactly 1 label indicating the type of PR. Other labels are optional and not being checked here.

  • Required label: Any label starting with [Type].
  • Labels found: .

Read more about Type labels in Gutenberg. Don't worry if you don't have the required permissions to add labels; the PR reviewer should be able to help with the task.

@github-actions github-actions Bot added [Package] Editor /packages/editor [Package] Edit Post /packages/edit-post [Package] E2E Tests /packages/e2e-tests labels Aug 28, 2026
@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.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Co-authored-by: ellatrix <ellatrix@git.wordpress.org>

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

@github-actions

Copy link
Copy Markdown

Size Change: -330 B (0%)

Total Size: 7.91 MB

📦 View Changed
Filename Size Change
build/scripts/edit-post/index.min.js 52.4 kB -338 B (-0.64%)
build/scripts/editor/index.min.js 580 kB +8 B (0%)

compressed-size-action

@github-actions

Copy link
Copy Markdown

Flaky tests detected in 96a9518.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/33139968420
📝 Reported tests:

Navigates the items list via UP/DOWN arrow keys in /test/e2e/specs/site-editor/dataviews-list-layout-keyboard.spec.js, passed after 1 failed attempt.
Error: expect(locator).toBeFocused() failed

Locator:  getByLabel('Page Two')
Expected: focused
Received: inactive
Timeout:  5000ms

Call log:
  - Expect "toBeFocused" with timeout 5000ms
  - waiting for getByLabel('Page Two')
    14 × locator resolved to <button type="button" tabindex="-1" aria-pressed="false" id="view-list-0-243-item-wrapper" class="dataviews-view-list__item" aria-labelledby="view-list-0-243-label" aria-describedby="view-list-0-243-description"></button>
       - unexpected value "inactive"

    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/site-editor/dataviews-list-layout-keyboard.spec.js:112:49
Should save the changes in /test/e2e/specs/editor/plugins/wp-editor-meta-box.spec.js, passed after 2 failed attempts.
TimeoutError: page.waitForFunction: Timeout 10000ms exceeded.
    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/plugins/wp-editor-meta-box.spec.js:34:14
TimeoutError: locator.click: Timeout 10000ms exceeded.
Call log:
  - waiting for locator('role=button[name="Visual"i]')

    at /home/runner/work/gutenberg/gutenberg/test/e2e/specs/editor/plugins/wp-editor-meta-box.spec.js:33:55

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

Labels

[Package] E2E Tests /packages/e2e-tests [Package] Edit Post /packages/edit-post [Package] Editor /packages/editor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant