[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

Search

title

Body

[close]

/wp-includes/ -> class-wp-navigation-fallback.php (source)

   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  }


Generated : Fri Jun 27 08:20:01 2025 Cross-referenced by PHPXref