Abilities API: add core/read-nav-menus ability - #858
Conversation
|
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 If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message. To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## develop #858 +/- ##
=============================================
+ Coverage 78.44% 78.99% +0.54%
- Complexity 2454 2506 +52
=============================================
Files 104 105 +1
Lines 9925 10216 +291
=============================================
+ Hits 7786 8070 +284
- Misses 2139 2146 +7
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Registers a read-only core/read-nav-menus ability that retrieves a single nav menu (with items) by ID, slug, or registered theme location, or the full collection of menus alongside registered theme locations and their current assignments. Follows the shape of the existing core/read-users and core/read-settings abilities: a single ability per resource with a oneOf input schema, rather than split list/get abilities. Also registers a navigation ability category, since core does not yet provide one. See WordPress/mcp-adapter#181 and the core.trac.wordpress.org ticket proposing this for core.
Leftover from adapting the Users ability's pattern; the collection branch is reached by falling through the id/slug/location checks rather than a dedicated lookup-type helper, so the constant was never read.
- Fully qualify \WP_Error, \WP_Term, \WP_Post in docblock annotations (SlevomatCodingStandard.Namespaces.FullyQualifiedClassNameInAnnotation) - Invert the two nav-menu/location loops to early-continue instead of nesting the append inside the if (SlevomatCodingStandard.ControlStructures.EarlyExit) - Align consecutive assignment operators in format_menu() (Generic.Formatting.MultipleStatementAlignment) - Rename format_menu_item()'s closure param off the reserved word `class` - Read nav menu item's dynamic properties (added by wp_setup_nav_menu_item(), not part of WP_Post's real property list) via get_object_vars() instead of direct property access, since PHPStan has no way to know they exist on a plain WP_Post
5b28f3e to
145b33f
Compare
|
@gziolo @jorgefilipecosta Just a quick heads up: I've pushed a few updates to fix the initial PHPCS and PHPStan CI failures I mentioned in the PR description, and cleaned up an unused constant. All CI checks are green now, so this is ready for review whenever you have a chance. |
What?
See WordPress/mcp-adapter#181
Adds a read-only
core/read-nav-menusability: fetch a single nav menu (with its items) by ID, slug, or registered theme location, or list every menu on the site alongside the registered theme locations and their current assignments.Why?
An end-to-end agent session against a live WordPress site surfaced a gap: there's no way for an agent to list, inspect, or otherwise reason about the site's navigation menus.
wp_get_nav_menus(),wp_get_nav_menu_items(),get_registered_nav_menus(), andget_nav_menu_locations()are all stable core functions, but none of them are exposed through the Abilities API. Full details and the manual-work-cost ranking are in WordPress/mcp-adapter#181.This is proposed as a
core/*ability, following the same path ascore/read-usersandcore/read-settings: land here first for review and real-usage feedback, then a WordPress core Trac ticket and eventual merge proposal. Core Trac ticket: https://core.trac.wordpress.org/ticket/65612How?
core/read-nav-menusis one ability with aoneOfinput schema, rather than split list/get abilities, matching the shape of the existingUsersandSettingsability classes:id,slug, orlocation: returns a single menu, with its items, resolved viawp_get_nav_menu_object()andwp_get_nav_menu_items().locationresolves throughget_nav_menu_locations()and errors if nothing is assigned there.search: returns every menu (without items) plus the site's registered theme locations (get_registered_nav_menus()) and which menu is currently assigned to each (get_nav_menu_locations()).Since core does not yet register a
navigationability category, the class registers one itself onwp_abilities_api_categories_init, the same wayMain.phpregisters theaicategory, so it's easy to drop once core has its own. LikeUsersandSettings, it unregisters any core-providedcore/read-nav-menusfirst so the plugin's version always wins, and hooks onwp_abilities_api_initat priority 11 to run after core's own registration.Permission is gated behind
edit_theme_options, the same capability WordPress requires to manage menus in wp-admin. Nav menus are ordinarily public (they're what renders in the site header), but a menu not assigned to any location, or the full location-assignment map, exposes more of the site's structure than the front end does, so I went with the more conservative gate here. Happy to loosen this if reviewers think read access should be broader.Nav menu items come back from
wp_get_nav_menu_items()asWP_Postobjects, but most of the fields used here (title,url,classes, etc.) are dynamic propertieswp_setup_nav_menu_item()adds at runtime, not part ofWP_Post's declared property list.format_menu_item()reads them viaget_object_vars()rather than direct property access so static analysis doesn't flag them as undefined.Use of AI Tools
AI assistance: Yes
Tool(s): Claude Code
Model(s): Claude Sonnet 5
Used for: Writing of integration tests. Reviewed by me before this PR was opened.
Testing Instructions
Check out this branch.
Run the plugin's PHPUnit integration suite, or just
tests/Integration/Includes/Abilities/Nav_Menus/Nav_MenusTest.php. Note: this hadn't been run in CI as of opening the PR; PHPCS and PHPStan have since been fixed and are passing.Manually: register a nav menu with at least one item, assign it to a theme location, then call
core/read-nav-menus:idset to the menu's term IDslugset to the menu's sluglocationset to the theme location slugCheck the response shapes match the output schema in each case.
Changelog Entry