[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

title

Body

[close]

/wp-includes/ -> theme.php (source)

   1  <?php
   2  /**
   3   * Theme, template, and stylesheet functions.
   4   *
   5   * @package WordPress
   6   * @subpackage Theme
   7   */
   8  
   9  /**
  10   * Returns an array of WP_Theme objects based on the arguments.
  11   *
  12   * Despite advances over get_themes(), this function is quite expensive, and grows
  13   * linearly with additional themes. Stick to wp_get_theme() if possible.
  14   *
  15   * @since 3.4.0
  16   *
  17   * @global array $wp_theme_directories
  18   * @staticvar array $_themes
  19   *
  20   * @param array $args {
  21   *     Optional. The search arguments.
  22   *
  23   *     @type mixed $errors  True to return themes with errors, false to return themes without errors, null to return all themes.
  24   *                          Defaults to false.
  25   *     @type mixed $allowed (Multisite) True to return only allowed themes for a site. False to return only disallowed themes for a site.
  26   *                          'site' to return only site-allowed themes. 'network' to return only network-allowed themes.
  27   *                          Null to return all themes. Defaults to null.
  28   *     @type int   $blog_id (Multisite) The blog ID used to calculate which themes are allowed.
  29   *                          Defaults to 0, synonymous for the current blog.
  30   * }
  31   * @return WP_Theme[] Array of WP_Theme objects.
  32   */
  33  function wp_get_themes( $args = array() ) {
  34      global $wp_theme_directories;
  35  
  36      $defaults = array(
  37          'errors'  => false,
  38          'allowed' => null,
  39          'blog_id' => 0,
  40      );
  41      $args     = wp_parse_args( $args, $defaults );
  42  
  43      $theme_directories = search_theme_directories();
  44  
  45      if ( is_array( $wp_theme_directories ) && count( $wp_theme_directories ) > 1 ) {
  46          // Make sure the current theme wins out, in case search_theme_directories() picks the wrong
  47          // one in the case of a conflict. (Normally, last registered theme root wins.)
  48          $current_theme = get_stylesheet();
  49          if ( isset( $theme_directories[ $current_theme ] ) ) {
  50              $root_of_current_theme = get_raw_theme_root( $current_theme );
  51              if ( ! in_array( $root_of_current_theme, $wp_theme_directories ) ) {
  52                  $root_of_current_theme = WP_CONTENT_DIR . $root_of_current_theme;
  53              }
  54              $theme_directories[ $current_theme ]['theme_root'] = $root_of_current_theme;
  55          }
  56      }
  57  
  58      if ( empty( $theme_directories ) ) {
  59          return array();
  60      }
  61  
  62      if ( is_multisite() && null !== $args['allowed'] ) {
  63          $allowed = $args['allowed'];
  64          if ( 'network' === $allowed ) {
  65              $theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed_on_network() );
  66          } elseif ( 'site' === $allowed ) {
  67              $theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed_on_site( $args['blog_id'] ) );
  68          } elseif ( $allowed ) {
  69              $theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed( $args['blog_id'] ) );
  70          } else {
  71              $theme_directories = array_diff_key( $theme_directories, WP_Theme::get_allowed( $args['blog_id'] ) );
  72          }
  73      }
  74  
  75      $themes         = array();
  76      static $_themes = array();
  77  
  78      foreach ( $theme_directories as $theme => $theme_root ) {
  79          if ( isset( $_themes[ $theme_root['theme_root'] . '/' . $theme ] ) ) {
  80              $themes[ $theme ] = $_themes[ $theme_root['theme_root'] . '/' . $theme ];
  81          } else {
  82              $themes[ $theme ]                                    = new WP_Theme( $theme, $theme_root['theme_root'] );
  83              $_themes[ $theme_root['theme_root'] . '/' . $theme ] = $themes[ $theme ];
  84          }
  85      }
  86  
  87      if ( null !== $args['errors'] ) {
  88          foreach ( $themes as $theme => $wp_theme ) {
  89              if ( $wp_theme->errors() != $args['errors'] ) {
  90                  unset( $themes[ $theme ] );
  91              }
  92          }
  93      }
  94  
  95      return $themes;
  96  }
  97  
  98  /**
  99   * Gets a WP_Theme object for a theme.
 100   *
 101   * @since 3.4.0
 102   *
 103   * @global array $wp_theme_directories
 104   *
 105   * @param string $stylesheet Directory name for the theme. Optional. Defaults to current theme.
 106   * @param string $theme_root Absolute path of the theme root to look in. Optional. If not specified, get_raw_theme_root()
 107   *                           is used to calculate the theme root for the $stylesheet provided (or current theme).
 108   * @return WP_Theme Theme object. Be sure to check the object's exists() method if you need to confirm the theme's existence.
 109   */
 110  function wp_get_theme( $stylesheet = '', $theme_root = '' ) {
 111      global $wp_theme_directories;
 112  
 113      if ( empty( $stylesheet ) ) {
 114          $stylesheet = get_stylesheet();
 115      }
 116  
 117      if ( empty( $theme_root ) ) {
 118          $theme_root = get_raw_theme_root( $stylesheet );
 119          if ( false === $theme_root ) {
 120              $theme_root = WP_CONTENT_DIR . '/themes';
 121          } elseif ( ! in_array( $theme_root, (array) $wp_theme_directories ) ) {
 122              $theme_root = WP_CONTENT_DIR . $theme_root;
 123          }
 124      }
 125  
 126      return new WP_Theme( $stylesheet, $theme_root );
 127  }
 128  
 129  /**
 130   * Clears the cache held by get_theme_roots() and WP_Theme.
 131   *
 132   * @since 3.5.0
 133   * @param bool $clear_update_cache Whether to clear the Theme updates cache
 134   */
 135  function wp_clean_themes_cache( $clear_update_cache = true ) {
 136      if ( $clear_update_cache ) {
 137          delete_site_transient( 'update_themes' );
 138      }
 139      search_theme_directories( true );
 140      foreach ( wp_get_themes( array( 'errors' => null ) ) as $theme ) {
 141          $theme->cache_delete();
 142      }
 143  }
 144  
 145  /**
 146   * Whether a child theme is in use.
 147   *
 148   * @since 3.0.0
 149   *
 150   * @return bool true if a child theme is in use, false otherwise.
 151   */
 152  function is_child_theme() {
 153      return ( TEMPLATEPATH !== STYLESHEETPATH );
 154  }
 155  
 156  /**
 157   * Retrieve name of the current stylesheet.
 158   *
 159   * The theme name that the administrator has currently set the front end theme
 160   * as.
 161   *
 162   * For all intents and purposes, the template name and the stylesheet name are
 163   * going to be the same for most cases.
 164   *
 165   * @since 1.5.0
 166   *
 167   * @return string Stylesheet name.
 168   */
 169  function get_stylesheet() {
 170      /**
 171       * Filters the name of current stylesheet.
 172       *
 173       * @since 1.5.0
 174       *
 175       * @param string $stylesheet Name of the current stylesheet.
 176       */
 177      return apply_filters( 'stylesheet', get_option( 'stylesheet' ) );
 178  }
 179  
 180  /**
 181   * Retrieve stylesheet directory path for current theme.
 182   *
 183   * @since 1.5.0
 184   *
 185   * @return string Path to current theme directory.
 186   */
 187  function get_stylesheet_directory() {
 188      $stylesheet     = get_stylesheet();
 189      $theme_root     = get_theme_root( $stylesheet );
 190      $stylesheet_dir = "$theme_root/$stylesheet";
 191  
 192      /**
 193       * Filters the stylesheet directory path for current theme.
 194       *
 195       * @since 1.5.0
 196       *
 197       * @param string $stylesheet_dir Absolute path to the current theme.
 198       * @param string $stylesheet     Directory name of the current theme.
 199       * @param string $theme_root     Absolute path to themes directory.
 200       */
 201      return apply_filters( 'stylesheet_directory', $stylesheet_dir, $stylesheet, $theme_root );
 202  }
 203  
 204  /**
 205   * Retrieve stylesheet directory URI.
 206   *
 207   * @since 1.5.0
 208   *
 209   * @return string
 210   */
 211  function get_stylesheet_directory_uri() {
 212      $stylesheet         = str_replace( '%2F', '/', rawurlencode( get_stylesheet() ) );
 213      $theme_root_uri     = get_theme_root_uri( $stylesheet );
 214      $stylesheet_dir_uri = "$theme_root_uri/$stylesheet";
 215  
 216      /**
 217       * Filters the stylesheet directory URI.
 218       *
 219       * @since 1.5.0
 220       *
 221       * @param string $stylesheet_dir_uri Stylesheet directory URI.
 222       * @param string $stylesheet         Name of the activated theme's directory.
 223       * @param string $theme_root_uri     Themes root URI.
 224       */
 225      return apply_filters( 'stylesheet_directory_uri', $stylesheet_dir_uri, $stylesheet, $theme_root_uri );
 226  }
 227  
 228  /**
 229   * Retrieves the URI of current theme stylesheet.
 230   *
 231   * The stylesheet file name is 'style.css' which is appended to the stylesheet directory URI path.
 232   * See get_stylesheet_directory_uri().
 233   *
 234   * @since 1.5.0
 235   *
 236   * @return string
 237   */
 238  function get_stylesheet_uri() {
 239      $stylesheet_dir_uri = get_stylesheet_directory_uri();
 240      $stylesheet_uri     = $stylesheet_dir_uri . '/style.css';
 241      /**
 242       * Filters the URI of the current theme stylesheet.
 243       *
 244       * @since 1.5.0
 245       *
 246       * @param string $stylesheet_uri     Stylesheet URI for the current theme/child theme.
 247       * @param string $stylesheet_dir_uri Stylesheet directory URI for the current theme/child theme.
 248       */
 249      return apply_filters( 'stylesheet_uri', $stylesheet_uri, $stylesheet_dir_uri );
 250  }
 251  
 252  /**
 253   * Retrieves the localized stylesheet URI.
 254   *
 255   * The stylesheet directory for the localized stylesheet files are located, by
 256   * default, in the base theme directory. The name of the locale file will be the
 257   * locale followed by '.css'. If that does not exist, then the text direction
 258   * stylesheet will be checked for existence, for example 'ltr.css'.
 259   *
 260   * The theme may change the location of the stylesheet directory by either using
 261   * the {@see 'stylesheet_directory_uri'} or {@see 'locale_stylesheet_uri'} filters.
 262   *
 263   * If you want to change the location of the stylesheet files for the entire
 264   * WordPress workflow, then change the former. If you just have the locale in a
 265   * separate folder, then change the latter.
 266   *
 267   * @since 2.1.0
 268   *
 269   * @global WP_Locale $wp_locale WordPress date and time locale object.
 270   *
 271   * @return string
 272   */
 273  function get_locale_stylesheet_uri() {
 274      global $wp_locale;
 275      $stylesheet_dir_uri = get_stylesheet_directory_uri();
 276      $dir                = get_stylesheet_directory();
 277      $locale             = get_locale();
 278      if ( file_exists( "$dir/$locale.css" ) ) {
 279          $stylesheet_uri = "$stylesheet_dir_uri/$locale.css";
 280      } elseif ( ! empty( $wp_locale->text_direction ) && file_exists( "$dir/{$wp_locale->text_direction}.css" ) ) {
 281          $stylesheet_uri = "$stylesheet_dir_uri/{$wp_locale->text_direction}.css";
 282      } else {
 283          $stylesheet_uri = '';
 284      }
 285      /**
 286       * Filters the localized stylesheet URI.
 287       *
 288       * @since 2.1.0
 289       *
 290       * @param string $stylesheet_uri     Localized stylesheet URI.
 291       * @param string $stylesheet_dir_uri Stylesheet directory URI.
 292       */
 293      return apply_filters( 'locale_stylesheet_uri', $stylesheet_uri, $stylesheet_dir_uri );
 294  }
 295  
 296  /**
 297   * Retrieve name of the current theme.
 298   *
 299   * @since 1.5.0
 300   *
 301   * @return string Template name.
 302   */
 303  function get_template() {
 304      /**
 305       * Filters the name of the current theme.
 306       *
 307       * @since 1.5.0
 308       *
 309       * @param string $template Current theme's directory name.
 310       */
 311      return apply_filters( 'template', get_option( 'template' ) );
 312  }
 313  
 314  /**
 315   * Retrieve current theme directory.
 316   *
 317   * @since 1.5.0
 318   *
 319   * @return string Template directory path.
 320   */
 321  function get_template_directory() {
 322      $template     = get_template();
 323      $theme_root   = get_theme_root( $template );
 324      $template_dir = "$theme_root/$template";
 325  
 326      /**
 327       * Filters the current theme directory path.
 328       *
 329       * @since 1.5.0
 330       *
 331       * @param string $template_dir The URI of the current theme directory.
 332       * @param string $template     Directory name of the current theme.
 333       * @param string $theme_root   Absolute path to the themes directory.
 334       */
 335      return apply_filters( 'template_directory', $template_dir, $template, $theme_root );
 336  }
 337  
 338  /**
 339   * Retrieve theme directory URI.
 340   *
 341   * @since 1.5.0
 342   *
 343   * @return string Template directory URI.
 344   */
 345  function get_template_directory_uri() {
 346      $template         = str_replace( '%2F', '/', rawurlencode( get_template() ) );
 347      $theme_root_uri   = get_theme_root_uri( $template );
 348      $template_dir_uri = "$theme_root_uri/$template";
 349  
 350      /**
 351       * Filters the current theme directory URI.
 352       *
 353       * @since 1.5.0
 354       *
 355       * @param string $template_dir_uri The URI of the current theme directory.
 356       * @param string $template         Directory name of the current theme.
 357       * @param string $theme_root_uri   The themes root URI.
 358       */
 359      return apply_filters( 'template_directory_uri', $template_dir_uri, $template, $theme_root_uri );
 360  }
 361  
 362  /**
 363   * Retrieve theme roots.
 364   *
 365   * @since 2.9.0
 366   *
 367   * @global array $wp_theme_directories
 368   *
 369   * @return array|string An array of theme roots keyed by template/stylesheet or a single theme root if all themes have the same root.
 370   */
 371  function get_theme_roots() {
 372      global $wp_theme_directories;
 373  
 374      if ( ! is_array( $wp_theme_directories ) || count( $wp_theme_directories ) <= 1 ) {
 375          return '/themes';
 376      }
 377  
 378      $theme_roots = get_site_transient( 'theme_roots' );
 379      if ( false === $theme_roots ) {
 380          search_theme_directories( true ); // Regenerate the transient.
 381          $theme_roots = get_site_transient( 'theme_roots' );
 382      }
 383      return $theme_roots;
 384  }
 385  
 386  /**
 387   * Register a directory that contains themes.
 388   *
 389   * @since 2.9.0
 390   *
 391   * @global array $wp_theme_directories
 392   *
 393   * @param string $directory Either the full filesystem path to a theme folder or a folder within WP_CONTENT_DIR
 394   * @return bool
 395   */
 396  function register_theme_directory( $directory ) {
 397      global $wp_theme_directories;
 398  
 399      if ( ! file_exists( $directory ) ) {
 400          // Try prepending as the theme directory could be relative to the content directory
 401          $directory = WP_CONTENT_DIR . '/' . $directory;
 402          // If this directory does not exist, return and do not register
 403          if ( ! file_exists( $directory ) ) {
 404              return false;
 405          }
 406      }
 407  
 408      if ( ! is_array( $wp_theme_directories ) ) {
 409          $wp_theme_directories = array();
 410      }
 411  
 412      $untrailed = untrailingslashit( $directory );
 413      if ( ! empty( $untrailed ) && ! in_array( $untrailed, $wp_theme_directories ) ) {
 414          $wp_theme_directories[] = $untrailed;
 415      }
 416  
 417      return true;
 418  }
 419  
 420  /**
 421   * Search all registered theme directories for complete and valid themes.
 422   *
 423   * @since 2.9.0
 424   *
 425   * @global array $wp_theme_directories
 426   * @staticvar array $found_themes
 427   *
 428   * @param bool $force Optional. Whether to force a new directory scan. Defaults to false.
 429   * @return array|false Valid themes found
 430   */
 431  function search_theme_directories( $force = false ) {
 432      global $wp_theme_directories;
 433      static $found_themes = null;
 434  
 435      if ( empty( $wp_theme_directories ) ) {
 436          return false;
 437      }
 438  
 439      if ( ! $force && isset( $found_themes ) ) {
 440          return $found_themes;
 441      }
 442  
 443      $found_themes = array();
 444  
 445      $wp_theme_directories = (array) $wp_theme_directories;
 446      $relative_theme_roots = array();
 447  
 448      // Set up maybe-relative, maybe-absolute array of theme directories.
 449      // We always want to return absolute, but we need to cache relative
 450      // to use in get_theme_root().
 451      foreach ( $wp_theme_directories as $theme_root ) {
 452          if ( 0 === strpos( $theme_root, WP_CONTENT_DIR ) ) {
 453              $relative_theme_roots[ str_replace( WP_CONTENT_DIR, '', $theme_root ) ] = $theme_root;
 454          } else {
 455              $relative_theme_roots[ $theme_root ] = $theme_root;
 456          }
 457      }
 458  
 459      /**
 460       * Filters whether to get the cache of the registered theme directories.
 461       *
 462       * @since 3.4.0
 463       *
 464       * @param bool   $cache_expiration Whether to get the cache of the theme directories. Default false.
 465       * @param string $cache_directory  Directory to be searched for the cache.
 466       */
 467      $cache_expiration = apply_filters( 'wp_cache_themes_persistently', false, 'search_theme_directories' );
 468      if ( $cache_expiration ) {
 469          $cached_roots = get_site_transient( 'theme_roots' );
 470          if ( is_array( $cached_roots ) ) {
 471              foreach ( $cached_roots as $theme_dir => $theme_root ) {
 472                  // A cached theme root is no longer around, so skip it.
 473                  if ( ! isset( $relative_theme_roots[ $theme_root ] ) ) {
 474                      continue;
 475                  }
 476                  $found_themes[ $theme_dir ] = array(
 477                      'theme_file' => $theme_dir . '/style.css',
 478                      'theme_root' => $relative_theme_roots[ $theme_root ], // Convert relative to absolute.
 479                  );
 480              }
 481              return $found_themes;
 482          }
 483          if ( ! is_int( $cache_expiration ) ) {
 484              $cache_expiration = 1800; // half hour
 485          }
 486      } else {
 487          $cache_expiration = 1800; // half hour
 488      }
 489  
 490      /* Loop the registered theme directories and extract all themes */
 491      foreach ( $wp_theme_directories as $theme_root ) {
 492  
 493          // Start with directories in the root of the current theme directory.
 494          $dirs = @ scandir( $theme_root );
 495          if ( ! $dirs ) {
 496              trigger_error( "$theme_root is not readable", E_USER_NOTICE );
 497              continue;
 498          }
 499          foreach ( $dirs as $dir ) {
 500              if ( ! is_dir( $theme_root . '/' . $dir ) || $dir[0] == '.' || $dir == 'CVS' ) {
 501                  continue;
 502              }
 503              if ( file_exists( $theme_root . '/' . $dir . '/style.css' ) ) {
 504                  // wp-content/themes/a-single-theme
 505                  // wp-content/themes is $theme_root, a-single-theme is $dir
 506                  $found_themes[ $dir ] = array(
 507                      'theme_file' => $dir . '/style.css',
 508                      'theme_root' => $theme_root,
 509                  );
 510              } else {
 511                  $found_theme = false;
 512                  // wp-content/themes/a-folder-of-themes/*
 513                  // wp-content/themes is $theme_root, a-folder-of-themes is $dir, then themes are $sub_dirs
 514                  $sub_dirs = @ scandir( $theme_root . '/' . $dir );
 515                  if ( ! $sub_dirs ) {
 516                      trigger_error( "$theme_root/$dir is not readable", E_USER_NOTICE );
 517                      continue;
 518                  }
 519                  foreach ( $sub_dirs as $sub_dir ) {
 520                      if ( ! is_dir( $theme_root . '/' . $dir . '/' . $sub_dir ) || $dir[0] == '.' || $dir == 'CVS' ) {
 521                          continue;
 522                      }
 523                      if ( ! file_exists( $theme_root . '/' . $dir . '/' . $sub_dir . '/style.css' ) ) {
 524                          continue;
 525                      }
 526                      $found_themes[ $dir . '/' . $sub_dir ] = array(
 527                          'theme_file' => $dir . '/' . $sub_dir . '/style.css',
 528                          'theme_root' => $theme_root,
 529                      );
 530                      $found_theme                           = true;
 531                  }
 532                  // Never mind the above, it's just a theme missing a style.css.
 533                  // Return it; WP_Theme will catch the error.
 534                  if ( ! $found_theme ) {
 535                      $found_themes[ $dir ] = array(
 536                          'theme_file' => $dir . '/style.css',
 537                          'theme_root' => $theme_root,
 538                      );
 539                  }
 540              }
 541          }
 542      }
 543  
 544      asort( $found_themes );
 545  
 546      $theme_roots          = array();
 547      $relative_theme_roots = array_flip( $relative_theme_roots );
 548  
 549      foreach ( $found_themes as $theme_dir => $theme_data ) {
 550          $theme_roots[ $theme_dir ] = $relative_theme_roots[ $theme_data['theme_root'] ]; // Convert absolute to relative.
 551      }
 552  
 553      if ( $theme_roots != get_site_transient( 'theme_roots' ) ) {
 554          set_site_transient( 'theme_roots', $theme_roots, $cache_expiration );
 555      }
 556  
 557      return $found_themes;
 558  }
 559  
 560  /**
 561   * Retrieve path to themes directory.
 562   *
 563   * Does not have trailing slash.
 564   *
 565   * @since 1.5.0
 566   *
 567   * @global array $wp_theme_directories
 568   *
 569   * @param string $stylesheet_or_template Optional. The stylesheet or template name of the theme.
 570   *                                       Default is to leverage the main theme root.
 571   * @return string Themes directory path.
 572   */
 573  function get_theme_root( $stylesheet_or_template = '' ) {
 574      global $wp_theme_directories;
 575  
 576      $theme_root = '';
 577  
 578      if ( $stylesheet_or_template ) {
 579          $theme_root = get_raw_theme_root( $stylesheet_or_template );
 580          if ( $theme_root ) {
 581              // Always prepend WP_CONTENT_DIR unless the root currently registered as a theme directory.
 582              // This gives relative theme roots the benefit of the doubt when things go haywire.
 583              if ( ! in_array( $theme_root, (array) $wp_theme_directories ) ) {
 584                  $theme_root = WP_CONTENT_DIR . $theme_root;
 585              }
 586          }
 587      }
 588  
 589      if ( ! $theme_root ) {
 590          $theme_root = WP_CONTENT_DIR . '/themes';
 591      }
 592  
 593      /**
 594       * Filters the absolute path to the themes directory.
 595       *
 596       * @since 1.5.0
 597       *
 598       * @param string $theme_root Absolute path to themes directory.
 599       */
 600      return apply_filters( 'theme_root', $theme_root );
 601  }
 602  
 603  /**
 604   * Retrieve URI for themes directory.
 605   *
 606   * Does not have trailing slash.
 607   *
 608   * @since 1.5.0
 609   *
 610   * @global array $wp_theme_directories
 611   *
 612   * @param string $stylesheet_or_template Optional. The stylesheet or template name of the theme.
 613   *                                       Default is to leverage the main theme root.
 614   * @param string $theme_root             Optional. The theme root for which calculations will be based,
 615   *                                       preventing the need for a get_raw_theme_root() call. Default empty.
 616   * @return string Themes directory URI.
 617   */
 618  function get_theme_root_uri( $stylesheet_or_template = '', $theme_root = '' ) {
 619      global $wp_theme_directories;
 620  
 621      if ( $stylesheet_or_template && ! $theme_root ) {
 622          $theme_root = get_raw_theme_root( $stylesheet_or_template );
 623      }
 624  
 625      if ( $stylesheet_or_template && $theme_root ) {
 626          if ( in_array( $theme_root, (array) $wp_theme_directories ) ) {
 627              // Absolute path. Make an educated guess. YMMV -- but note the filter below.
 628              if ( 0 === strpos( $theme_root, WP_CONTENT_DIR ) ) {
 629                  $theme_root_uri = content_url( str_replace( WP_CONTENT_DIR, '', $theme_root ) );
 630              } elseif ( 0 === strpos( $theme_root, ABSPATH ) ) {
 631                  $theme_root_uri = site_url( str_replace( ABSPATH, '', $theme_root ) );
 632              } elseif ( 0 === strpos( $theme_root, WP_PLUGIN_DIR ) || 0 === strpos( $theme_root, WPMU_PLUGIN_DIR ) ) {
 633                  $theme_root_uri = plugins_url( basename( $theme_root ), $theme_root );
 634              } else {
 635                  $theme_root_uri = $theme_root;
 636              }
 637          } else {
 638              $theme_root_uri = content_url( $theme_root );
 639          }
 640      } else {
 641          $theme_root_uri = content_url( 'themes' );
 642      }
 643  
 644      /**
 645       * Filters the URI for themes directory.
 646       *
 647       * @since 1.5.0
 648       *
 649       * @param string $theme_root_uri         The URI for themes directory.
 650       * @param string $siteurl                WordPress web address which is set in General Options.
 651       * @param string $stylesheet_or_template The stylesheet or template name of the theme.
 652       */
 653      return apply_filters( 'theme_root_uri', $theme_root_uri, get_option( 'siteurl' ), $stylesheet_or_template );
 654  }
 655  
 656  /**
 657   * Get the raw theme root relative to the content directory with no filters applied.
 658   *
 659   * @since 3.1.0
 660   *
 661   * @global array $wp_theme_directories
 662   *
 663   * @param string $stylesheet_or_template The stylesheet or template name of the theme.
 664   * @param bool   $skip_cache             Optional. Whether to skip the cache.
 665   *                                       Defaults to false, meaning the cache is used.
 666   * @return string Theme root.
 667   */
 668  function get_raw_theme_root( $stylesheet_or_template, $skip_cache = false ) {
 669      global $wp_theme_directories;
 670  
 671      if ( ! is_array( $wp_theme_directories ) || count( $wp_theme_directories ) <= 1 ) {
 672          return '/themes';
 673      }
 674  
 675      $theme_root = false;
 676  
 677      // If requesting the root for the current theme, consult options to avoid calling get_theme_roots()
 678      if ( ! $skip_cache ) {
 679          if ( get_option( 'stylesheet' ) == $stylesheet_or_template ) {
 680              $theme_root = get_option( 'stylesheet_root' );
 681          } elseif ( get_option( 'template' ) == $stylesheet_or_template ) {
 682              $theme_root = get_option( 'template_root' );
 683          }
 684      }
 685  
 686      if ( empty( $theme_root ) ) {
 687          $theme_roots = get_theme_roots();
 688          if ( ! empty( $theme_roots[ $stylesheet_or_template ] ) ) {
 689              $theme_root = $theme_roots[ $stylesheet_or_template ];
 690          }
 691      }
 692  
 693      return $theme_root;
 694  }
 695  
 696  /**
 697   * Display localized stylesheet link element.
 698   *
 699   * @since 2.1.0
 700   */
 701  function locale_stylesheet() {
 702      $stylesheet = get_locale_stylesheet_uri();
 703      if ( empty( $stylesheet ) ) {
 704          return;
 705      }
 706  
 707      $type_attr = current_theme_supports( 'html5', 'style' ) ? '' : ' type="text/css"';
 708  
 709      printf(
 710          '<link rel="stylesheet" href="%s"%s media="screen" />',
 711          $stylesheet,
 712          $type_attr
 713      );
 714  }
 715  
 716  /**
 717   * Switches the theme.
 718   *
 719   * Accepts one argument: $stylesheet of the theme. It also accepts an additional function signature
 720   * of two arguments: $template then $stylesheet. This is for backward compatibility.
 721   *
 722   * @since 2.5.0
 723   *
 724   * @global array                $wp_theme_directories
 725   * @global WP_Customize_Manager $wp_customize
 726   * @global array                $sidebars_widgets
 727   *
 728   * @param string $stylesheet Stylesheet name
 729   */
 730  function switch_theme( $stylesheet ) {
 731      global $wp_theme_directories, $wp_customize, $sidebars_widgets;
 732  
 733      $_sidebars_widgets = null;
 734      if ( 'wp_ajax_customize_save' === current_action() ) {
 735          $old_sidebars_widgets_data_setting = $wp_customize->get_setting( 'old_sidebars_widgets_data' );
 736          if ( $old_sidebars_widgets_data_setting ) {
 737              $_sidebars_widgets = $wp_customize->post_value( $old_sidebars_widgets_data_setting );
 738          }
 739      } elseif ( is_array( $sidebars_widgets ) ) {
 740          $_sidebars_widgets = $sidebars_widgets;
 741      }
 742  
 743      if ( is_array( $_sidebars_widgets ) ) {
 744          set_theme_mod(
 745              'sidebars_widgets',
 746              array(
 747                  'time' => time(),
 748                  'data' => $_sidebars_widgets,
 749              )
 750          );
 751      }
 752  
 753      $nav_menu_locations = get_theme_mod( 'nav_menu_locations' );
 754      update_option( 'theme_switch_menu_locations', $nav_menu_locations );
 755  
 756      if ( func_num_args() > 1 ) {
 757          $stylesheet = func_get_arg( 1 );
 758      }
 759  
 760      $old_theme = wp_get_theme();
 761      $new_theme = wp_get_theme( $stylesheet );
 762      $template  = $new_theme->get_template();
 763  
 764      if ( wp_is_recovery_mode() ) {
 765          $paused_themes = wp_paused_themes();
 766          $paused_themes->delete( $old_theme->get_stylesheet() );
 767          $paused_themes->delete( $old_theme->get_template() );
 768      }
 769  
 770      update_option( 'template', $template );
 771      update_option( 'stylesheet', $stylesheet );
 772  
 773      if ( count( $wp_theme_directories ) > 1 ) {
 774          update_option( 'template_root', get_raw_theme_root( $template, true ) );
 775          update_option( 'stylesheet_root', get_raw_theme_root( $stylesheet, true ) );
 776      } else {
 777          delete_option( 'template_root' );
 778          delete_option( 'stylesheet_root' );
 779      }
 780  
 781      $new_name = $new_theme->get( 'Name' );
 782  
 783      update_option( 'current_theme', $new_name );
 784  
 785      // Migrate from the old mods_{name} option to theme_mods_{slug}.
 786      if ( is_admin() && false === get_option( 'theme_mods_' . $stylesheet ) ) {
 787          $default_theme_mods = (array) get_option( 'mods_' . $new_name );
 788          if ( ! empty( $nav_menu_locations ) && empty( $default_theme_mods['nav_menu_locations'] ) ) {
 789              $default_theme_mods['nav_menu_locations'] = $nav_menu_locations;
 790          }
 791          add_option( "theme_mods_$stylesheet", $default_theme_mods );
 792      } else {
 793          /*
 794           * Since retrieve_widgets() is called when initializing a theme in the Customizer,
 795           * we need to remove the theme mods to avoid overwriting changes made via
 796           * the Customizer when accessing wp-admin/widgets.php.
 797           */
 798          if ( 'wp_ajax_customize_save' === current_action() ) {
 799              remove_theme_mod( 'sidebars_widgets' );
 800          }
 801      }
 802  
 803      update_option( 'theme_switched', $old_theme->get_stylesheet() );
 804  
 805      /**
 806       * Fires after the theme is switched.
 807       *
 808       * @since 1.5.0
 809       * @since 4.5.0 Introduced the `$old_theme` parameter.
 810       *
 811       * @param string   $new_name  Name of the new theme.
 812       * @param WP_Theme $new_theme WP_Theme instance of the new theme.
 813       * @param WP_Theme $old_theme WP_Theme instance of the old theme.
 814       */
 815      do_action( 'switch_theme', $new_name, $new_theme, $old_theme );
 816  }
 817  
 818  /**
 819   * Checks that current theme files 'index.php' and 'style.css' exists.
 820   *
 821   * Does not initially check the default theme, which is the fallback and should always exist.
 822   * But if it doesn't exist, it'll fall back to the latest core default theme that does exist.
 823   * Will switch theme to the fallback theme if current theme does not validate.
 824   *
 825   * You can use the {@see 'validate_current_theme'} filter to return false to
 826   * disable this functionality.
 827   *
 828   * @since 1.5.0
 829   * @see WP_DEFAULT_THEME
 830   *
 831   * @return bool
 832   */
 833  function validate_current_theme() {
 834      /**
 835       * Filters whether to validate the current theme.
 836       *
 837       * @since 2.7.0
 838       *
 839       * @param bool $validate Whether to validate the current theme. Default true.
 840       */
 841      if ( wp_installing() || ! apply_filters( 'validate_current_theme', true ) ) {
 842          return true;
 843      }
 844  
 845      if ( ! file_exists( get_template_directory() . '/index.php' ) ) {
 846          // Invalid.
 847      } elseif ( ! file_exists( get_template_directory() . '/style.css' ) ) {
 848          // Invalid.
 849      } elseif ( is_child_theme() && ! file_exists( get_stylesheet_directory() . '/style.css' ) ) {
 850          // Invalid.
 851      } else {
 852          // Valid.
 853          return true;
 854      }
 855  
 856      $default = wp_get_theme( WP_DEFAULT_THEME );
 857      if ( $default->exists() ) {
 858          switch_theme( WP_DEFAULT_THEME );
 859          return false;
 860      }
 861  
 862      /**
 863       * If we're in an invalid state but WP_DEFAULT_THEME doesn't exist,
 864       * switch to the latest core default theme that's installed.
 865       * If it turns out that this latest core default theme is our current
 866       * theme, then there's nothing we can do about that, so we have to bail,
 867       * rather than going into an infinite loop. (This is why there are
 868       * checks against WP_DEFAULT_THEME above, also.) We also can't do anything
 869       * if it turns out there is no default theme installed. (That's `false`.)
 870       */
 871      $default = WP_Theme::get_core_default_theme();
 872      if ( false === $default || get_stylesheet() == $default->get_stylesheet() ) {
 873          return true;
 874      }
 875  
 876      switch_theme( $default->get_stylesheet() );
 877      return false;
 878  }
 879  
 880  /**
 881   * Retrieve all theme modifications.
 882   *
 883   * @since 3.1.0
 884   *
 885   * @return array|void Theme modifications.
 886   */
 887  function get_theme_mods() {
 888      $theme_slug = get_option( 'stylesheet' );
 889      $mods       = get_option( "theme_mods_$theme_slug" );
 890      if ( false === $mods ) {
 891          $theme_name = get_option( 'current_theme' );
 892          if ( false === $theme_name ) {
 893              $theme_name = wp_get_theme()->get( 'Name' );
 894          }
 895          $mods = get_option( "mods_$theme_name" ); // Deprecated location.
 896          if ( is_admin() && false !== $mods ) {
 897              update_option( "theme_mods_$theme_slug", $mods );
 898              delete_option( "mods_$theme_name" );
 899          }
 900      }
 901      return $mods;
 902  }
 903  
 904  /**
 905   * Retrieve theme modification value for the current theme.
 906   *
 907   * If the modification name does not exist, then the $default will be passed
 908   * through {@link https://secure.php.net/sprintf sprintf()} PHP function with
 909   * the template directory URI as the first string and the stylesheet directory URI
 910   * as the second string.
 911   *
 912   * @since 2.1.0
 913   *
 914   * @param string       $name    Theme modification name.
 915   * @param string|false $default Optional. Theme modification default value. Default false.
 916   * @return mixed Theme modification value.
 917   */
 918  function get_theme_mod( $name, $default = false ) {
 919      $mods = get_theme_mods();
 920  
 921      if ( isset( $mods[ $name ] ) ) {
 922          /**
 923           * Filters the theme modification, or 'theme_mod', value.
 924           *
 925           * The dynamic portion of the hook name, `$name`, refers to the key name
 926           * of the modification array. For example, 'header_textcolor', 'header_image',
 927           * and so on depending on the theme options.
 928           *
 929           * @since 2.2.0
 930           *
 931           * @param string $current_mod The value of the current theme modification.
 932           */
 933          return apply_filters( "theme_mod_{$name}", $mods[ $name ] );
 934      }
 935  
 936      if ( is_string( $default ) ) {
 937          // Only run the replacement if an sprintf() string format pattern was found.
 938          if ( preg_match( '#(?<!%)%(?:\d+\$?)?s#', $default ) ) {
 939              $default = sprintf( $default, get_template_directory_uri(), get_stylesheet_directory_uri() );
 940          }
 941      }
 942  
 943      /** This filter is documented in wp-includes/theme.php */
 944      return apply_filters( "theme_mod_{$name}", $default );
 945  }
 946  
 947  /**
 948   * Update theme modification value for the current theme.
 949   *
 950   * @since 2.1.0
 951   *
 952   * @param string $name  Theme modification name.
 953   * @param mixed  $value Theme modification value.
 954   */
 955  function set_theme_mod( $name, $value ) {
 956      $mods      = get_theme_mods();
 957      $old_value = isset( $mods[ $name ] ) ? $mods[ $name ] : false;
 958  
 959      /**
 960       * Filters the theme modification, or 'theme_mod', value on save.
 961       *
 962       * The dynamic portion of the hook name, `$name`, refers to the key name
 963       * of the modification array. For example, 'header_textcolor', 'header_image',
 964       * and so on depending on the theme options.
 965       *
 966       * @since 3.9.0
 967       *
 968       * @param string $value     The new value of the theme modification.
 969       * @param string $old_value The current value of the theme modification.
 970       */
 971      $mods[ $name ] = apply_filters( "pre_set_theme_mod_{$name}", $value, $old_value );
 972  
 973      $theme = get_option( 'stylesheet' );
 974      update_option( "theme_mods_$theme", $mods );
 975  }
 976  
 977  /**
 978   * Remove theme modification name from current theme list.
 979   *
 980   * If removing the name also removes all elements, then the entire option will
 981   * be removed.
 982   *
 983   * @since 2.1.0
 984   *
 985   * @param string $name Theme modification name.
 986   */
 987  function remove_theme_mod( $name ) {
 988      $mods = get_theme_mods();
 989  
 990      if ( ! isset( $mods[ $name ] ) ) {
 991          return;
 992      }
 993  
 994      unset( $mods[ $name ] );
 995  
 996      if ( empty( $mods ) ) {
 997          remove_theme_mods();
 998          return;
 999      }
1000      $theme = get_option( 'stylesheet' );
1001      update_option( "theme_mods_$theme", $mods );
1002  }
1003  
1004  /**
1005   * Remove theme modifications option for current theme.
1006   *
1007   * @since 2.1.0
1008   */
1009  function remove_theme_mods() {
1010      delete_option( 'theme_mods_' . get_option( 'stylesheet' ) );
1011  
1012      // Old style.
1013      $theme_name = get_option( 'current_theme' );
1014      if ( false === $theme_name ) {
1015          $theme_name = wp_get_theme()->get( 'Name' );
1016      }
1017      delete_option( 'mods_' . $theme_name );
1018  }
1019  
1020  /**
1021   * Retrieves the custom header text color in 3- or 6-digit hexadecimal form.
1022   *
1023   * @since 2.1.0
1024   *
1025   * @return string Header text color in 3- or 6-digit hexadecimal form (minus the hash symbol).
1026   */
1027  function get_header_textcolor() {
1028      return get_theme_mod( 'header_textcolor', get_theme_support( 'custom-header', 'default-text-color' ) );
1029  }
1030  
1031  /**
1032   * Displays the custom header text color in 3- or 6-digit hexadecimal form (minus the hash symbol).
1033   *
1034   * @since 2.1.0
1035   */
1036  function header_textcolor() {
1037      echo get_header_textcolor();
1038  }
1039  
1040  /**
1041   * Whether to display the header text.
1042   *
1043   * @since 3.4.0
1044   *
1045   * @return bool
1046   */
1047  function display_header_text() {
1048      if ( ! current_theme_supports( 'custom-header', 'header-text' ) ) {
1049          return false;
1050      }
1051  
1052      $text_color = get_theme_mod( 'header_textcolor', get_theme_support( 'custom-header', 'default-text-color' ) );
1053      return 'blank' !== $text_color;
1054  }
1055  
1056  /**
1057   * Check whether a header image is set or not.
1058   *
1059   * @since 4.2.0
1060   *
1061   * @see get_header_image()
1062   *
1063   * @return bool Whether a header image is set or not.
1064   */
1065  function has_header_image() {
1066      return (bool) get_header_image();
1067  }
1068  
1069  /**
1070   * Retrieve header image for custom header.
1071   *
1072   * @since 2.1.0
1073   *
1074   * @return string|false
1075   */
1076  function get_header_image() {
1077      $url = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) );
1078  
1079      if ( 'remove-header' == $url ) {
1080          return false;
1081      }
1082  
1083      if ( is_random_header_image() ) {
1084          $url = get_random_header_image();
1085      }
1086  
1087      return esc_url_raw( set_url_scheme( $url ) );
1088  }
1089  
1090  /**
1091   * Create image tag markup for a custom header image.
1092   *
1093   * @since 4.4.0
1094   *
1095   * @param array $attr Optional. Additional attributes for the image tag. Can be used
1096   *                              to override the default attributes. Default empty.
1097   * @return string HTML image element markup or empty string on failure.
1098   */
1099  function get_header_image_tag( $attr = array() ) {
1100      $header      = get_custom_header();
1101      $header->url = get_header_image();
1102  
1103      if ( ! $header->url ) {
1104          return '';
1105      }
1106  
1107      $width  = absint( $header->width );
1108      $height = absint( $header->height );
1109  
1110      $attr = wp_parse_args(
1111          $attr,
1112          array(
1113              'src'    => $header->url,
1114              'width'  => $width,
1115              'height' => $height,
1116              'alt'    => get_bloginfo( 'name' ),
1117          )
1118      );
1119  
1120      // Generate 'srcset' and 'sizes' if not already present.
1121      if ( empty( $attr['srcset'] ) && ! empty( $header->attachment_id ) ) {
1122          $image_meta = get_post_meta( $header->attachment_id, '_wp_attachment_metadata', true );
1123          $size_array = array( $width, $height );
1124  
1125          if ( is_array( $image_meta ) ) {
1126              $srcset = wp_calculate_image_srcset( $size_array, $header->url, $image_meta, $header->attachment_id );
1127              $sizes  = ! empty( $attr['sizes'] ) ? $attr['sizes'] : wp_calculate_image_sizes( $size_array, $header->url, $image_meta, $header->attachment_id );
1128  
1129              if ( $srcset && $sizes ) {
1130                  $attr['srcset'] = $srcset;
1131                  $attr['sizes']  = $sizes;
1132              }
1133          }
1134      }
1135  
1136      $attr = array_map( 'esc_attr', $attr );
1137      $html = '<img';
1138  
1139      foreach ( $attr as $name => $value ) {
1140          $html .= ' ' . $name . '="' . $value . '"';
1141      }
1142  
1143      $html .= ' />';
1144  
1145      /**
1146       * Filters the markup of header images.
1147       *
1148       * @since 4.4.0
1149       *
1150       * @param string $html   The HTML image tag markup being filtered.
1151       * @param object $header The custom header object returned by 'get_custom_header()'.
1152       * @param array  $attr   Array of the attributes for the image tag.
1153       */
1154      return apply_filters( 'get_header_image_tag', $html, $header, $attr );
1155  }
1156  
1157  /**
1158   * Display the image markup for a custom header image.
1159   *
1160   * @since 4.4.0
1161   *
1162   * @param array $attr Optional. Attributes for the image markup. Default empty.
1163   */
1164  function the_header_image_tag( $attr = array() ) {
1165      echo get_header_image_tag( $attr );
1166  }
1167  
1168  /**
1169   * Get random header image data from registered images in theme.
1170   *
1171   * @since 3.4.0
1172   *
1173   * @access private
1174   *
1175   * @global array  $_wp_default_headers
1176   * @staticvar object $_wp_random_header
1177   *
1178   * @return object
1179   */
1180  function _get_random_header_data() {
1181      static $_wp_random_header = null;
1182  
1183      if ( empty( $_wp_random_header ) ) {
1184          global $_wp_default_headers;
1185          $header_image_mod = get_theme_mod( 'header_image', '' );
1186          $headers          = array();
1187  
1188          if ( 'random-uploaded-image' == $header_image_mod ) {
1189              $headers = get_uploaded_header_images();
1190          } elseif ( ! empty( $_wp_default_headers ) ) {
1191              if ( 'random-default-image' == $header_image_mod ) {
1192                  $headers = $_wp_default_headers;
1193              } else {
1194                  if ( current_theme_supports( 'custom-header', 'random-default' ) ) {
1195                      $headers = $_wp_default_headers;
1196                  }
1197              }
1198          }
1199  
1200          if ( empty( $headers ) ) {
1201              return new stdClass;
1202          }
1203  
1204          $_wp_random_header = (object) $headers[ array_rand( $headers ) ];
1205  
1206          $_wp_random_header->url           = sprintf( $_wp_random_header->url, get_template_directory_uri(), get_stylesheet_directory_uri() );
1207          $_wp_random_header->thumbnail_url = sprintf( $_wp_random_header->thumbnail_url, get_template_directory_uri(), get_stylesheet_directory_uri() );
1208      }
1209      return $_wp_random_header;
1210  }
1211  
1212  /**
1213   * Get random header image url from registered images in theme.
1214   *
1215   * @since 3.2.0
1216   *
1217   * @return string Path to header image
1218   */
1219  function get_random_header_image() {
1220      $random_image = _get_random_header_data();
1221      if ( empty( $random_image->url ) ) {
1222          return '';
1223      }
1224      return $random_image->url;
1225  }
1226  
1227  /**
1228   * Check if random header image is in use.
1229   *
1230   * Always true if user expressly chooses the option in Appearance > Header.
1231   * Also true if theme has multiple header images registered, no specific header image
1232   * is chosen, and theme turns on random headers with add_theme_support().
1233   *
1234   * @since 3.2.0
1235   *
1236   * @param string $type The random pool to use. any|default|uploaded
1237   * @return bool
1238   */
1239  function is_random_header_image( $type = 'any' ) {
1240      $header_image_mod = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) );
1241  
1242      if ( 'any' == $type ) {
1243          if ( 'random-default-image' == $header_image_mod || 'random-uploaded-image' == $header_image_mod || ( '' != get_random_header_image() && empty( $header_image_mod ) ) ) {
1244              return true;
1245          }
1246      } else {
1247          if ( "random-$type-image" == $header_image_mod ) {
1248              return true;
1249          } elseif ( 'default' == $type && empty( $header_image_mod ) && '' != get_random_header_image() ) {
1250              return true;
1251          }
1252      }
1253  
1254      return false;
1255  }
1256  
1257  /**
1258   * Display header image URL.
1259   *
1260   * @since 2.1.0
1261   */
1262  function header_image() {
1263      $image = get_header_image();
1264      if ( $image ) {
1265          echo esc_url( $image );
1266      }
1267  }
1268  
1269  /**
1270   * Get the header images uploaded for the current theme.
1271   *
1272   * @since 3.2.0
1273   *
1274   * @return array
1275   */
1276  function get_uploaded_header_images() {
1277      $header_images = array();
1278  
1279      // @todo caching
1280      $headers = get_posts(
1281          array(
1282              'post_type'  => 'attachment',
1283              'meta_key'   => '_wp_attachment_is_custom_header',
1284              'meta_value' => get_option( 'stylesheet' ),
1285              'orderby'    => 'none',
1286              'nopaging'   => true,
1287          )
1288      );
1289  
1290      if ( empty( $headers ) ) {
1291          return array();
1292      }
1293  
1294      foreach ( (array) $headers as $header ) {
1295          $url          = esc_url_raw( wp_get_attachment_url( $header->ID ) );
1296          $header_data  = wp_get_attachment_metadata( $header->ID );
1297          $header_index = $header->ID;
1298  
1299          $header_images[ $header_index ]                      = array();
1300          $header_images[ $header_index ]['attachment_id']     = $header->ID;
1301          $header_images[ $header_index ]['url']               = $url;
1302          $header_images[ $header_index ]['thumbnail_url']     = $url;
1303          $header_images[ $header_index ]['alt_text']          = get_post_meta( $header->ID, '_wp_attachment_image_alt', true );
1304          $header_images[ $header_index ]['attachment_parent'] = isset( $header_data['attachment_parent'] ) ? $header_data['attachment_parent'] : '';
1305  
1306          if ( isset( $header_data['width'] ) ) {
1307              $header_images[ $header_index ]['width'] = $header_data['width'];
1308          }
1309          if ( isset( $header_data['height'] ) ) {
1310              $header_images[ $header_index ]['height'] = $header_data['height'];
1311          }
1312      }
1313  
1314      return $header_images;
1315  }
1316  
1317  /**
1318   * Get the header image data.
1319   *
1320   * @since 3.4.0
1321   *
1322   * @global array $_wp_default_headers
1323   *
1324   * @return object
1325   */
1326  function get_custom_header() {
1327      global $_wp_default_headers;
1328  
1329      if ( is_random_header_image() ) {
1330          $data = _get_random_header_data();
1331      } else {
1332          $data = get_theme_mod( 'header_image_data' );
1333          if ( ! $data && current_theme_supports( 'custom-header', 'default-image' ) ) {
1334              $directory_args        = array( get_template_directory_uri(), get_stylesheet_directory_uri() );
1335              $data                  = array();
1336              $data['url']           = vsprintf( get_theme_support( 'custom-header', 'default-image' ), $directory_args );
1337              $data['thumbnail_url'] = $data['url'];
1338              if ( ! empty( $_wp_default_headers ) ) {
1339                  foreach ( (array) $_wp_default_headers as $default_header ) {
1340                      $url = vsprintf( $default_header['url'], $directory_args );
1341                      if ( $data['url'] == $url ) {
1342                          $data                  = $default_header;
1343                          $data['url']           = $url;
1344                          $data['thumbnail_url'] = vsprintf( $data['thumbnail_url'], $directory_args );
1345                          break;
1346                      }
1347                  }
1348              }
1349          }
1350      }
1351  
1352      $default = array(
1353          'url'           => '',
1354          'thumbnail_url' => '',
1355          'width'         => get_theme_support( 'custom-header', 'width' ),
1356          'height'        => get_theme_support( 'custom-header', 'height' ),
1357          'video'         => get_theme_support( 'custom-header', 'video' ),
1358      );
1359      return (object) wp_parse_args( $data, $default );
1360  }
1361  
1362  /**
1363   * Register a selection of default headers to be displayed by the custom header admin UI.
1364   *
1365   * @since 3.0.0
1366   *
1367   * @global array $_wp_default_headers
1368   *
1369   * @param array $headers Array of headers keyed by a string id. The ids point to arrays containing 'url', 'thumbnail_url', and 'description' keys.
1370   */
1371  function register_default_headers( $headers ) {
1372      global $_wp_default_headers;
1373  
1374      $_wp_default_headers = array_merge( (array) $_wp_default_headers, (array) $headers );
1375  }
1376  
1377  /**
1378   * Unregister default headers.
1379   *
1380   * This function must be called after register_default_headers() has already added the
1381   * header you want to remove.
1382   *
1383   * @see register_default_headers()
1384   * @since 3.0.0
1385   *
1386   * @global array $_wp_default_headers
1387   *
1388   * @param string|array $header The header string id (key of array) to remove, or an array thereof.
1389   * @return bool|void A single header returns true on success, false on failure.
1390   *                   There is currently no return value for multiple headers.
1391   */
1392  function unregister_default_headers( $header ) {
1393      global $_wp_default_headers;
1394      if ( is_array( $header ) ) {
1395          array_map( 'unregister_default_headers', $header );
1396      } elseif ( isset( $_wp_default_headers[ $header ] ) ) {
1397          unset( $_wp_default_headers[ $header ] );
1398          return true;
1399      } else {
1400          return false;
1401      }
1402  }
1403  
1404  /**
1405   * Check whether a header video is set or not.
1406   *
1407   * @since 4.7.0
1408   *
1409   * @see get_header_video_url()
1410   *
1411   * @return bool Whether a header video is set or not.
1412   */
1413  function has_header_video() {
1414      return (bool) get_header_video_url();
1415  }
1416  
1417  /**
1418   * Retrieve header video URL for custom header.
1419   *
1420   * Uses a local video if present, or falls back to an external video.
1421   *
1422   * @since 4.7.0
1423   *
1424   * @return string|false Header video URL or false if there is no video.
1425   */
1426  function get_header_video_url() {
1427      $id  = absint( get_theme_mod( 'header_video' ) );
1428      $url = esc_url( get_theme_mod( 'external_header_video' ) );
1429  
1430      if ( $id ) {
1431          // Get the file URL from the attachment ID.
1432          $url = wp_get_attachment_url( $id );
1433      }
1434  
1435      /**
1436       * Filters the header video URL.
1437       *
1438       * @since 4.7.3
1439       *
1440       * @param string $url Header video URL, if available.
1441       */
1442      $url = apply_filters( 'get_header_video_url', $url );
1443  
1444      if ( ! $id && ! $url ) {
1445          return false;
1446      }
1447  
1448      return esc_url_raw( set_url_scheme( $url ) );
1449  }
1450  
1451  /**
1452   * Display header video URL.
1453   *
1454   * @since 4.7.0
1455   */
1456  function the_header_video_url() {
1457      $video = get_header_video_url();
1458      if ( $video ) {
1459          echo esc_url( $video );
1460      }
1461  }
1462  
1463  /**
1464   * Retrieve header video settings.
1465   *
1466   * @since 4.7.0
1467   *
1468   * @return array
1469   */
1470  function get_header_video_settings() {
1471      $header     = get_custom_header();
1472      $video_url  = get_header_video_url();
1473      $video_type = wp_check_filetype( $video_url, wp_get_mime_types() );
1474  
1475      $settings = array(
1476          'mimeType'  => '',
1477          'posterUrl' => get_header_image(),
1478          'videoUrl'  => $video_url,
1479          'width'     => absint( $header->width ),
1480          'height'    => absint( $header->height ),
1481          'minWidth'  => 900,
1482          'minHeight' => 500,
1483          'l10n'      => array(
1484              'pause'      => __( 'Pause' ),
1485              'play'       => __( 'Play' ),
1486              'pauseSpeak' => __( 'Video is paused.' ),
1487              'playSpeak'  => __( 'Video is playing.' ),
1488          ),
1489      );
1490  
1491      if ( preg_match( '#^https?://(?:www\.)?(?:youtube\.com/watch|youtu\.be/)#', $video_url ) ) {
1492          $settings['mimeType'] = 'video/x-youtube';
1493      } elseif ( ! empty( $video_type['type'] ) ) {
1494          $settings['mimeType'] = $video_type['type'];
1495      }
1496  
1497      /**
1498       * Filters header video settings.
1499       *
1500       * @since 4.7.0
1501       *
1502       * @param array $settings An array of header video settings.
1503       */
1504      return apply_filters( 'header_video_settings', $settings );
1505  }
1506  
1507  /**
1508   * Check whether a custom header is set or not.
1509   *
1510   * @since 4.7.0
1511   *
1512   * @return bool True if a custom header is set. False if not.
1513   */
1514  function has_custom_header() {
1515      if ( has_header_image() || ( has_header_video() && is_header_video_active() ) ) {
1516          return true;
1517      }
1518  
1519      return false;
1520  }
1521  
1522  /**
1523   * Checks whether the custom header video is eligible to show on the current page.
1524   *
1525   * @since 4.7.0
1526   *
1527   * @return bool True if the custom header video should be shown. False if not.
1528   */
1529  function is_header_video_active() {
1530      if ( ! get_theme_support( 'custom-header', 'video' ) ) {
1531          return false;
1532      }
1533  
1534      $video_active_cb = get_theme_support( 'custom-header', 'video-active-callback' );
1535  
1536      if ( empty( $video_active_cb ) || ! is_callable( $video_active_cb ) ) {
1537          $show_video = true;
1538      } else {
1539          $show_video = call_user_func( $video_active_cb );
1540      }
1541  
1542      /**
1543       * Modify whether the custom header video is eligible to show on the current page.
1544       *
1545       * @since 4.7.0
1546       *
1547       * @param bool $show_video Whether the custom header video should be shown. Returns the value
1548       *                         of the theme setting for the `custom-header`'s `video-active-callback`.
1549       *                         If no callback is set, the default value is that of `is_front_page()`.
1550       */
1551      return apply_filters( 'is_header_video_active', $show_video );
1552  }
1553  
1554  /**
1555   * Retrieve the markup for a custom header.
1556   *
1557   * The container div will always be returned in the Customizer preview.
1558   *
1559   * @since 4.7.0
1560   *
1561   * @return string The markup for a custom header on success.
1562   */
1563  function get_custom_header_markup() {
1564      if ( ! has_custom_header() && ! is_customize_preview() ) {
1565          return '';
1566      }
1567  
1568      return sprintf(
1569          '<div id="wp-custom-header" class="wp-custom-header">%s</div>',
1570          get_header_image_tag()
1571      );
1572  }
1573  
1574  /**
1575   * Print the markup for a custom header.
1576   *
1577   * A container div will always be printed in the Customizer preview.
1578   *
1579   * @since 4.7.0
1580   */
1581  function the_custom_header_markup() {
1582      $custom_header = get_custom_header_markup();
1583      if ( empty( $custom_header ) ) {
1584          return;
1585      }
1586  
1587      echo $custom_header;
1588  
1589      if ( is_header_video_active() && ( has_header_video() || is_customize_preview() ) ) {
1590          wp_enqueue_script( 'wp-custom-header' );
1591          wp_localize_script( 'wp-custom-header', '_wpCustomHeaderSettings', get_header_video_settings() );
1592      }
1593  }
1594  
1595  /**
1596   * Retrieve background image for custom background.
1597   *
1598   * @since 3.0.0
1599   *
1600   * @return string
1601   */
1602  function get_background_image() {
1603      return get_theme_mod( 'background_image', get_theme_support( 'custom-background', 'default-image' ) );
1604  }
1605  
1606  /**
1607   * Display background image path.
1608   *
1609   * @since 3.0.0
1610   */
1611  function background_image() {
1612      echo get_background_image();
1613  }
1614  
1615  /**
1616   * Retrieve value for custom background color.
1617   *
1618   * @since 3.0.0
1619   *
1620   * @return string
1621   */
1622  function get_background_color() {
1623      return get_theme_mod( 'background_color', get_theme_support( 'custom-background', 'default-color' ) );
1624  }
1625  
1626  /**
1627   * Display background color value.
1628   *
1629   * @since 3.0.0
1630   */
1631  function background_color() {
1632      echo get_background_color();
1633  }
1634  
1635  /**
1636   * Default custom background callback.
1637   *
1638   * @since 3.0.0
1639   */
1640  function _custom_background_cb() {
1641      // $background is the saved custom image, or the default image.
1642      $background = set_url_scheme( get_background_image() );
1643  
1644      // $color is the saved custom color.
1645      // A default has to be specified in style.css. It will not be printed here.
1646      $color = get_background_color();
1647  
1648      if ( $color === get_theme_support( 'custom-background', 'default-color' ) ) {
1649          $color = false;
1650      }
1651  
1652      $type_attr = current_theme_supports( 'html5', 'style' ) ? '' : ' type="text/css"';
1653  
1654      if ( ! $background && ! $color ) {
1655          if ( is_customize_preview() ) {
1656              printf( '<style%s id="custom-background-css"></style>', $type_attr );
1657          }
1658          return;
1659      }
1660  
1661      $style = $color ? "background-color: #$color;" : '';
1662  
1663      if ( $background ) {
1664          $image = ' background-image: url("' . esc_url_raw( $background ) . '");';
1665  
1666          // Background Position.
1667          $position_x = get_theme_mod( 'background_position_x', get_theme_support( 'custom-background', 'default-position-x' ) );
1668          $position_y = get_theme_mod( 'background_position_y', get_theme_support( 'custom-background', 'default-position-y' ) );
1669  
1670          if ( ! in_array( $position_x, array( 'left', 'center', 'right' ), true ) ) {
1671              $position_x = 'left';
1672          }
1673  
1674          if ( ! in_array( $position_y, array( 'top', 'center', 'bottom' ), true ) ) {
1675              $position_y = 'top';
1676          }
1677  
1678          $position = " background-position: $position_x $position_y;";
1679  
1680          // Background Size.
1681          $size = get_theme_mod( 'background_size', get_theme_support( 'custom-background', 'default-size' ) );
1682  
1683          if ( ! in_array( $size, array( 'auto', 'contain', 'cover' ), true ) ) {
1684              $size = 'auto';
1685          }
1686  
1687          $size = " background-size: $size;";
1688  
1689          // Background Repeat.
1690          $repeat = get_theme_mod( 'background_repeat', get_theme_support( 'custom-background', 'default-repeat' ) );
1691  
1692          if ( ! in_array( $repeat, array( 'repeat-x', 'repeat-y', 'repeat', 'no-repeat' ), true ) ) {
1693              $repeat = 'repeat';
1694          }
1695  
1696          $repeat = " background-repeat: $repeat;";
1697  
1698          // Background Scroll.
1699          $attachment = get_theme_mod( 'background_attachment', get_theme_support( 'custom-background', 'default-attachment' ) );
1700  
1701          if ( 'fixed' !== $attachment ) {
1702              $attachment = 'scroll';
1703          }
1704  
1705          $attachment = " background-attachment: $attachment;";
1706  
1707          $style .= $image . $position . $size . $repeat . $attachment;
1708      }
1709      ?>
1710  <style<?php echo $type_attr; ?> id="custom-background-css">
1711  body.custom-background { <?php echo trim( $style ); ?> }
1712  </style>
1713      <?php
1714  }
1715  
1716  /**
1717   * Render the Custom CSS style element.
1718   *
1719   * @since 4.7.0
1720   */
1721  function wp_custom_css_cb() {
1722      $styles = wp_get_custom_css();
1723      if ( $styles || is_customize_preview() ) :
1724          $type_attr = current_theme_supports( 'html5', 'style' ) ? '' : ' type="text/css"';
1725          ?>
1726          <style<?php echo $type_attr; ?> id="wp-custom-css">
1727              <?php echo strip_tags( $styles ); // Note that esc_html() cannot be used because `div &gt; span` is not interpreted properly. ?>
1728          </style>
1729          <?php
1730      endif;
1731  }
1732  
1733  /**
1734   * Fetch the `custom_css` post for a given theme.
1735   *
1736   * @since 4.7.0
1737   *
1738   * @param string $stylesheet Optional. A theme object stylesheet name. Defaults to the current theme.
1739   * @return WP_Post|null The custom_css post or null if none exists.
1740   */
1741  function wp_get_custom_css_post( $stylesheet = '' ) {
1742      if ( empty( $stylesheet ) ) {
1743          $stylesheet = get_stylesheet();
1744      }
1745  
1746      $custom_css_query_vars = array(
1747          'post_type'              => 'custom_css',
1748          'post_status'            => get_post_stati(),
1749          'name'                   => sanitize_title( $stylesheet ),
1750          'posts_per_page'         => 1,
1751          'no_found_rows'          => true,
1752          'cache_results'          => true,
1753          'update_post_meta_cache' => false,
1754          'update_post_term_cache' => false,
1755          'lazy_load_term_meta'    => false,
1756      );
1757  
1758      $post = null;
1759      if ( get_stylesheet() === $stylesheet ) {
1760          $post_id = get_theme_mod( 'custom_css_post_id' );
1761  
1762          if ( $post_id > 0 && get_post( $post_id ) ) {
1763              $post = get_post( $post_id );
1764          }
1765  
1766          // `-1` indicates no post exists; no query necessary.
1767          if ( ! $post && -1 !== $post_id ) {
1768              $query = new WP_Query( $custom_css_query_vars );
1769              $post  = $query->post;
1770              /*
1771               * Cache the lookup. See wp_update_custom_css_post().
1772               * @todo This should get cleared if a custom_css post is added/removed.
1773               */
1774              set_theme_mod( 'custom_css_post_id', $post ? $post->ID : -1 );
1775          }
1776      } else {
1777          $query = new WP_Query( $custom_css_query_vars );
1778          $post  = $query->post;
1779      }
1780  
1781      return $post;
1782  }
1783  
1784  /**
1785   * Fetch the saved Custom CSS content for rendering.
1786   *
1787   * @since 4.7.0
1788   *
1789   * @param string $stylesheet Optional. A theme object stylesheet name. Defaults to the current theme.
1790   * @return string The Custom CSS Post content.
1791   */
1792  function wp_get_custom_css( $stylesheet = '' ) {
1793      $css = '';
1794  
1795      if ( empty( $stylesheet ) ) {
1796          $stylesheet = get_stylesheet();
1797      }
1798  
1799      $post = wp_get_custom_css_post( $stylesheet );
1800      if ( $post ) {
1801          $css = $post->post_content;
1802      }
1803  
1804      /**
1805       * Filters the Custom CSS Output into the <head>.
1806       *
1807       * @since 4.7.0
1808       *
1809       * @param string $css        CSS pulled in from the Custom CSS CPT.
1810       * @param string $stylesheet The theme stylesheet name.
1811       */
1812      $css = apply_filters( 'wp_get_custom_css', $css, $stylesheet );
1813  
1814      return $css;
1815  }
1816  
1817  /**
1818   * Update the `custom_css` post for a given theme.
1819   *
1820   * Inserts a `custom_css` post when one doesn't yet exist.
1821   *
1822   * @since 4.7.0
1823   *
1824   * @param string $css CSS, stored in `post_content`.
1825   * @param array  $args {
1826   *     Args.
1827   *
1828   *     @type string $preprocessed Pre-processed CSS, stored in `post_content_filtered`. Normally empty string. Optional.
1829   *     @type string $stylesheet   Stylesheet (child theme) to update. Optional, defaults to current theme/stylesheet.
1830   * }
1831   * @return WP_Post|WP_Error Post on success, error on failure.
1832   */
1833  function wp_update_custom_css_post( $css, $args = array() ) {
1834      $args = wp_parse_args(
1835          $args,
1836          array(
1837              'preprocessed' => '',
1838              'stylesheet'   => get_stylesheet(),
1839          )
1840      );
1841  
1842      $data = array(
1843          'css'          => $css,
1844          'preprocessed' => $args['preprocessed'],
1845      );
1846  
1847      /**
1848       * Filters the `css` (`post_content`) and `preprocessed` (`post_content_filtered`) args for a `custom_css` post being updated.
1849       *
1850       * This filter can be used by plugin that offer CSS pre-processors, to store the original
1851       * pre-processed CSS in `post_content_filtered` and then store processed CSS in `post_content`.
1852       * When used in this way, the `post_content_filtered` should be supplied as the setting value
1853       * instead of `post_content` via a the `customize_value_custom_css` filter, for example:
1854       *
1855       * <code>
1856       * add_filter( 'customize_value_custom_css', function( $value, $setting ) {
1857       *     $post = wp_get_custom_css_post( $setting->stylesheet );
1858       *     if ( $post && ! empty( $post->post_content_filtered ) ) {
1859       *         $css = $post->post_content_filtered;
1860       *     }
1861       *     return $css;
1862       * }, 10, 2 );
1863       * </code>
1864       *
1865       * @since 4.7.0
1866       * @param array $data {
1867       *     Custom CSS data.
1868       *
1869       *     @type string $css          CSS stored in `post_content`.
1870       *     @type string $preprocessed Pre-processed CSS stored in `post_content_filtered`. Normally empty string.
1871       * }
1872       * @param array $args {
1873       *     The args passed into `wp_update_custom_css_post()` merged with defaults.
1874       *
1875       *     @type string $css          The original CSS passed in to be updated.
1876       *     @type string $preprocessed The original preprocessed CSS passed in to be updated.
1877       *     @type string $stylesheet   The stylesheet (theme) being updated.
1878       * }
1879       */
1880      $data = apply_filters( 'update_custom_css_data', $data, array_merge( $args, compact( 'css' ) ) );
1881  
1882      $post_data = array(
1883          'post_title'            => $args['stylesheet'],
1884          'post_name'             => sanitize_title( $args['stylesheet'] ),
1885          'post_type'             => 'custom_css',
1886          'post_status'           => 'publish',
1887          'post_content'          => $data['css'],
1888          'post_content_filtered' => $data['preprocessed'],
1889      );
1890  
1891      // Update post if it already exists, otherwise create a new one.
1892      $post = wp_get_custom_css_post( $args['stylesheet'] );
1893      if ( $post ) {
1894          $post_data['ID'] = $post->ID;
1895          $r               = wp_update_post( wp_slash( $post_data ), true );
1896      } else {
1897          $r = wp_insert_post( wp_slash( $post_data ), true );
1898  
1899          if ( ! is_wp_error( $r ) ) {
1900              if ( get_stylesheet() === $args['stylesheet'] ) {
1901                  set_theme_mod( 'custom_css_post_id', $r );
1902              }
1903  
1904              // Trigger creation of a revision. This should be removed once #30854 is resolved.
1905              if ( 0 === count( wp_get_post_revisions( $r ) ) ) {
1906                  wp_save_post_revision( $r );
1907              }
1908          }
1909      }
1910  
1911      if ( is_wp_error( $r ) ) {
1912          return $r;
1913      }
1914      return get_post( $r );
1915  }
1916  
1917  /**
1918   * Add callback for custom TinyMCE editor stylesheets.
1919   *
1920   * The parameter $stylesheet is the name of the stylesheet, relative to
1921   * the theme root. It also accepts an array of stylesheets.
1922   * It is optional and defaults to 'editor-style.css'.
1923   *
1924   * This function automatically adds another stylesheet with -rtl prefix, e.g. editor-style-rtl.css.
1925   * If that file doesn't exist, it is removed before adding the stylesheet(s) to TinyMCE.
1926   * If an array of stylesheets is passed to add_editor_style(),
1927   * RTL is only added for the first stylesheet.
1928   *
1929   * Since version 3.4 the TinyMCE body has .rtl CSS class.
1930   * It is a better option to use that class and add any RTL styles to the main stylesheet.
1931   *
1932   * @since 3.0.0
1933   *
1934   * @global array $editor_styles
1935   *
1936   * @param array|string $stylesheet Optional. Stylesheet name or array thereof, relative to theme root.
1937   *                                 Defaults to 'editor-style.css'
1938   */
1939  function add_editor_style( $stylesheet = 'editor-style.css' ) {
1940      global $editor_styles;
1941  
1942      add_theme_support( 'editor-style' );
1943  
1944      $editor_styles = (array) $editor_styles;
1945      $stylesheet    = (array) $stylesheet;
1946  
1947      if ( is_rtl() ) {
1948          $rtl_stylesheet = str_replace( '.css', '-rtl.css', $stylesheet[0] );
1949          $stylesheet[]   = $rtl_stylesheet;
1950      }
1951  
1952      $editor_styles = array_merge( $editor_styles, $stylesheet );
1953  }
1954  
1955  /**
1956   * Removes all visual editor stylesheets.
1957   *
1958   * @since 3.1.0
1959   *
1960   * @global array $editor_styles
1961   *
1962   * @return bool True on success, false if there were no stylesheets to remove.
1963   */
1964  function remove_editor_styles() {
1965      if ( ! current_theme_supports( 'editor-style' ) ) {
1966          return false;
1967      }
1968      _remove_theme_support( 'editor-style' );
1969      if ( is_admin() ) {
1970          $GLOBALS['editor_styles'] = array();
1971      }
1972      return true;
1973  }
1974  
1975  /**
1976   * Retrieve any registered editor stylesheets
1977   *
1978   * @since 4.0.0
1979   *
1980   * @global array $editor_styles Registered editor stylesheets
1981   *
1982   * @return array If registered, a list of editor stylesheet URLs.
1983   */
1984  function get_editor_stylesheets() {
1985      $stylesheets = array();
1986      // load editor_style.css if the current theme supports it
1987      if ( ! empty( $GLOBALS['editor_styles'] ) && is_array( $GLOBALS['editor_styles'] ) ) {
1988          $editor_styles = $GLOBALS['editor_styles'];
1989  
1990          $editor_styles = array_unique( array_filter( $editor_styles ) );
1991          $style_uri     = get_stylesheet_directory_uri();
1992          $style_dir     = get_stylesheet_directory();
1993  
1994          // Support externally referenced styles (like, say, fonts).
1995          foreach ( $editor_styles as $key => $file ) {
1996              if ( preg_match( '~^(https?:)?//~', $file ) ) {
1997                  $stylesheets[] = esc_url_raw( $file );
1998                  unset( $editor_styles[ $key ] );
1999              }
2000          }
2001  
2002          // Look in a parent theme first, that way child theme CSS overrides.
2003          if ( is_child_theme() ) {
2004              $template_uri = get_template_directory_uri();
2005              $template_dir = get_template_directory();
2006  
2007              foreach ( $editor_styles as $key => $file ) {
2008                  if ( $file && file_exists( "$template_dir/$file" ) ) {
2009                      $stylesheets[] = "$template_uri/$file";
2010                  }
2011              }
2012          }
2013  
2014          foreach ( $editor_styles as $file ) {
2015              if ( $file && file_exists( "$style_dir/$file" ) ) {
2016                  $stylesheets[] = "$style_uri/$file";
2017              }
2018          }
2019      }
2020  
2021      /**
2022       * Filters the array of stylesheets applied to the editor.
2023       *
2024       * @since 4.3.0
2025       *
2026       * @param array $stylesheets Array of stylesheets to be applied to the editor.
2027       */
2028      return apply_filters( 'editor_stylesheets', $stylesheets );
2029  }
2030  
2031  /**
2032   * Expand a theme's starter content configuration using core-provided data.
2033   *
2034   * @since 4.7.0
2035   *
2036   * @return array Array of starter content.
2037   */
2038  function get_theme_starter_content() {
2039      $theme_support = get_theme_support( 'starter-content' );
2040      if ( is_array( $theme_support ) && ! empty( $theme_support[0] ) && is_array( $theme_support[0] ) ) {
2041          $config = $theme_support[0];
2042      } else {
2043          $config = array();
2044      }
2045  
2046      $core_content = array(
2047          'widgets'   => array(
2048              'text_business_info' => array(
2049                  'text',
2050                  array(
2051                      'title'  => _x( 'Find Us', 'Theme starter content' ),
2052                      'text'   => join(
2053                          '',
2054                          array(
2055                              '<strong>' . _x( 'Address', 'Theme starter content' ) . "</strong>\n",
2056                              _x( '123 Main Street', 'Theme starter content' ) . "\n" . _x( 'New York, NY 10001', 'Theme starter content' ) . "\n\n",
2057                              '<strong>' . _x( 'Hours', 'Theme starter content' ) . "</strong>\n",
2058                              _x( 'Monday&mdash;Friday: 9:00AM&ndash;5:00PM', 'Theme starter content' ) . "\n" . _x( 'Saturday &amp; Sunday: 11:00AM&ndash;3:00PM', 'Theme starter content' ),
2059                          )
2060                      ),
2061                      'filter' => true,
2062                      'visual' => true,
2063                  ),
2064              ),
2065              'text_about'         => array(
2066                  'text',
2067                  array(
2068                      'title'  => _x( 'About This Site', 'Theme starter content' ),
2069                      'text'   => _x( 'This may be a good place to introduce yourself and your site or include some credits.', 'Theme starter content' ),
2070                      'filter' => true,
2071                      'visual' => true,
2072                  ),
2073              ),
2074              'archives'           => array(
2075                  'archives',
2076                  array(
2077                      'title' => _x( 'Archives', 'Theme starter content' ),
2078                  ),
2079              ),
2080              'calendar'           => array(
2081                  'calendar',
2082                  array(
2083                      'title' => _x( 'Calendar', 'Theme starter content' ),
2084                  ),
2085              ),
2086              'categories'         => array(
2087                  'categories',
2088                  array(
2089                      'title' => _x( 'Categories', 'Theme starter content' ),
2090                  ),
2091              ),
2092              'meta'               => array(
2093                  'meta',
2094                  array(
2095                      'title' => _x( 'Meta', 'Theme starter content' ),
2096                  ),
2097              ),
2098              'recent-comments'    => array(
2099                  'recent-comments',
2100                  array(
2101                      'title' => _x( 'Recent Comments', 'Theme starter content' ),
2102                  ),
2103              ),
2104              'recent-posts'       => array(
2105                  'recent-posts',
2106                  array(
2107                      'title' => _x( 'Recent Posts', 'Theme starter content' ),
2108                  ),
2109              ),
2110              'search'             => array(
2111                  'search',
2112                  array(
2113                      'title' => _x( 'Search', 'Theme starter content' ),
2114                  ),
2115              ),
2116          ),
2117          'nav_menus' => array(
2118              'link_home'       => array(
2119                  'type'  => 'custom',
2120                  'title' => _x( 'Home', 'Theme starter content' ),
2121                  'url'   => home_url( '/' ),
2122              ),
2123              'page_home'       => array( // Deprecated in favor of link_home.
2124                  'type'      => 'post_type',
2125                  'object'    => 'page',
2126                  'object_id' => '{{home}}',
2127              ),
2128              'page_about'      => array(
2129                  'type'      => 'post_type',
2130                  'object'    => 'page',
2131                  'object_id' => '{{about}}',
2132              ),
2133              'page_blog'       => array(
2134                  'type'      => 'post_type',
2135                  'object'    => 'page',
2136                  'object_id' => '{{blog}}',
2137              ),
2138              'page_news'       => array(
2139                  'type'      => 'post_type',
2140                  'object'    => 'page',
2141                  'object_id' => '{{news}}',
2142              ),
2143              'page_contact'    => array(
2144                  'type'      => 'post_type',
2145                  'object'    => 'page',
2146                  'object_id' => '{{contact}}',
2147              ),
2148  
2149              'link_email'      => array(
2150                  'title' => _x( 'Email', 'Theme starter content' ),
2151                  'url'   => 'mailto:wordpress@example.com',
2152              ),
2153              'link_facebook'   => array(
2154                  'title' => _x( 'Facebook', 'Theme starter content' ),
2155                  'url'   => 'https://www.facebook.com/wordpress',
2156              ),
2157              'link_foursquare' => array(
2158                  'title' => _x( 'Foursquare', 'Theme starter content' ),
2159                  'url'   => 'https://foursquare.com/',
2160              ),
2161              'link_github'     => array(
2162                  'title' => _x( 'GitHub', 'Theme starter content' ),
2163                  'url'   => 'https://github.com/wordpress/',
2164              ),
2165              'link_instagram'  => array(
2166                  'title' => _x( 'Instagram', 'Theme starter content' ),
2167                  'url'   => 'https://www.instagram.com/explore/tags/wordcamp/',
2168              ),
2169              'link_linkedin'   => array(
2170                  'title' => _x( 'LinkedIn', 'Theme starter content' ),
2171                  'url'   => 'https://www.linkedin.com/company/1089783',
2172              ),
2173              'link_pinterest'  => array(
2174                  'title' => _x( 'Pinterest', 'Theme starter content' ),
2175                  'url'   => 'https://www.pinterest.com/',
2176              ),
2177              'link_twitter'    => array(
2178                  'title' => _x( 'Twitter', 'Theme starter content' ),
2179                  'url'   => 'https://twitter.com/wordpress',
2180              ),
2181              'link_yelp'       => array(
2182                  'title' => _x( 'Yelp', 'Theme starter content' ),
2183                  'url'   => 'https://www.yelp.com',
2184              ),
2185              'link_youtube'    => array(
2186                  'title' => _x( 'YouTube', 'Theme starter content' ),
2187                  'url'   => 'https://www.youtube.com/channel/UCdof4Ju7amm1chz1gi1T2ZA',
2188              ),
2189          ),
2190          'posts'     => array(
2191              'home'             => array(
2192                  'post_type'    => 'page',
2193                  'post_title'   => _x( 'Home', 'Theme starter content' ),
2194                  'post_content' => sprintf(
2195                      "<!-- wp:paragraph -->\n<p>%s</p>\n<!-- /wp:paragraph -->",
2196                      _x( 'Welcome to your site! This is your homepage, which is what most visitors will see when they come to your site for the first time.', 'Theme starter content' )
2197                  ),
2198              ),
2199              'about'            => array(
2200                  'post_type'    => 'page',
2201                  'post_title'   => _x( 'About', 'Theme starter content' ),
2202                  'post_content' => sprintf(
2203                      "<!-- wp:paragraph -->\n<p>%s</p>\n<!-- /wp:paragraph -->",
2204                      _x( 'You might be an artist who would like to introduce yourself and your work here or maybe you&rsquo;re a business with a mission to describe.', 'Theme starter content' )
2205                  ),
2206              ),
2207              'contact'          => array(
2208                  'post_type'    => 'page',
2209                  'post_title'   => _x( 'Contact', 'Theme starter content' ),
2210                  'post_content' => sprintf(
2211                      "<!-- wp:paragraph -->\n<p>%s</p>\n<!-- /wp:paragraph -->",
2212                      _x( 'This is a page with some basic contact information, such as an address and phone number. You might also try a plugin to add a contact form.', 'Theme starter content' )
2213                  ),
2214              ),
2215              'blog'             => array(
2216                  'post_type'  => 'page',
2217                  'post_title' => _x( 'Blog', 'Theme starter content' ),
2218              ),
2219              'news'             => array(
2220                  'post_type'  => 'page',
2221                  'post_title' => _x( 'News', 'Theme starter content' ),
2222              ),
2223  
2224              'homepage-section' => array(
2225                  'post_type'    => 'page',
2226                  'post_title'   => _x( 'A homepage section', 'Theme starter content' ),
2227                  'post_content' => sprintf(
2228                      "<!-- wp:paragraph -->\n<p>%s</p>\n<!-- /wp:paragraph -->",
2229                      _x( 'This is an example of a homepage section. Homepage sections can be any page other than the homepage itself, including the page that shows your latest blog posts.', 'Theme starter content' )
2230                  ),
2231              ),
2232          ),
2233      );
2234  
2235      $content = array();
2236  
2237      foreach ( $config as $type => $args ) {
2238          switch ( $type ) {
2239              // Use options and theme_mods as-is.
2240              case 'options':
2241              case 'theme_mods':
2242                  $content[ $type ] = $config[ $type ];
2243                  break;
2244  
2245              // Widgets are grouped into sidebars.
2246              case 'widgets':
2247                  foreach ( $config[ $type ] as $sidebar_id => $widgets ) {
2248                      foreach ( $widgets as $id => $widget ) {
2249                          if ( is_array( $widget ) ) {
2250  
2251                              // Item extends core content.
2252                              if ( ! empty( $core_content[ $type ][ $id ] ) ) {
2253                                  $widget = array(
2254                                      $core_content[ $type ][ $id ][0],
2255                                      array_merge( $core_content[ $type ][ $id ][1], $widget ),
2256                                  );
2257                              }
2258  
2259                              $content[ $type ][ $sidebar_id ][] = $widget;
2260                          } elseif ( is_string( $widget ) && ! empty( $core_content[ $type ] ) && ! empty( $core_content[ $type ][ $widget ] ) ) {
2261                              $content[ $type ][ $sidebar_id ][] = $core_content[ $type ][ $widget ];
2262                          }
2263                      }
2264                  }
2265                  break;
2266  
2267              // And nav menu items are grouped into nav menus.
2268              case 'nav_menus':
2269                  foreach ( $config[ $type ] as $nav_menu_location => $nav_menu ) {
2270  
2271                      // Ensure nav menus get a name.
2272                      if ( empty( $nav_menu['name'] ) ) {
2273                          $nav_menu['name'] = $nav_menu_location;
2274                      }
2275  
2276                      $content[ $type ][ $nav_menu_location ]['name'] = $nav_menu['name'];
2277  
2278                      foreach ( $nav_menu['items'] as $id => $nav_menu_item ) {
2279                          if ( is_array( $nav_menu_item ) ) {
2280  
2281                              // Item extends core content.
2282                              if ( ! empty( $core_content[ $type ][ $id ] ) ) {
2283                                  $nav_menu_item = array_merge( $core_content[ $type ][ $id ], $nav_menu_item );
2284                              }
2285  
2286                              $content[ $type ][ $nav_menu_location ]['items'][] = $nav_menu_item;
2287                          } elseif ( is_string( $nav_menu_item ) && ! empty( $core_content[ $type ] ) && ! empty( $core_content[ $type ][ $nav_menu_item ] ) ) {
2288                              $content[ $type ][ $nav_menu_location ]['items'][] = $core_content[ $type ][ $nav_menu_item ];
2289                          }
2290                      }
2291                  }
2292                  break;
2293  
2294              // Attachments are posts but have special treatment.
2295              case 'attachments':
2296                  foreach ( $config[ $type ] as $id => $item ) {
2297                      if ( ! empty( $item['file'] ) ) {
2298                          $content[ $type ][ $id ] = $item;
2299                      }
2300                  }
2301                  break;
2302  
2303              // All that's left now are posts (besides attachments). Not a default case for the sake of clarity and future work.
2304              case 'posts':
2305                  foreach ( $config[ $type ] as $id => $item ) {
2306                      if ( is_array( $item ) ) {
2307  
2308                          // Item extends core content.
2309                          if ( ! empty( $core_content[ $type ][ $id ] ) ) {
2310                              $item = array_merge( $core_content[ $type ][ $id ], $item );
2311                          }
2312  
2313                          // Enforce a subset of fields.
2314                          $content[ $type ][ $id ] = wp_array_slice_assoc(
2315                              $item,
2316                              array(
2317                                  'post_type',
2318                                  'post_title',
2319                                  'post_excerpt',
2320                                  'post_name',
2321                                  'post_content',
2322                                  'menu_order',
2323                                  'comment_status',
2324                                  'thumbnail',
2325                                  'template',
2326                              )
2327                          );
2328                      } elseif ( is_string( $item ) && ! empty( $core_content[ $type ][ $item ] ) ) {
2329                          $content[ $type ][ $item ] = $core_content[ $type ][ $item ];
2330                      }
2331                  }
2332                  break;
2333          }
2334      }
2335  
2336      /**
2337       * Filters the expanded array of starter content.
2338       *
2339       * @since 4.7.0
2340       *
2341       * @param array $content Array of starter content.
2342       * @param array $config  Array of theme-specific starter content configuration.
2343       */
2344      return apply_filters( 'get_theme_starter_content', $content, $config );
2345  }
2346  
2347  /**
2348   * Registers theme support for a given feature.
2349   *
2350   * Must be called in the theme's functions.php file to work.
2351   * If attached to a hook, it must be {@see 'after_setup_theme'}.
2352   * The {@see 'init'} hook may be too late for some features.
2353   *
2354   * Example usage:
2355   *
2356   *     add_theme_support( 'title-tag' );
2357   *     add_theme_support( 'custom-logo', array(
2358   *         'height' => 480,
2359   *         'width'  => 720,
2360   *     ) );
2361   *
2362   * @since 2.9.0
2363   * @since 3.6.0 The `html5` feature was added.
2364   * @since 3.9.0 The `html5` feature now also accepts 'gallery' and 'caption'.
2365   * @since 4.1.0 The `title-tag` feature was added.
2366   * @since 4.5.0 The `customize-selective-refresh-widgets` feature was added.
2367   * @since 4.7.0 The `starter-content` feature was added.
2368   * @since 5.0.0 The `responsive-embeds`, `align-wide`, `dark-editor-style`, `disable-custom-colors`,
2369   *              `disable-custom-font-sizes`, `editor-color-palette`, `editor-font-sizes`,
2370   *              `editor-styles`, and `wp-block-styles` features were added.
2371   * @since 5.3.0 The `html5` feature now also accepts 'script' and 'style'.
2372   * @since 5.3.0 Formalized the existing and already documented `...$args` parameter
2373   *              by adding it to the function signature.
2374   *
2375   * @global array $_wp_theme_features
2376   *
2377   * @param string $feature The feature being added. Likely core values include 'post-formats',
2378   *                        'post-thumbnails', 'html5', 'custom-logo', 'custom-header-uploads',
2379   *                        'custom-header', 'custom-background', 'title-tag', 'starter-content',
2380   *                        'responsive-embeds', etc.
2381   * @param mixed  ...$args Optional extra arguments to pass along with certain features.
2382   * @return void|bool False on failure, void otherwise.
2383   */
2384  function add_theme_support( $feature, ...$args ) {
2385      global $_wp_theme_features;
2386  
2387      if ( ! $args ) {
2388          $args = true;
2389      }
2390  
2391      switch ( $feature ) {
2392          case 'post-thumbnails':
2393              // All post types are already supported.
2394              if ( true === get_theme_support( 'post-thumbnails' ) ) {
2395                  return;
2396              }
2397  
2398              /*
2399               * Merge post types with any that already declared their support
2400               * for post thumbnails.
2401               */
2402              if ( isset( $args[0] ) && is_array( $args[0] ) && isset( $_wp_theme_features['post-thumbnails'] ) ) {
2403                  $args[0] = array_unique( array_merge( $_wp_theme_features['post-thumbnails'][0], $args[0] ) );
2404              }
2405  
2406              break;
2407  
2408          case 'post-formats':
2409              if ( isset( $args[0] ) && is_array( $args[0] ) ) {
2410                  $post_formats = get_post_format_slugs();
2411                  unset( $post_formats['standard'] );
2412  
2413                  $args[0] = array_intersect( $args[0], array_keys( $post_formats ) );
2414              }
2415              break;
2416  
2417          case 'html5':
2418              // You can't just pass 'html5', you need to pass an array of types.
2419              if ( empty( $args[0] ) ) {
2420                  // Build an array of types for back-compat.
2421                  $args = array( 0 => array( 'comment-list', 'comment-form', 'search-form' ) );
2422              } elseif ( ! isset( $args[0] ) || ! is_array( $args[0] ) ) {
2423                  _doing_it_wrong( "add_theme_support( 'html5' )", __( 'You need to pass an array of types.' ), '3.6.1' );
2424                  return false;
2425              }
2426  
2427              // Calling 'html5' again merges, rather than overwrites.
2428              if ( isset( $_wp_theme_features['html5'] ) ) {
2429                  $args[0] = array_merge( $_wp_theme_features['html5'][0], $args[0] );
2430              }
2431              break;
2432  
2433          case 'custom-logo':
2434              if ( true === $args ) {
2435                  $args = array( 0 => array() );
2436              }
2437              $defaults = array(
2438                  'width'       => null,
2439                  'height'      => null,
2440                  'flex-width'  => false,
2441                  'flex-height' => false,
2442                  'header-text' => '',
2443              );
2444              $args[0]  = wp_parse_args( array_intersect_key( $args[0], $defaults ), $defaults );
2445  
2446              // Allow full flexibility if no size is specified.
2447              if ( is_null( $args[0]['width'] ) && is_null( $args[0]['height'] ) ) {
2448                  $args[0]['flex-width']  = true;
2449                  $args[0]['flex-height'] = true;
2450              }
2451              break;
2452  
2453          case 'custom-header-uploads':
2454              return add_theme_support( 'custom-header', array( 'uploads' => true ) );
2455  
2456          case 'custom-header':
2457              if ( true === $args ) {
2458                  $args = array( 0 => array() );
2459              }
2460  
2461              $defaults = array(
2462                  'default-image'          => '',
2463                  'random-default'         => false,
2464                  'width'                  => 0,
2465                  'height'                 => 0,
2466                  'flex-height'            => false,
2467                  'flex-width'             => false,
2468                  'default-text-color'     => '',
2469                  'header-text'            => true,
2470                  'uploads'                => true,
2471                  'wp-head-callback'       => '',
2472                  'admin-head-callback'    => '',
2473                  'admin-preview-callback' => '',
2474                  'video'                  => false,
2475                  'video-active-callback'  => 'is_front_page',
2476              );
2477  
2478              $jit = isset( $args[0]['__jit'] );
2479              unset( $args[0]['__jit'] );
2480  
2481              // Merge in data from previous add_theme_support() calls.
2482              // The first value registered wins. (A child theme is set up first.)
2483              if ( isset( $_wp_theme_features['custom-header'] ) ) {
2484                  $args[0] = wp_parse_args( $_wp_theme_features['custom-header'][0], $args[0] );
2485              }
2486  
2487              // Load in the defaults at the end, as we need to insure first one wins.
2488              // This will cause all constants to be defined, as each arg will then be set to the default.
2489              if ( $jit ) {
2490                  $args[0] = wp_parse_args( $args[0], $defaults );
2491              }
2492  
2493              // If a constant was defined, use that value. Otherwise, define the constant to ensure
2494              // the constant is always accurate (and is not defined later,  overriding our value).
2495              // As stated above, the first value wins.
2496              // Once we get to wp_loaded (just-in-time), define any constants we haven't already.
2497              // Constants are lame. Don't reference them. This is just for backward compatibility.
2498  
2499              if ( defined( 'NO_HEADER_TEXT' ) ) {
2500                  $args[0]['header-text'] = ! NO_HEADER_TEXT;
2501              } elseif ( isset( $args[0]['header-text'] ) ) {
2502                  define( 'NO_HEADER_TEXT', empty( $args[0]['header-text'] ) );
2503              }
2504  
2505              if ( defined( 'HEADER_IMAGE_WIDTH' ) ) {
2506                  $args[0]['width'] = (int) HEADER_IMAGE_WIDTH;
2507              } elseif ( isset( $args[0]['width'] ) ) {
2508                  define( 'HEADER_IMAGE_WIDTH', (int) $args[0]['width'] );
2509              }
2510  
2511              if ( defined( 'HEADER_IMAGE_HEIGHT' ) ) {
2512                  $args[0]['height'] = (int) HEADER_IMAGE_HEIGHT;
2513              } elseif ( isset( $args[0]['height'] ) ) {
2514                  define( 'HEADER_IMAGE_HEIGHT', (int) $args[0]['height'] );
2515              }
2516  
2517              if ( defined( 'HEADER_TEXTCOLOR' ) ) {
2518                  $args[0]['default-text-color'] = HEADER_TEXTCOLOR;
2519              } elseif ( isset( $args[0]['default-text-color'] ) ) {
2520                  define( 'HEADER_TEXTCOLOR', $args[0]['default-text-color'] );
2521              }
2522  
2523              if ( defined( 'HEADER_IMAGE' ) ) {
2524                  $args[0]['default-image'] = HEADER_IMAGE;
2525              } elseif ( isset( $args[0]['default-image'] ) ) {
2526                  define( 'HEADER_IMAGE', $args[0]['default-image'] );
2527              }
2528  
2529              if ( $jit && ! empty( $args[0]['default-image'] ) ) {
2530                  $args[0]['random-default'] = false;
2531              }
2532  
2533              // If headers are supported, and we still don't have a defined width or height,
2534              // we have implicit flex sizes.
2535              if ( $jit ) {
2536                  if ( empty( $args[0]['width'] ) && empty( $args[0]['flex-width'] ) ) {
2537                      $args[0]['flex-width'] = true;
2538                  }
2539                  if ( empty( $args[0]['height'] ) && empty( $args[0]['flex-height'] ) ) {
2540                      $args[0]['flex-height'] = true;
2541                  }
2542              }
2543  
2544              break;
2545  
2546          case 'custom-background':
2547              if ( true === $args ) {
2548                  $args = array( 0 => array() );
2549              }
2550  
2551              $defaults = array(
2552                  'default-image'          => '',
2553                  'default-preset'         => 'default',
2554                  'default-position-x'     => 'left',
2555                  'default-position-y'     => 'top',
2556                  'default-size'           => 'auto',
2557                  'default-repeat'         => 'repeat',
2558                  'default-attachment'     => 'scroll',
2559                  'default-color'          => '',
2560                  'wp-head-callback'       => '_custom_background_cb',
2561                  'admin-head-callback'    => '',
2562                  'admin-preview-callback' => '',
2563              );
2564  
2565              $jit = isset( $args[0]['__jit'] );
2566              unset( $args[0]['__jit'] );
2567  
2568              // Merge in data from previous add_theme_support() calls. The first value registered wins.
2569              if ( isset( $_wp_theme_features['custom-background'] ) ) {
2570                  $args[0] = wp_parse_args( $_wp_theme_features['custom-background'][0], $args[0] );
2571              }
2572  
2573              if ( $jit ) {
2574                  $args[0] = wp_parse_args( $args[0], $defaults );
2575              }
2576  
2577              if ( defined( 'BACKGROUND_COLOR' ) ) {
2578                  $args[0]['default-color'] = BACKGROUND_COLOR;
2579              } elseif ( isset( $args[0]['default-color'] ) || $jit ) {
2580                  define( 'BACKGROUND_COLOR', $args[0]['default-color'] );
2581              }
2582  
2583              if ( defined( 'BACKGROUND_IMAGE' ) ) {
2584                  $args[0]['default-image'] = BACKGROUND_IMAGE;
2585              } elseif ( isset( $args[0]['default-image'] ) || $jit ) {
2586                  define( 'BACKGROUND_IMAGE', $args[0]['default-image'] );
2587              }
2588  
2589              break;
2590  
2591          // Ensure that 'title-tag' is accessible in the admin.
2592          case 'title-tag':
2593              // Can be called in functions.php but must happen before wp_loaded, i.e. not in header.php.
2594              if ( did_action( 'wp_loaded' ) ) {
2595                  _doing_it_wrong(
2596                      "add_theme_support( 'title-tag' )",
2597                      sprintf(
2598                          /* translators: 1: title-tag, 2: wp_loaded */
2599                          __( 'Theme support for %1$s should be registered before the %2$s hook.' ),
2600                          '<code>title-tag</code>',
2601                          '<code>wp_loaded</code>'
2602                      ),
2603                      '4.1.0'
2604                  );
2605  
2606                  return false;
2607              }
2608      }
2609  
2610      $_wp_theme_features[ $feature ] = $args;
2611  }
2612  
2613  /**
2614   * Registers the internal custom header and background routines.
2615   *
2616   * @since 3.4.0
2617   * @access private
2618   *
2619   * @global Custom_Image_Header $custom_image_header
2620   * @global Custom_Background   $custom_background
2621   */
2622  function _custom_header_background_just_in_time() {
2623      global $custom_image_header, $custom_background;
2624  
2625      if ( current_theme_supports( 'custom-header' ) ) {
2626          // In case any constants were defined after an add_custom_image_header() call, re-run.
2627          add_theme_support( 'custom-header', array( '__jit' => true ) );
2628  
2629          $args = get_theme_support( 'custom-header' );
2630          if ( $args[0]['wp-head-callback'] ) {
2631              add_action( 'wp_head', $args[0]['wp-head-callback'] );
2632          }
2633  
2634          if ( is_admin() ) {
2635              require_once ( ABSPATH . 'wp-admin/includes/class-custom-image-header.php' );
2636              $custom_image_header = new Custom_Image_Header( $args[0]['admin-head-callback'], $args[0]['admin-preview-callback'] );
2637          }
2638      }
2639  
2640      if ( current_theme_supports( 'custom-background' ) ) {
2641          // In case any constants were defined after an add_custom_background() call, re-run.
2642          add_theme_support( 'custom-background', array( '__jit' => true ) );
2643  
2644          $args = get_theme_support( 'custom-background' );
2645          add_action( 'wp_head', $args[0]['wp-head-callback'] );
2646  
2647          if ( is_admin() ) {
2648              require_once ( ABSPATH . 'wp-admin/includes/class-custom-background.php' );
2649              $custom_background = new Custom_Background( $args[0]['admin-head-callback'], $args[0]['admin-preview-callback'] );
2650          }
2651      }
2652  }
2653  
2654  /**
2655   * Adds CSS to hide header text for custom logo, based on Customizer setting.
2656   *
2657   * @since 4.5.0
2658   * @access private
2659   */
2660  function _custom_logo_header_styles() {
2661      if ( ! current_theme_supports( 'custom-header', 'header-text' ) && get_theme_support( 'custom-logo', 'header-text' ) && ! get_theme_mod( 'header_text', true ) ) {
2662          $classes = (array) get_theme_support( 'custom-logo', 'header-text' );
2663          $classes = array_map( 'sanitize_html_class', $classes );
2664          $classes = '.' . implode( ', .', $classes );
2665  
2666          $type_attr = current_theme_supports( 'html5', 'style' ) ? '' : ' type="text/css"';
2667          ?>
2668          <!-- Custom Logo: hide header text -->
2669          <style id="custom-logo-css"<?php echo $type_attr; ?>>
2670              <?php echo $classes; ?> {
2671                  position: absolute;
2672                  clip: rect(1px, 1px, 1px, 1px);
2673              }
2674          </style>
2675          <?php
2676      }
2677  }
2678  
2679  /**
2680   * Gets the theme support arguments passed when registering that support
2681   *
2682   * Example usage:
2683   *
2684   *     get_theme_support( 'custom-logo' );
2685   *     get_theme_support( 'custom-header', 'width' );
2686   *
2687   * @since 3.1.0
2688   * @since 5.3.0 Formalized the existing and already documented `...$args` parameter
2689   *              by adding it to the function signature.
2690   *
2691   * @global array $_wp_theme_features
2692   *
2693   * @param string $feature The feature to check.
2694   * @param mixed  ...$args Optional extra arguments to be checked against certain features.
2695   * @return mixed The array of extra arguments or the value for the registered feature.
2696   */
2697  function get_theme_support( $feature, ...$args ) {
2698      global $_wp_theme_features;
2699      if ( ! isset( $_wp_theme_features[ $feature ] ) ) {
2700          return false;
2701      }
2702  
2703      if ( ! $args ) {
2704          return $_wp_theme_features[ $feature ];
2705      }
2706  
2707      switch ( $feature ) {
2708          case 'custom-logo':
2709          case 'custom-header':
2710          case 'custom-background':
2711              if ( isset( $_wp_theme_features[ $feature ][0][ $args[0] ] ) ) {
2712                  return $_wp_theme_features[ $feature ][0][ $args[0] ];
2713              }
2714              return false;
2715  
2716          default:
2717              return $_wp_theme_features[ $feature ];
2718      }
2719  }
2720  
2721  /**
2722   * Allows a theme to de-register its support of a certain feature
2723   *
2724   * Should be called in the theme's functions.php file. Generally would
2725   * be used for child themes to override support from the parent theme.
2726   *
2727   * @since 3.0.0
2728   * @see add_theme_support()
2729   * @param string $feature The feature being removed.
2730   * @return bool|void Whether feature was removed.
2731   */
2732  function remove_theme_support( $feature ) {
2733      // Blacklist: for internal registrations not used directly by themes.
2734      if ( in_array( $feature, array( 'editor-style', 'widgets', 'menus' ) ) ) {
2735          return false;
2736      }
2737  
2738      return _remove_theme_support( $feature );
2739  }
2740  
2741  /**
2742   * Do not use. Removes theme support internally, ignorant of the blacklist.
2743   *
2744   * @access private
2745   * @since 3.1.0
2746   *
2747   * @global array               $_wp_theme_features
2748   * @global Custom_Image_Header $custom_image_header
2749   * @global Custom_Background   $custom_background
2750   *
2751   * @param string $feature
2752   */
2753  function _remove_theme_support( $feature ) {
2754      global $_wp_theme_features;
2755  
2756      switch ( $feature ) {
2757          case 'custom-header-uploads':
2758              if ( ! isset( $_wp_theme_features['custom-header'] ) ) {
2759                  return false;
2760              }
2761              add_theme_support( 'custom-header', array( 'uploads' => false ) );
2762              return; // Do not continue - custom-header-uploads no longer exists.
2763      }
2764  
2765      if ( ! isset( $_wp_theme_features[ $feature ] ) ) {
2766          return false;
2767      }
2768  
2769      switch ( $feature ) {
2770          case 'custom-header':
2771              if ( ! did_action( 'wp_loaded' ) ) {
2772                  break;
2773              }
2774              $support = get_theme_support( 'custom-header' );
2775              if ( isset( $support[0]['wp-head-callback'] ) ) {
2776                  remove_action( 'wp_head', $support[0]['wp-head-callback'] );
2777              }
2778              if ( isset( $GLOBALS['custom_image_header'] ) ) {
2779                  remove_action( 'admin_menu', array( $GLOBALS['custom_image_header'], 'init' ) );
2780                  unset( $GLOBALS['custom_image_header'] );
2781              }
2782              break;
2783  
2784          case 'custom-background':
2785              if ( ! did_action( 'wp_loaded' ) ) {
2786                  break;
2787              }
2788              $support = get_theme_support( 'custom-background' );
2789              if ( isset( $support[0]['wp-head-callback'] ) ) {
2790                  remove_action( 'wp_head', $support[0]['wp-head-callback'] );
2791              }
2792              remove_action( 'admin_menu', array( $GLOBALS['custom_background'], 'init' ) );
2793              unset( $GLOBALS['custom_background'] );
2794              break;
2795      }
2796  
2797      unset( $_wp_theme_features[ $feature ] );
2798      return true;
2799  }
2800  
2801  /**
2802   * Checks a theme's support for a given feature.
2803   *
2804   * Example usage:
2805   *
2806   *     current_theme_supports( 'custom-logo' );
2807   *     current_theme_supports( 'html5', 'comment-form' );
2808   *
2809   * @since 2.9.0
2810   * @since 5.3.0 Formalized the existing and already documented `...$args` parameter
2811   *              by adding it to the function signature.
2812   *
2813   * @global array $_wp_theme_features
2814   *
2815   * @param string $feature The feature being checked.
2816   * @param mixed  ...$args Optional extra arguments to be checked against certain features.
2817   * @return bool True if the current theme supports the feature, false otherwise.
2818   */
2819  function current_theme_supports( $feature, ...$args ) {
2820      global $_wp_theme_features;
2821  
2822      if ( 'custom-header-uploads' == $feature ) {
2823          return current_theme_supports( 'custom-header', 'uploads' );
2824      }
2825  
2826      if ( ! isset( $_wp_theme_features[ $feature ] ) ) {
2827          return false;
2828      }
2829  
2830      // If no args passed then no extra checks need be performed
2831      if ( ! $args ) {
2832          return true;
2833      }
2834  
2835      switch ( $feature ) {
2836          case 'post-thumbnails':
2837              // post-thumbnails can be registered for only certain content/post types by passing
2838              // an array of types to add_theme_support(). If no array was passed, then
2839              // any type is accepted
2840              if ( true === $_wp_theme_features[ $feature ] ) {  // Registered for all types
2841                  return true;
2842              }
2843              $content_type = $args[0];
2844              return in_array( $content_type, $_wp_theme_features[ $feature ][0] );
2845  
2846          case 'html5':
2847          case 'post-formats':
2848              // specific post formats can be registered by passing an array of types to
2849              // add_theme_support()
2850  
2851              // Specific areas of HTML5 support *must* be passed via an array to add_theme_support()
2852  
2853              $type = $args[0];
2854              return in_array( $type, $_wp_theme_features[ $feature ][0] );
2855  
2856          case 'custom-logo':
2857          case 'custom-header':
2858          case 'custom-background':
2859              // Specific capabilities can be registered by passing an array to add_theme_support().
2860              return ( isset( $_wp_theme_features[ $feature ][0][ $args[0] ] ) && $_wp_theme_features[ $feature ][0][ $args[0] ] );
2861      }
2862  
2863      /**
2864       * Filters whether the current theme supports a specific feature.
2865       *
2866       * The dynamic portion of the hook name, `$feature`, refers to the specific theme
2867       * feature. Possible values include 'post-formats', 'post-thumbnails', 'custom-background',
2868       * 'custom-header', 'menus', 'automatic-feed-links', 'html5',
2869       * 'starter-content', and 'customize-selective-refresh-widgets'.
2870       *
2871       * @since 3.4.0
2872       *
2873       * @param bool   true     Whether the current theme supports the given feature. Default true.
2874       * @param array  $args    Array of arguments for the feature.
2875       * @param string $feature The theme feature.
2876       */
2877      return apply_filters( "current_theme_supports-{$feature}", true, $args, $_wp_theme_features[ $feature ] ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
2878  }
2879  
2880  /**
2881   * Checks a theme's support for a given feature before loading the functions which implement it.
2882   *
2883   * @since 2.9.0
2884   *
2885   * @param string $feature The feature being checked.
2886   * @param string $include Path to the file.
2887   * @return bool True if the current theme supports the supplied feature, false otherwise.
2888   */
2889  function require_if_theme_supports( $feature, $include ) {
2890      if ( current_theme_supports( $feature ) ) {
2891          require( $include );
2892          return true;
2893      }
2894      return false;
2895  }
2896  
2897  /**
2898   * Checks an attachment being deleted to see if it's a header or background image.
2899   *
2900   * If true it removes the theme modification which would be pointing at the deleted
2901   * attachment.
2902   *
2903   * @access private
2904   * @since 3.0.0
2905   * @since 4.3.0 Also removes `header_image_data`.
2906   * @since 4.5.0 Also removes custom logo theme mods.
2907   *
2908   * @param int $id The attachment id.
2909   */
2910  function _delete_attachment_theme_mod( $id ) {
2911      $attachment_image = wp_get_attachment_url( $id );
2912      $header_image     = get_header_image();
2913      $background_image = get_background_image();
2914      $custom_logo_id   = get_theme_mod( 'custom_logo' );
2915  
2916      if ( $custom_logo_id && $custom_logo_id == $id ) {
2917          remove_theme_mod( 'custom_logo' );
2918          remove_theme_mod( 'header_text' );
2919      }
2920  
2921      if ( $header_image && $header_image == $attachment_image ) {
2922          remove_theme_mod( 'header_image' );
2923          remove_theme_mod( 'header_image_data' );
2924      }
2925  
2926      if ( $background_image && $background_image == $attachment_image ) {
2927          remove_theme_mod( 'background_image' );
2928      }
2929  }
2930  
2931  /**
2932   * Checks if a theme has been changed and runs 'after_switch_theme' hook on the next WP load.
2933   *
2934   * See {@see 'after_switch_theme'}.
2935   *
2936   * @since 3.3.0
2937   */
2938  function check_theme_switched() {
2939      $stylesheet = get_option( 'theme_switched' );
2940      if ( $stylesheet ) {
2941          $old_theme = wp_get_theme( $stylesheet );
2942  
2943          // Prevent widget & menu mapping from running since Customizer already called it up front
2944          if ( get_option( 'theme_switched_via_customizer' ) ) {
2945              remove_action( 'after_switch_theme', '_wp_menus_changed' );
2946              remove_action( 'after_switch_theme', '_wp_sidebars_changed' );
2947              update_option( 'theme_switched_via_customizer', false );
2948          }
2949  
2950          if ( $old_theme->exists() ) {
2951              /**
2952               * Fires on the first WP load after a theme switch if the old theme still exists.
2953               *
2954               * This action fires multiple times and the parameters differs
2955               * according to the context, if the old theme exists or not.
2956               * If the old theme is missing, the parameter will be the slug
2957               * of the old theme.
2958               *
2959               * @since 3.3.0
2960               *
2961               * @param string   $old_name  Old theme name.
2962               * @param WP_Theme $old_theme WP_Theme instance of the old theme.
2963               */
2964              do_action( 'after_switch_theme', $old_theme->get( 'Name' ), $old_theme );
2965          } else {
2966              /** This action is documented in wp-includes/theme.php */
2967              do_action( 'after_switch_theme', $stylesheet, $old_theme );
2968          }
2969          flush_rewrite_rules();
2970  
2971          update_option( 'theme_switched', false );
2972      }
2973  }
2974  
2975  /**
2976   * Includes and instantiates the WP_Customize_Manager class.
2977   *
2978   * Loads the Customizer at plugins_loaded when accessing the customize.php admin
2979   * page or when any request includes a wp_customize=on param or a customize_changeset
2980   * param (a UUID). This param is a signal for whether to bootstrap the Customizer when
2981   * WordPress is loading, especially in the Customizer preview
2982   * or when making Customizer Ajax requests for widgets or menus.
2983   *
2984   * @since 3.4.0
2985   *
2986   * @global WP_Customize_Manager $wp_customize
2987   */
2988  function _wp_customize_include() {
2989  
2990      $is_customize_admin_page = ( is_admin() && 'customize.php' == basename( $_SERVER['PHP_SELF'] ) );
2991      $should_include          = (
2992          $is_customize_admin_page
2993          ||
2994          ( isset( $_REQUEST['wp_customize'] ) && 'on' == $_REQUEST['wp_customize'] )
2995          ||
2996          ( ! empty( $_GET['customize_changeset_uuid'] ) || ! empty( $_POST['customize_changeset_uuid'] ) )
2997      );
2998  
2999      if ( ! $should_include ) {
3000          return;
3001      }
3002  
3003      /*
3004       * Note that wp_unslash() is not being used on the input vars because it is
3005       * called before wp_magic_quotes() gets called. Besides this fact, none of
3006       * the values should contain any characters needing slashes anyway.
3007       */
3008      $keys       = array( 'changeset_uuid', 'customize_changeset_uuid', 'customize_theme', 'theme', 'customize_messenger_channel', 'customize_autosaved' );
3009      $input_vars = array_merge(
3010          wp_array_slice_assoc( $_GET, $keys ),
3011          wp_array_slice_assoc( $_POST, $keys )
3012      );
3013  
3014      $theme             = null;
3015      $changeset_uuid    = false; // Value false indicates UUID should be determined after_setup_theme to either re-use existing saved changeset or else generate a new UUID if none exists.
3016      $messenger_channel = null;
3017      $autosaved         = null;
3018      $branching         = false; // Set initially fo false since defaults to true for back-compat; can be overridden via the customize_changeset_branching filter.
3019  
3020      if ( $is_customize_admin_page && isset( $input_vars['changeset_uuid'] ) ) {
3021          $changeset_uuid = sanitize_key( $input_vars['changeset_uuid'] );
3022      } elseif ( ! empty( $input_vars['customize_changeset_uuid'] ) ) {
3023          $changeset_uuid = sanitize_key( $input_vars['customize_changeset_uuid'] );
3024      }
3025  
3026      // Note that theme will be sanitized via WP_Theme.
3027      if ( $is_customize_admin_page && isset( $input_vars['theme'] ) ) {
3028          $theme = $input_vars['theme'];
3029      } elseif ( isset( $input_vars['customize_theme'] ) ) {
3030          $theme = $input_vars['customize_theme'];
3031      }
3032  
3033      if ( ! empty( $input_vars['customize_autosaved'] ) ) {
3034          $autosaved = true;
3035      }
3036  
3037      if ( isset( $input_vars['customize_messenger_channel'] ) ) {
3038          $messenger_channel = sanitize_key( $input_vars['customize_messenger_channel'] );
3039      }
3040  
3041      /*
3042       * Note that settings must be previewed even outside the customizer preview
3043       * and also in the customizer pane itself. This is to enable loading an existing
3044       * changeset into the customizer. Previewing the settings only has to be prevented
3045       * here in the case of a customize_save action because this will cause WP to think
3046       * there is nothing changed that needs to be saved.
3047       */
3048      $is_customize_save_action = (
3049          wp_doing_ajax()
3050          &&
3051          isset( $_REQUEST['action'] )
3052          &&
3053          'customize_save' === wp_unslash( $_REQUEST['action'] )
3054      );
3055      $settings_previewed       = ! $is_customize_save_action;
3056  
3057      require_once  ABSPATH . WPINC . '/class-wp-customize-manager.php';
3058      $GLOBALS['wp_customize'] = new WP_Customize_Manager( compact( 'changeset_uuid', 'theme', 'messenger_channel', 'settings_previewed', 'autosaved', 'branching' ) );
3059  }
3060  
3061  /**
3062   * Publishes a snapshot's changes.
3063   *
3064   * @since 4.7.0
3065   * @access private
3066   *
3067   * @global wpdb                 $wpdb         WordPress database abstraction object.
3068   * @global WP_Customize_Manager $wp_customize Customizer instance.
3069   *
3070   * @param string  $new_status     New post status.
3071   * @param string  $old_status     Old post status.
3072   * @param WP_Post $changeset_post Changeset post object.
3073   */
3074  function _wp_customize_publish_changeset( $new_status, $old_status, $changeset_post ) {
3075      global $wp_customize, $wpdb;
3076  
3077      $is_publishing_changeset = (
3078          'customize_changeset' === $changeset_post->post_type
3079          &&
3080          'publish' === $new_status
3081          &&
3082          'publish' !== $old_status
3083      );
3084      if ( ! $is_publishing_changeset ) {
3085          return;
3086      }
3087  
3088      if ( empty( $wp_customize ) ) {
3089          require_once  ABSPATH . WPINC . '/class-wp-customize-manager.php';
3090          $wp_customize = new WP_Customize_Manager(
3091              array(
3092                  'changeset_uuid'     => $changeset_post->post_name,
3093                  'settings_previewed' => false,
3094              )
3095          );
3096      }
3097  
3098      if ( ! did_action( 'customize_register' ) ) {
3099          /*
3100           * When running from CLI or Cron, the customize_register action will need
3101           * to be triggered in order for core, themes, and plugins to register their
3102           * settings. Normally core will add_action( 'customize_register' ) at
3103           * priority 10 to register the core settings, and if any themes/plugins
3104           * also add_action( 'customize_register' ) at the same priority, they
3105           * will have a $wp_customize with those settings registered since they
3106           * call add_action() afterward, normally. However, when manually doing
3107           * the customize_register action after the setup_theme, then the order
3108           * will be reversed for two actions added at priority 10, resulting in
3109           * the core settings no longer being available as expected to themes/plugins.
3110           * So the following manually calls the method that registers the core
3111           * settings up front before doing the action.
3112           */
3113          remove_action( 'customize_register', array( $wp_customize, 'register_controls' ) );
3114          $wp_customize->register_controls();
3115  
3116          /** This filter is documented in /wp-includes/class-wp-customize-manager.php */
3117          do_action( 'customize_register', $wp_customize );
3118      }
3119      $wp_customize->_publish_changeset_values( $changeset_post->ID );
3120  
3121      /*
3122       * Trash the changeset post if revisions are not enabled. Unpublished
3123       * changesets by default get garbage collected due to the auto-draft status.
3124       * When a changeset post is published, however, it would no longer get cleaned
3125       * out. This is a problem when the changeset posts are never displayed anywhere,
3126       * since they would just be endlessly piling up. So here we use the revisions
3127       * feature to indicate whether or not a published changeset should get trashed
3128       * and thus garbage collected.
3129       */
3130      if ( ! wp_revisions_enabled( $changeset_post ) ) {
3131          $wp_customize->trash_changeset_post( $changeset_post->ID );
3132      }
3133  }
3134  
3135  /**
3136   * Filters changeset post data upon insert to ensure post_name is intact.
3137   *
3138   * This is needed to prevent the post_name from being dropped when the post is
3139   * transitioned into pending status by a contributor.
3140   *
3141   * @since 4.7.0
3142   * @see wp_insert_post()
3143   *
3144   * @param array $post_data          An array of slashed post data.
3145   * @param array $supplied_post_data An array of sanitized, but otherwise unmodified post data.
3146   * @returns array Filtered data.
3147   */
3148  function _wp_customize_changeset_filter_insert_post_data( $post_data, $supplied_post_data ) {
3149      if ( isset( $post_data['post_type'] ) && 'customize_changeset' === $post_data['post_type'] ) {
3150  
3151          // Prevent post_name from being dropped, such as when contributor saves a changeset post as pending.
3152          if ( empty( $post_data['post_name'] ) && ! empty( $supplied_post_data['post_name'] ) ) {
3153              $post_data['post_name'] = $supplied_post_data['post_name'];
3154          }
3155      }
3156      return $post_data;
3157  }
3158  
3159  /**
3160   * Adds settings for the customize-loader script.
3161   *
3162   * @since 3.4.0
3163   */
3164  function _wp_customize_loader_settings() {
3165      $admin_origin = parse_url( admin_url() );
3166      $home_origin  = parse_url( home_url() );
3167      $cross_domain = ( strtolower( $admin_origin['host'] ) != strtolower( $home_origin['host'] ) );
3168  
3169      $browser = array(
3170          'mobile' => wp_is_mobile(),
3171          'ios'    => wp_is_mobile() && preg_match( '/iPad|iPod|iPhone/', $_SERVER['HTTP_USER_AGENT'] ),
3172      );
3173  
3174      $settings = array(
3175          'url'           => esc_url( admin_url( 'customize.php' ) ),
3176          'isCrossDomain' => $cross_domain,
3177          'browser'       => $browser,
3178          'l10n'          => array(
3179              'saveAlert'       => __( 'The changes you made will be lost if you navigate away from this page.' ),
3180              'mainIframeTitle' => __( 'Customizer' ),
3181          ),
3182      );
3183  
3184      $script = 'var _wpCustomizeLoaderSettings = ' . wp_json_encode( $settings ) . ';';
3185  
3186      $wp_scripts = wp_scripts();
3187      $data       = $wp_scripts->get_data( 'customize-loader', 'data' );
3188      if ( $data ) {
3189          $script = "$data\n$script";
3190      }
3191  
3192      $wp_scripts->add_data( 'customize-loader', 'data', $script );
3193  }
3194  
3195  /**
3196   * Returns a URL to load the Customizer.
3197   *
3198   * @since 3.4.0
3199   *
3200   * @param string $stylesheet Optional. Theme to customize. Defaults to current theme.
3201   *                           The theme's stylesheet will be urlencoded if necessary.
3202   * @return string
3203   */
3204  function wp_customize_url( $stylesheet = '' ) {
3205      $url = admin_url( 'customize.php' );
3206      if ( $stylesheet ) {
3207          $url .= '?theme=' . urlencode( $stylesheet );
3208      }
3209      return esc_url( $url );
3210  }
3211  
3212  /**
3213   * Prints a script to check whether or not the Customizer is supported,
3214   * and apply either the no-customize-support or customize-support class
3215   * to the body.
3216   *
3217   * This function MUST be called inside the body tag.
3218   *
3219   * Ideally, call this function immediately after the body tag is opened.
3220   * This prevents a flash of unstyled content.
3221   *
3222   * It is also recommended that you add the "no-customize-support" class
3223   * to the body tag by default.
3224   *
3225   * @since 3.4.0
3226   * @since 4.7.0 Support for IE8 and below is explicitly removed via conditional comments.
3227   */
3228  function wp_customize_support_script() {
3229      $admin_origin = parse_url( admin_url() );
3230      $home_origin  = parse_url( home_url() );
3231      $cross_domain = ( strtolower( $admin_origin['host'] ) != strtolower( $home_origin['host'] ) );
3232      $type_attr    = current_theme_supports( 'html5', 'script' ) ? '' : ' type="text/javascript"';
3233      ?>
3234      <!--[if lte IE 8]>
3235          <script<?php echo $type_attr; ?>>
3236              document.body.className = document.body.className.replace( /(^|\s)(no-)?customize-support(?=\s|$)/, '' ) + ' no-customize-support';
3237          </script>
3238      <![endif]-->
3239      <!--[if gte IE 9]><!-->
3240          <script<?php echo $type_attr; ?>>
3241              (function() {
3242                  var request, b = document.body, c = 'className', cs = 'customize-support', rcs = new RegExp('(^|\\s+)(no-)?'+cs+'(\\s+|$)');
3243  
3244          <?php    if ( $cross_domain ) : ?>
3245                  request = (function(){ var xhr = new XMLHttpRequest(); return ('withCredentials' in xhr); })();
3246          <?php    else : ?>
3247                  request = true;
3248          <?php    endif; ?>
3249  
3250                  b[c] = b[c].replace( rcs, ' ' );
3251                  // The customizer requires postMessage and CORS (if the site is cross domain)
3252                  b[c] += ( window.postMessage && request ? ' ' : ' no-' ) + cs;
3253              }());
3254          </script>
3255      <!--<![endif]-->
3256      <?php
3257  }
3258  
3259  /**
3260   * Whether the site is being previewed in the Customizer.
3261   *
3262   * @since 4.0.0
3263   *
3264   * @global WP_Customize_Manager $wp_customize Customizer instance.
3265   *
3266   * @return bool True if the site is being previewed in the Customizer, false otherwise.
3267   */
3268  function is_customize_preview() {
3269      global $wp_customize;
3270  
3271      return ( $wp_customize instanceof WP_Customize_Manager ) && $wp_customize->is_preview();
3272  }
3273  
3274  /**
3275   * Make sure that auto-draft posts get their post_date bumped or status changed to draft to prevent premature garbage-collection.
3276   *
3277   * When a changeset is updated but remains an auto-draft, ensure the post_date
3278   * for the auto-draft posts remains the same so that it will be
3279   * garbage-collected at the same time by `wp_delete_auto_drafts()`. Otherwise,
3280   * if the changeset is updated to be a draft then update the posts
3281   * to have a far-future post_date so that they will never be garbage collected
3282   * unless the changeset post itself is deleted.
3283   *
3284   * When a changeset is updated to be a persistent draft or to be scheduled for
3285   * publishing, then transition any dependent auto-drafts to a draft status so
3286   * that they likewise will not be garbage-collected but also so that they can
3287   * be edited in the admin before publishing since there is not yet a post/page
3288   * editing flow in the Customizer. See #39752.
3289   *
3290   * @link https://core.trac.wordpress.org/ticket/39752
3291   *
3292   * @since 4.8.0
3293   * @access private
3294   * @see wp_delete_auto_drafts()
3295   *
3296   * @global wpdb $wpdb WordPress database abstraction object.
3297   *
3298   * @param string   $new_status Transition to this post status.
3299   * @param string   $old_status Previous post status.
3300   * @param \WP_Post $post       Post data.
3301   */
3302  function _wp_keep_alive_customize_changeset_dependent_auto_drafts( $new_status, $old_status, $post ) {
3303      global $wpdb;
3304      unset( $old_status );
3305  
3306      // Short-circuit if not a changeset or if the changeset was published.
3307      if ( 'customize_changeset' !== $post->post_type || 'publish' === $new_status ) {
3308          return;
3309      }
3310  
3311      $data = json_decode( $post->post_content, true );
3312      if ( empty( $data['nav_menus_created_posts']['value'] ) ) {
3313          return;
3314      }
3315  
3316      /*
3317       * Actually, in lieu of keeping alive, trash any customization drafts here if the changeset itself is
3318       * getting trashed. This is needed because when a changeset transitions to a draft, then any of the
3319       * dependent auto-draft post/page stubs will also get transitioned to customization drafts which
3320       * are then visible in the WP Admin. We cannot wait for the deletion of the changeset in which
3321       * _wp_delete_customize_changeset_dependent_auto_drafts() will be called, since they need to be
3322       * trashed to remove from visibility immediately.
3323       */
3324      if ( 'trash' === $new_status ) {
3325          foreach ( $data['nav_menus_created_posts']['value'] as $post_id ) {
3326              if ( ! empty( $post_id ) && 'draft' === get_post_status( $post_id ) ) {
3327                  wp_trash_post( $post_id );
3328              }
3329          }
3330          return;
3331      }
3332  
3333      $post_args = array();
3334      if ( 'auto-draft' === $new_status ) {
3335          /*
3336           * Keep the post date for the post matching the changeset
3337           * so that it will not be garbage-collected before the changeset.
3338           */
3339          $post_args['post_date'] = $post->post_date; // Note wp_delete_auto_drafts() only looks at this date.
3340      } else {
3341          /*
3342           * Since the changeset no longer has an auto-draft (and it is not published)
3343           * it is now a persistent changeset, a long-lived draft, and so any
3344           * associated auto-draft posts should likewise transition into having a draft
3345           * status. These drafts will be treated differently than regular drafts in
3346           * that they will be tied to the given changeset. The publish meta box is
3347           * replaced with a notice about how the post is part of a set of customized changes
3348           * which will be published when the changeset is published.
3349           */
3350          $post_args['post_status'] = 'draft';
3351      }
3352  
3353      foreach ( $data['nav_menus_created_posts']['value'] as $post_id ) {
3354          if ( empty( $post_id ) || 'auto-draft' !== get_post_status( $post_id ) ) {
3355              continue;
3356          }
3357          $wpdb->update(
3358              $wpdb->posts,
3359              $post_args,
3360              array( 'ID' => $post_id )
3361          );
3362          clean_post_cache( $post_id );
3363      }
3364  }


Generated: Fri Oct 25 08:20:01 2019 Cross-referenced by PHPXref 0.7