[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * WP_Navigation_Fallback class 4 * 5 * Manages fallback behavior for Navigation menus. 6 * 7 * @package WordPress 8 * @subpackage Navigation 9 * @since 6.3.0 10 */ 11 12 /** 13 * Manages fallback behavior for Navigation menus. 14 * 15 * @since 6.3.0 16 */ 17 class WP_Navigation_Fallback { 18 19 /** 20 * Updates the wp_navigation custom post type schema, in order to expose 21 * additional fields in the embeddable links of WP_REST_Navigation_Fallback_Controller. 22 * 23 * The Navigation Fallback endpoint may embed the full Navigation Menu object 24 * into the response as the `self` link. By default, the Posts Controller 25 * will only expose a limited subset of fields but the editor requires 26 * additional fields to be available in order to utilize the menu. 27 * 28 * Used with the `rest_wp_navigation_item_schema` hook. 29 * 30 * @since 6.4.0 31 * 32 * @param array $schema The schema for the `wp_navigation` post. 33 * @return array The modified schema. 34 */ 35 public static function update_wp_navigation_post_schema( $schema ) { 36 // Expose top level fields. 37 $schema['properties']['status']['context'] = array_merge( $schema['properties']['status']['context'], array( 'embed' ) ); 38 $schema['properties']['content']['context'] = array_merge( $schema['properties']['content']['context'], array( 'embed' ) ); 39 40 /* 41 * Exposes sub properties of content field. 42 * These sub properties aren't exposed by the posts controller by default, 43 * for requests where context is `embed`. 44 * 45 * @see WP_REST_Posts_Controller::get_item_schema() 46 */ 47 $schema['properties']['content']['properties']['raw']['context'] = array_merge( $schema['properties']['content']['properties']['raw']['context'], array( 'embed' ) ); 48 $schema['properties']['content']['properties']['rendered']['context'] = array_merge( $schema['properties']['content']['properties']['rendered']['context'], array( 'embed' ) ); 49 $schema['properties']['content']['properties']['block_version']['context'] = array_merge( $schema['properties']['content']['properties']['block_version']['context'], array( 'embed' ) ); 50 51 /* 52 * Exposes sub properties of title field. 53 * These sub properties aren't exposed by the posts controller by default, 54 * for requests where context is `embed`. 55 * 56 * @see WP_REST_Posts_Controller::get_item_schema() 57 */ 58 $schema['properties']['title']['properties']['raw']['context'] = array_merge( $schema['properties']['title']['properties']['raw']['context'], array( 'embed' ) ); 59 60 return $schema; 61 } 62 63 /** 64 * Gets (and/or creates) an appropriate fallback Navigation Menu. 65 * 66 * @since 6.3.0 67 * 68 * @return WP_Post|null the fallback Navigation Post or null. 69 */ 70 public static function get_fallback() { 71 /** 72 * Filters whether or not a fallback should be created. 73 * 74 * @since 6.3.0 75 * 76 * @param bool $create Whether to create a fallback navigation menu. Default true. 77 */ 78 $should_create_fallback = apply_filters( 'wp_navigation_should_create_fallback', true ); 79 80 $fallback = static::get_most_recently_published_navigation(); 81 82 if ( $fallback || ! $should_create_fallback ) { 83 return $fallback; 84 } 85 86 $fallback = static::create_classic_menu_fallback(); 87 88 if ( $fallback && ! is_wp_error( $fallback ) ) { 89 // Return the newly created fallback post object which will now be the most recently created navigation menu. 90 return $fallback instanceof WP_Post ? $fallback : static::get_most_recently_published_navigation(); 91 } 92 93 $fallback = static::create_default_fallback(); 94 95 if ( $fallback && ! is_wp_error( $fallback ) ) { 96 // Return the newly created fallback post object which will now be the most recently created navigation menu. 97 return $fallback instanceof WP_Post ? $fallback : static::get_most_recently_published_navigation(); 98 } 99 100 return null; 101 } 102 103 /** 104 * Finds the most recently published `wp_navigation` post type. 105 * 106 * @since 6.3.0 107 * 108 * @return WP_Post|null the first non-empty Navigation or null. 109 */ 110 private static function get_most_recently_published_navigation() { 111 112 $parsed_args = array( 113 'post_type' => 'wp_navigation', 114 'no_found_rows' => true, 115 'update_post_meta_cache' => false, 116 'update_post_term_cache' => false, 117 'order' => 'DESC', 118 'orderby' => 'date', 119 'post_status' => 'publish', 120 'posts_per_page' => 1, 121 ); 122 123 $navigation_post = new WP_Query( $parsed_args ); 124 125 if ( count( $navigation_post->posts ) > 0 ) { 126 return $navigation_post->posts[0]; 127 } 128 129 return null; 130 } 131 132 /** 133 * Creates a Navigation Menu post from a Classic Menu. 134 * 135 * @since 6.3.0 136 * 137 * @return int|WP_Error The post ID of the default fallback menu or a WP_Error object. 138 */ 139 private static function create_classic_menu_fallback() { 140 // See if we have a classic menu. 141 $classic_nav_menu = static::get_fallback_classic_menu(); 142 143 if ( ! $classic_nav_menu ) { 144 return new WP_Error( 'no_classic_menus', __( 'No Classic Menus found.' ) ); 145 } 146 147 // If there is a classic menu then convert it to blocks. 148 $classic_nav_menu_blocks = WP_Classic_To_Block_Menu_Converter::convert( $classic_nav_menu ); 149 150 if ( is_wp_error( $classic_nav_menu_blocks ) ) { 151 return $classic_nav_menu_blocks; 152 } 153 154 if ( empty( $classic_nav_menu_blocks ) ) { 155 return new WP_Error( 'cannot_convert_classic_menu', __( 'Unable to convert Classic Menu to blocks.' ) ); 156 } 157 158 // Create a new navigation menu from the classic menu. 159 $classic_menu_fallback = wp_insert_post( 160 array( 161 'post_content' => $classic_nav_menu_blocks, 162 'post_title' => $classic_nav_menu->name, 163 'post_name' => $classic_nav_menu->slug, 164 'post_status' => 'publish', 165 'post_type' => 'wp_navigation', 166 ), 167 true // So that we can check whether the result is an error. 168 ); 169 170 return $classic_menu_fallback; 171 } 172 173 /** 174 * Determines the most appropriate classic navigation menu to use as a fallback. 175 * 176 * @since 6.3.0 177 * 178 * @return WP_Term|null The most appropriate classic navigation menu to use as a fallback. 179 */ 180 private static function get_fallback_classic_menu() { 181 $classic_nav_menus = wp_get_nav_menus(); 182 183 if ( ! $classic_nav_menus || is_wp_error( $classic_nav_menus ) ) { 184 return null; 185 } 186 187 $nav_menu = static::get_nav_menu_at_primary_location(); 188 189 if ( $nav_menu ) { 190 return $nav_menu; 191 } 192 193 $nav_menu = static::get_nav_menu_with_primary_slug( $classic_nav_menus ); 194 195 if ( $nav_menu ) { 196 return $nav_menu; 197 } 198 199 return static::get_most_recently_created_nav_menu( $classic_nav_menus ); 200 } 201 202 203 /** 204 * Sorts the classic menus and returns the most recently created one. 205 * 206 * @since 6.3.0 207 * 208 * @param WP_Term[] $classic_nav_menus Array of classic nav menu term objects. 209 * @return WP_Term The most recently created classic nav menu. 210 */ 211 private static function get_most_recently_created_nav_menu( $classic_nav_menus ) { 212 usort( 213 $classic_nav_menus, 214 static function ( $a, $b ) { 215 return $b->term_id - $a->term_id; 216 } 217 ); 218 219 return $classic_nav_menus[0]; 220 } 221 222 /** 223 * Returns the classic menu with the slug `primary` if it exists. 224 * 225 * @since 6.3.0 226 * 227 * @param WP_Term[] $classic_nav_menus Array of classic nav menu term objects. 228 * @return WP_Term|null The classic nav menu with the slug `primary` or null. 229 */ 230 private static function get_nav_menu_with_primary_slug( $classic_nav_menus ) { 231 foreach ( $classic_nav_menus as $classic_nav_menu ) { 232 if ( 'primary' === $classic_nav_menu->slug ) { 233 return $classic_nav_menu; 234 } 235 } 236 237 return null; 238 } 239 240 241 /** 242 * Gets the classic menu assigned to the `primary` navigation menu location 243 * if it exists. 244 * 245 * @since 6.3.0 246 * 247 * @return WP_Term|null The classic nav menu assigned to the `primary` location or null. 248 */ 249 private static function get_nav_menu_at_primary_location() { 250 $locations = get_nav_menu_locations(); 251 252 if ( isset( $locations['primary'] ) ) { 253 $primary_menu = wp_get_nav_menu_object( $locations['primary'] ); 254 255 if ( $primary_menu ) { 256 return $primary_menu; 257 } 258 } 259 260 return null; 261 } 262 263 /** 264 * Creates a default Navigation Block Menu fallback. 265 * 266 * @since 6.3.0 267 * 268 * @return int|WP_Error The post ID of the default fallback menu or a WP_Error object. 269 */ 270 private static function create_default_fallback() { 271 272 $default_blocks = static::get_default_fallback_blocks(); 273 274 // Create a new navigation menu from the fallback blocks. 275 $default_fallback = wp_insert_post( 276 array( 277 'post_content' => $default_blocks, 278 'post_title' => _x( 'Navigation', 'Title of a Navigation menu' ), 279 'post_name' => 'navigation', 280 'post_status' => 'publish', 281 'post_type' => 'wp_navigation', 282 ), 283 true // So that we can check whether the result is an error. 284 ); 285 286 return $default_fallback; 287 } 288 289 /** 290 * Gets the rendered markup for the default fallback blocks. 291 * 292 * @since 6.3.0 293 * 294 * @return string default blocks markup to use a the fallback. 295 */ 296 private static function get_default_fallback_blocks() { 297 $registry = WP_Block_Type_Registry::get_instance(); 298 299 // If `core/page-list` is not registered then use empty blocks. 300 return $registry->is_registered( 'core/page-list' ) ? '<!-- wp:page-list /-->' : ''; 301 } 302 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Fri Jun 27 08:20:01 2025 | Cross-referenced by PHPXref |