[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

title

Body

[close]

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

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


Generated: Mon Jun 17 08:20:02 2019 Cross-referenced by PHPXref 0.7