[ Index ]

PHP Cross Reference of WordPress

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


Generated: Tue Jul 16 01:00:03 2019 Cross-referenced by PHPXref 0.7.1