Skip to content

Actions API: Register the homepage and posts page actions as entity actions - #84646

Closed
jorgefilipecosta wants to merge 8 commits into
trunkfrom
add/homepage-entity-actions
Closed

jorgefilipecosta wants to merge 8 commits into
trunkfrom
add/homepage-entity-actions

Conversation

@jorgefilipecosta

@jorgefilipecosta jorgefilipecosta commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

Part of #83537.

Fixes #76668.

Right now "Set as homepage" and "Set as posts page" are not entity actions. usePostActions() creates them with hooks that read the site settings and adds them to the actions the server returns, so wp_unregister_entity_actions( 'postType', 'page', array( 'set-as-homepage' ) ) doesn't remove them. They are hooks because their eligibility depends on the data of the stores (the homepage, the posts page and the front page template), while isEligible only receives the item.

This PR follows Approach C of #76668:

  • Makes isEligible receive { registry } as its second argument, as callback already does.
  • Makes DataViews evaluate isEligible in useSelect in the item actions, the list and activity layouts, the bulk actions and the picker footer, so the eligibility is evaluated again when the data it reads through the registry changes. The table and grid layouts also pass the registry to the check that decides if a row can be selected.
  • Does the same in PostActions, the actions menu of the editor inspector.
  • Moves set-as-homepage and set-as-posts-page to a new page collection of @wordpress/core-actions as plain actions, with their label and modal options in action.php and their isEligible and modal in action.tsx. Their isEligible keeps the checks the hooks did: the page is published, it isn't the homepage or the posts page already, and the theme has no front-page template.
  • Registers them on the server for pages when the user has manage_options, the capability behind canUser( 'update', { kind: 'root', name: 'site' } ), and removes them from usePostActions(). They are registered after the other core actions, so the core actions keep the order they have on trunk.

Some notes:

  • Only the actions that read the stores add work when the stores change, because useSelect only subscribes to the stores its callback reads. On wp-env, with 20 pages in the pages list, I dispatched 500 changes to the core store that no action reads (median of 7 runs): a change took 186 µs on trunk, 369 µs with this PR, and 183 µs with this PR and the two homepage actions unregistered. So the extra cost, about 9 µs per row, comes from the two actions reading the site settings and the templates, and the actions that only check the item cost the same as before. Loading the pages list until the action menus show took about 1.47 s in both.
  • I considered passing the data the actions need with the items, but the homepage and the posts page are site settings, not data of each page. DataViews already passes the registry to callback, so I opted for passing it to isEligible too, which keeps both functions symmetric. The actions read the stores through that registry, not the global select(), which was the concern in Data Views: Add action for pages to set site homepage #65426.
  • isEligible implementations that only take the item keep working. Code that calls isEligible outside DataViews should pass the registry, as it does for callback.
  • The server only registers the two actions for the users who can manage the site settings, so the client no longer checks canUser( 'update', { kind: 'root', name: 'site' } ) before showing them.
  • A plugin action registered for pages now comes after the two actions, because the core actions, these two included, are registered first. On trunk, usePostActions() adds them after every action the server returns.
  • The strings of the modals and the notices of the two actions are now in a script module, so, like the other core actions, they are not translated until Script Modules: Fix missing translations of script modules #84651 lands. Their labels come from PHP and are translated.

Testing Instructions

  1. Start wp-env with a block theme that has no front-page template (e.g. Twenty Twenty-Four), and create two published pages, "Page A" and "Page B".

  2. Go to http://localhost:8888/wp-admin/site-editor.php?p=%2Fpage, open the actions of "Page A", and verify "Set as homepage…" and "Set as posts page…" show after the other actions, as on trunk.

  3. Set "Page A" as the homepage. Without reloading, open its actions again and verify neither action shows, while "Page B" still has both.

  4. Open "Page B" in the post editor, set it as the posts page from the actions menu of the inspector, and verify that, without reloading, the menu no longer shows either action.

  5. Add this mu-plugin, reload the pages list, and verify the actions of "Sample Page" no longer show "Set as homepage…" and still show "Set as posts page…". On trunk, "Set as homepage…" stays.

    <?php
    add_action( 'wp_entity_actions_api_init', function () {
    	wp_unregister_entity_actions( 'postType', 'page', array( 'set-as-homepage' ) );
    } );
    
  6. Log in as an editor and verify neither action shows in the pages list or in the inspector.

  7. Enable the "Extensible Site Editor" experiment in Gutenberg > Experiments, and repeat steps 2, 3 and 5 with two new pages in http://localhost:8888/wp-admin/admin.php?page=site-editor-v2&p=%2Ftypes%2Fpage.

AI usage disclosure: fix and description drafted with AI assistance and reviewed by me.

@jorgefilipecosta jorgefilipecosta added [Type] Enhancement A suggestion for improvement. [Feature] Extensibility The ability to extend blocks or the editing experience [Package] Editor /packages/editor [Feature] DataViews Work surrounding upgrading and evolving views in the site editor and beyond [Package] DataViews /packages/dataviews labels Oct 13, 2026
@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

🤖 PR meta 🤖

📦 Bundle size

Size Change: +785 B (+0.01%)

Total Size: 8.85 MB

📦 View Changed
Filename Size Change
build/modules/core-actions/page.min.js 1.77 kB +1.77 kB (new file) 🆕
build/scripts/block-editor/index.min.js 597 kB -5 B (0%)
build/scripts/edit-site/index.min.js 397 kB +6 B (0%)
build/scripts/editor/index.min.js 608 kB -1.05 kB (-0.17%)
build/scripts/media-utils/index.min.js 238 kB +59 B (+0.02%)

dd20e58 Run

⚡ Performance

Show the results

Client side metrics exclude the server response time.

front-end-block-theme

Metric 962bba5 trunk % Change
timeToFirstByte 55.35 ms +2.44% -3.7% 55 ms +4.55% -1.64% 0.64%
largestContentfulPaint 94 ms +8.51% -2.13% 92 ms +8.7% -4.35% 2.17%
lcpMinusTtfb 39.55 ms +14.41% -10.11% 35.1 ms +19.37% -3.7% 12.68%
wpBeforeTemplate 29.35 ms +5.25% -2.69% 29.13 ms +6.01% -1.3% 0.76%
wpTemplate 21.37 ms +3.93% -3.46% 21.58 ms +1.76% -3.06% -0.97%
wpTotal 51.52 ms +2.5% -3.75% 51.03 ms +5.15% -1.37% 0.96%
wpMemoryUsage 7.62 MB +0% -0% 7.58 MB +0% -0% 0.47%
wpDbQueries 17 +0% -0% 17 +0% -0% 0%

front-end-classic-theme

Metric 962bba5 trunk % Change
timeToFirstByte 43.05 ms +4.99% -2.44% 41.95 ms +5.6% -0.72% 2.62%
largestContentfulPaint 92 ms +4.35% -0% 92 ms +2.17% -4.35% 0%
lcpMinusTtfb 50 ms +1.7% -1.2% 48.45 ms +3.3% -3.82% 3.2%
wpBeforeTemplate 27.34 ms +7.21% -1.68% 26.69 ms +3.56% -1.42% 2.44%
wpTemplate 12.3 ms +3.58% -1.54% 12.21 ms +3.52% -1.06% 0.74%
wpTotal 40.08 ms +5.59% -2.12% 39.1 ms +6.04% -0.87% 2.51%
wpMemoryUsage 6.17 MB +0% -0% 6.13 MB +0% -0% 0.6%
wpDbQueries 10 +0% -0% 10 +0% -0% 0%

media-processing

Metric 962bba5 trunk % Change
mediaProcessingJpeg 404.31 ms +0.26% -0.89% 408.21 ms +0.45% -0.91% -0.96%
mediaProcessingAvif 6099.43 ms +0.3% -0.11% 6085.12 ms +0.07% -0.09% 0.24%
mediaProcessingJpegToAvif 4208.85 ms +0.13% -0.16% 4194.14 ms +0.38% -0.37% 0.35%

media-upload

Metric 962bba5 trunk % Change
jpegUploadProcessing 1680.62 ms +16.14% -15.98% 1421.47 ms +0.71% -0.3% 18.23%
pngUploadProcessing 221.76 ms +17.1% -11.3% 191.77 ms +15.47% -5.61% 15.64%
largeJpegUploadProcessing 1419.04 ms +0.99% -0.64% 1411.86 ms +1.18% -0.3% 0.51%
multipleImageUploadProcessing 1597.46 ms +0.53% -1.6% 1578 ms +1.05% -0.6% 1.23%

post-editor

Metric 962bba5 trunk % Change
serverResponse 274.82 ms +8.46% -6.55% 280.17 ms +3.06% -6.55% -1.91%
firstPaint 326.08 ms +11.17% -38.89% 327.16 ms +12.38% -48.34% -0.33%
domContentLoaded 777.18 ms +4.83% -3.87% 774.09 ms +4.05% -1.65% 0.4%
loaded 778.15 ms +4.82% -3.86% 775.2 ms +4.08% -1.66% 0.38%
firstContentfulPaint 357.13 ms +3.96% -4.52% 365.56 ms +1.62% -5.79% -2.31%
firstBlock 2307.12 ms +2.48% -1.75% 2329.07 ms +1.09% -2.77% -0.94%
type 14.68 ms +5.72% -3.68% 16.32 ms +4.47% -4.29% -10.05%
typeWithoutInspector 15.22 ms +11.3% -7.82% 16.05 ms +6.92% -2.74% -5.17%
typeWithTopToolbar 20.81 ms +1.68% -8.46% 19.24 ms +7.38% -4.11% 8.16%
typeContainer 7.78 ms +2.19% -8.23% 7.06 ms +14.31% -4.96% 10.2%
focus 91.7 ms +7.34% -3.96% 82.55 ms +6.4% -4.87% 11.08%
firstFocus 167.78 ms +0% -0% 160.31 ms +0% -0% 4.66%
selectAll 361.81 ms +0.9% -2.52% 383.07 ms +2.09% -1.38% -5.55%
listViewOpen 54.84 ms +4.98% -8.04% 49.65 ms +14.72% -6.45% 10.45%
inserterOpen 29.88 ms +12.85% -3.55% 28.63 ms +19.32% -3.46% 4.37%
inserterHover 10.35 ms +6.67% -8.41% 9.88 ms +8% -5.06% 4.76%
inserterSearch 6.84 ms +11.7% -2.63% 6.8 ms +8.24% -6.32% 0.59%
loadPatterns 493.77 ms +2.7% -1.69% 481.71 ms +6.14% -4.33% 2.5%
wpTotal 267.22 ms +8.25% -6.59% 273.07 ms +2.72% -6.65% -2.14%
wpMemoryUsage 13.51 MB +0% -0% 13.47 MB +0% -0% 0.3%
wpDbQueries 54 +0% -0% 54 +0% -0% 0%

site-editor

Metric 962bba5 trunk % Change
serverResponse 286.62 ms +6.64% -8.7% 278.23 ms +3.1% -5.94% 3.02%
firstPaint 203.91 ms +10.95% -6.94% 225.74 ms +20.32% -16.82% -9.67%
domContentLoaded 864.25 ms +2.94% -3.09% 843.91 ms +1.01% -3.17% 2.41%
loaded 865.03 ms +2.93% -3.1% 844.74 ms +1.02% -3.17% 2.4%
firstContentfulPaint 373.46 ms +4.02% -2.55% 365.04 ms +1.02% -1.76% 2.31%
firstBlock 3137.36 ms +5.59% -1.27% 3165.39 ms +0.71% -1.09% -0.89%
type 14.98 ms +3.81% -1.2% 15.94 ms +4.33% -2.63% -6.02%
navigate 92.13 ms +4.84% -1.86% 91.15 ms +13.46% -3.03% 1.08%
loadPatterns 889.5 ms +13.56% -8.68% 1166.79 ms +6.98% -14.65% -23.77%
loadPages 1043.54 ms +1.13% -2.78% 1035.04 ms +6.52% -2.91% 0.82%
wpTotal 279.35 ms +6.75% -8.94% 270.85 ms +3.21% -6.01% 3.14%
wpMemoryUsage 12.57 MB +0% -0% 12.53 MB +0% -0% 0.32%
wpDbQueries 43 +2.33% -0% 43.5 +1.15% -1.15% -1.15%

dd20e58 Run

@jorgefilipecosta

Copy link
Copy Markdown
Member Author

Although I think isEligible should receive the registry like callback does, as in multiple cases checking information outside the item itself is required to know if the action is eligible or not, like deleting a product may require checking that there are no pending orders with that product. I'm giving a try to an alternative implementation relying on REST link information avoid us the need to do the change here.

@oandregal oandregal mentioned this pull request Oct 15, 2026
…elect`

`callback` receives the data registry, but `isEligible` only received
the item, so an action whose eligibility depends on the data of the
stores had to read that data in a hook and be recreated when it
changed. Pass `{ registry }` to `isEligible` too, and evaluate it in
`useSelect` where DataViews filters the actions: the item actions, the
list and activity layouts, the bulk actions and the picker footer.
`useSelect` subscribes to the stores `isEligible` reads, so the
eligibility is evaluated again when their data changes.

The picker footer evaluates the eligibility per action, so each of its
buttons moves to its own component. The table and grid layouts pass
the registry to the check that decides whether a row can be selected.
`PostActions`, the actions menu of the editor inspector, filters the
actions of the post with `isEligible`. Pass `{ registry }` as DataViews
does, and evaluate it in `useSelect`, so the menu is updated when the
data an action reads changes.
`usePostActions()` added "Set as homepage" and "Set as posts page" to
the actions it read from the registry, so `unregisterEntityAction()`
could not remove them. Make them plain actions whose `isEligible`
reads the site settings and the front page template through the
registry, and register them in `registerPostTypeSchema()` for pages
when the user can update the site settings. They are registered last,
so the menus keep the order they had.
`useSelect` calls its callback with `( select, registry )`, so pass
that registry to `isEligible` instead of the one `useRegistry()`
returns, and drop it from the dependencies.

A component that still calls `useRegistry()` for the action callbacks
can't name the callback parameter `registry` (`no-shadow`), so the
filters move to two DataViews hooks: `useEligibleActions()` for the
item actions and the list and activity layouts, and
`useEligibleItems()` for the bulk actions and picker footer buttons.
The `useSelect` mock of the post actions test passes the registry too.
@jorgefilipecosta
jorgefilipecosta force-pushed the add/homepage-entity-actions branch from ed0ad29 to f3390f8 Compare October 16, 2026 11:31
@github-actions github-actions Bot added the [Package] Fields /packages/fields label Oct 16, 2026
@github-actions github-actions Bot added the [Package] E2E Tests /packages/e2e-tests label Oct 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Feature] DataViews Work surrounding upgrading and evolving views in the site editor and beyond [Feature] Extensibility The ability to extend blocks or the editing experience [Package] DataViews /packages/dataviews [Package] E2E Tests /packages/e2e-tests [Package] Editor /packages/editor [Package] Fields /packages/fields [Type] Enhancement A suggestion for improvement.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Register set-as-homepage and set-as-posts-page as entity actions

1 participant