[ Index ]

PHP Cross Reference of WordPress Trunk (Updated Daily)

title

Body

[close]

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

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


Generated: Sun Sep 22 08:20:01 2019 Cross-referenced by PHPXref 0.7