diff --git a/includes/Admin/TranslationPicker/Page.php b/includes/Admin/TranslationPicker/Page.php index 85e9e9ca..d39d73eb 100644 --- a/includes/Admin/TranslationPicker/Page.php +++ b/includes/Admin/TranslationPicker/Page.php @@ -50,7 +50,7 @@ class Page { */ public static function init(): void { add_action( 'admin_menu', array( self::class, 'register' ) ); - // Late-priority reorder: put our entries right under "All Posts" + // Late-priority reorder: put our entries right under "Add New" // regardless of what other plugins do to the submenu array. add_action( 'admin_menu', array( self::class, 'reorder_submenu' ), 999 ); @@ -154,7 +154,7 @@ static function () use ( $post_type ) { /** * Moves each of our submenu entries to the slot directly after the - * parent's first item ("All Posts" / "All Pages" / "All CPTs"). Runs + * parent's "Add New" item (or "All Posts" if that is missing). Runs * on admin_menu at priority 999 so it applies after every other * plugin has finished adding entries. * @@ -195,13 +195,18 @@ public static function reorder_submenu(): void { $our_item = $submenu[ $parent ][ $our_key ]; unset( $submenu[ $parent ][ $our_key ] ); - // Locate the "All Posts" entry by its slug rather than assuming - // it is the first item — another plugin could have prepended. - $all_key = null; - foreach ( $submenu[ $parent ] as $k => $item ) { - if ( isset( $item[2] ) && $parent === $item[2] ) { - $all_key = $k; - break; + // Anchor below core's "Add New" — the picker is another way to add + // a post, and wedged between "All Posts" and "Add New" it reads like + // broken styling. Fall back to "All Posts" when "Add New" is gone. + // Both are located by slug: another plugin could have prepended. + $anchors = array( self::new_slug( $post_type ), $parent ); + $anchor = null; + foreach ( $anchors as $anchor_slug ) { + foreach ( $submenu[ $parent ] as $k => $item ) { + if ( isset( $item[2] ) && $anchor_slug === $item[2] ) { + $anchor = $k; + break 2; + } } } @@ -209,7 +214,7 @@ public static function reorder_submenu(): void { $placed = false; foreach ( $submenu[ $parent ] as $k => $item ) { $rebuilt[ $k ] = $item; - if ( ! $placed && ( null === $all_key || $k === $all_key ) ) { + if ( ! $placed && ( null === $anchor || $k === $anchor ) ) { $rebuilt[] = $our_item; $placed = true; } @@ -241,6 +246,15 @@ public static function parent_slug( string $post_type ): string { return 'post' === $post_type ? 'edit.php' : 'edit.php?post_type=' . $post_type; } + /** + * Slug of core's "Add New" submenu entry for the post type. + * + * @param string $post_type Post type whose "Add New" entry is wanted. + */ + public static function new_slug( string $post_type ): string { + return 'post' === $post_type ? 'post-new.php' : 'post-new.php?post_type=' . $post_type; + } + /** * Unique page slug per post type. Needed because WordPress enforces * globally unique submenu slugs, so we can't reuse one slug under diff --git a/tests/phpunit/Admin/TranslationPicker/TestPage.php b/tests/phpunit/Admin/TranslationPicker/TestPage.php index c6cd1888..f5dbb6c6 100644 --- a/tests/phpunit/Admin/TranslationPicker/TestPage.php +++ b/tests/phpunit/Admin/TranslationPicker/TestPage.php @@ -54,6 +54,11 @@ public function test_parent_slug( string $post_type, string $expected ): void { $this->assertSame( $expected, Page::parent_slug( $post_type ) ); } + public function test_new_slug(): void { + $this->assertSame( 'post-new.php', Page::new_slug( 'post' ) ); + $this->assertSame( 'post-new.php?post_type=page', Page::new_slug( 'page' ) ); + } + public function test_url_uses_admin_url_and_query_arg(): void { Functions\expect( 'admin_url' ) ->once()