[ Index ]

WordPress Cross Reference

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   * @param array $args The search arguments. Optional.
  18   * - errors      mixed  True to return themes with errors, false to return themes without errors, null
  19   *                      to return all themes. Defaults to false.
  20   * - allowed     mixed  (Multisite) True to return only allowed themes for a site. False to return only
  21   *                      disallowed themes for a site. 'site' to return only site-allowed themes. 'network'
  22   *                      to return only network-allowed themes. Null to return all themes. Defaults to null.
  23   * - blog_id     int    (Multisite) The blog ID used to calculate which themes are allowed. Defaults to 0,
  24   *                      synonymous for the current blog.
  25   * @return Array of WP_Theme objects.
  26   */
  27  function wp_get_themes( $args = array() ) {
  28      global $wp_theme_directories;
  29  
  30      $defaults = array( 'errors' => false, 'allowed' => null, 'blog_id' => 0 );
  31      $args = wp_parse_args( $args, $defaults );
  32  
  33      $theme_directories = search_theme_directories();
  34  
  35      if ( count( $wp_theme_directories ) > 1 ) {
  36          // Make sure the current theme wins out, in case search_theme_directories() picks the wrong
  37          // one in the case of a conflict. (Normally, last registered theme root wins.)
  38          $current_theme = get_stylesheet();
  39          if ( isset( $theme_directories[ $current_theme ] ) ) {
  40              $root_of_current_theme = get_raw_theme_root( $current_theme );
  41              if ( ! in_array( $root_of_current_theme, $wp_theme_directories ) )
  42                  $root_of_current_theme = WP_CONTENT_DIR . $root_of_current_theme;
  43              $theme_directories[ $current_theme ]['theme_root'] = $root_of_current_theme;
  44          }
  45      }
  46  
  47      if ( empty( $theme_directories ) )
  48          return array();
  49  
  50      if ( is_multisite() && null !== $args['allowed'] ) {
  51          $allowed = $args['allowed'];
  52          if ( 'network' === $allowed )
  53              $theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed_on_network() );
  54          elseif ( 'site' === $allowed )
  55              $theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed_on_site( $args['blog_id'] ) );
  56          elseif ( $allowed )
  57              $theme_directories = array_intersect_key( $theme_directories, WP_Theme::get_allowed( $args['blog_id'] ) );
  58          else
  59              $theme_directories = array_diff_key( $theme_directories, WP_Theme::get_allowed( $args['blog_id'] ) );
  60      }
  61  
  62      $themes = array();
  63      static $_themes = array();
  64  
  65      foreach ( $theme_directories as $theme => $theme_root ) {
  66          if ( isset( $_themes[ $theme_root['theme_root'] . '/' . $theme ] ) )
  67              $themes[ $theme ] = $_themes[ $theme_root['theme_root'] . '/' . $theme ];
  68          else
  69              $themes[ $theme ] = $_themes[ $theme_root['theme_root'] . '/' . $theme ] = new WP_Theme( $theme, $theme_root['theme_root'] );
  70      }
  71  
  72      if ( null !== $args['errors'] ) {
  73          foreach ( $themes as $theme => $wp_theme ) {
  74              if ( $wp_theme->errors() != $args['errors'] )
  75                  unset( $themes[ $theme ] );
  76          }
  77      }
  78  
  79      return $themes;
  80  }
  81  
  82  /**
  83   * Gets a WP_Theme object for a theme.
  84   *
  85   * @since 3.4.0
  86   *
  87   * @param string $stylesheet Directory name for the theme. Optional. Defaults to current theme.
  88   * @param string $theme_root Absolute path of the theme root to look in. Optional. If not specified, get_raw_theme_root()
  89   *     is used to calculate the theme root for the $stylesheet provided (or current theme).
  90   * @return WP_Theme Theme object. Be sure to check the object's exists() method if you need to confirm the theme's existence.
  91   */
  92  function wp_get_theme( $stylesheet = null, $theme_root = null ) {
  93      global $wp_theme_directories;
  94  
  95      if ( empty( $stylesheet ) )
  96          $stylesheet = get_stylesheet();
  97  
  98      if ( empty( $theme_root ) ) {
  99          $theme_root = get_raw_theme_root( $stylesheet );
 100          if ( false === $theme_root )
 101              $theme_root = WP_CONTENT_DIR . '/themes';
 102          elseif ( ! in_array( $theme_root, (array) $wp_theme_directories ) )
 103              $theme_root = WP_CONTENT_DIR . $theme_root;
 104      }
 105  
 106      return new WP_Theme( $stylesheet, $theme_root );
 107  }
 108  
 109  /**
 110   * Clears the cache held by get_theme_roots() and WP_Theme.
 111   *
 112   * @since 3.5.0
 113   * @param bool $clear_update_cache Whether to clear the Theme updates cache
 114   */
 115  function wp_clean_themes_cache( $clear_update_cache = true ) {
 116      if ( $clear_update_cache )
 117          delete_site_transient( 'update_themes' );
 118      search_theme_directories( true );
 119      foreach ( wp_get_themes( array( 'errors' => null ) ) as $theme )
 120          $theme->cache_delete();
 121  }
 122  
 123  /**
 124   * Whether a child theme is in use.
 125   *
 126   * @since 3.0.0
 127   *
 128   * @return bool true if a child theme is in use, false otherwise.
 129   **/
 130  function is_child_theme() {
 131      return ( TEMPLATEPATH !== STYLESHEETPATH );
 132  }
 133  
 134  /**
 135   * Retrieve name of the current stylesheet.
 136   *
 137   * The theme name that the administrator has currently set the front end theme
 138   * as.
 139   *
 140   * For all extensive purposes, the template name and the stylesheet name are
 141   * going to be the same for most cases.
 142   *
 143   * @since 1.5.0
 144   *
 145   * @return string Stylesheet name.
 146   */
 147  function get_stylesheet() {
 148      /**
 149       * Filter the name of current stylesheet.
 150       *
 151       * @since 1.5.0
 152       *
 153       * @param string $stylesheet Name of the current stylesheet.
 154       */
 155      return apply_filters( 'stylesheet', get_option( 'stylesheet' ) );
 156  }
 157  
 158  /**
 159   * Retrieve stylesheet directory path for current theme.
 160   *
 161   * @since 1.5.0
 162   *
 163   * @return string Path to current theme directory.
 164   */
 165  function get_stylesheet_directory() {
 166      $stylesheet = get_stylesheet();
 167      $theme_root = get_theme_root( $stylesheet );
 168      $stylesheet_dir = "$theme_root/$stylesheet";
 169  
 170      /**
 171       * Filter the stylesheet directory path for current theme.
 172       *
 173       * @since 1.5.0
 174       *
 175       * @param string $stylesheet_dir Absolute path to the current them.
 176       * @param string $stylesheet     Directory name of the current theme.
 177       * @param string $theme_root     Absolute path to themes directory.
 178       */
 179      return apply_filters( 'stylesheet_directory', $stylesheet_dir, $stylesheet, $theme_root );
 180  }
 181  
 182  /**
 183   * Retrieve stylesheet directory URI.
 184   *
 185   * @since 1.5.0
 186   *
 187   * @return string
 188   */
 189  function get_stylesheet_directory_uri() {
 190      $stylesheet = get_stylesheet();
 191      $theme_root_uri = get_theme_root_uri( $stylesheet );
 192      $stylesheet_dir_uri = "$theme_root_uri/$stylesheet";
 193  
 194      /**
 195       * Filter the stylesheet directory URI.
 196       *
 197       * @since 1.5.0
 198       *
 199       * @param string $stylesheet_dir_uri Stylesheet directory URI.
 200       * @param string $stylesheet         Name of the activated theme's directory.
 201       * @param string $theme_root_uri     Themes root URI.
 202       */
 203      return apply_filters( 'stylesheet_directory_uri', $stylesheet_dir_uri, $stylesheet, $theme_root_uri );
 204  }
 205  
 206  /**
 207   * Retrieve URI of current theme stylesheet.
 208   *
 209   * The stylesheet file name is 'style.css' which is appended to {@link
 210   * get_stylesheet_directory_uri() stylesheet directory URI} path.
 211   *
 212   * @since 1.5.0
 213   *
 214   * @return string
 215   */
 216  function get_stylesheet_uri() {
 217      $stylesheet_dir_uri = get_stylesheet_directory_uri();
 218      $stylesheet_uri = $stylesheet_dir_uri . '/style.css';
 219      /**
 220       * Filter the URI of the current theme stylesheet.
 221       *
 222       * @since 1.5.0
 223       *
 224       * @param string $stylesheet_uri     Stylesheet URI for the current theme/child theme.
 225       * @param string $stylesheet_dir_uri Stylesheet directory URI for the current theme/child theme.
 226       */
 227      return apply_filters( 'stylesheet_uri', $stylesheet_uri, $stylesheet_dir_uri );
 228  }
 229  
 230  /**
 231   * Retrieve localized stylesheet URI.
 232   *
 233   * The stylesheet directory for the localized stylesheet files are located, by
 234   * default, in the base theme directory. The name of the locale file will be the
 235   * locale followed by '.css'. If that does not exist, then the text direction
 236   * stylesheet will be checked for existence, for example 'ltr.css'.
 237   *
 238   * The theme may change the location of the stylesheet directory by either using
 239   * the 'stylesheet_directory_uri' filter or the 'locale_stylesheet_uri' filter.
 240   * If you want to change the location of the stylesheet files for the entire
 241   * WordPress workflow, then change the former. If you just have the locale in a
 242   * separate folder, then change the latter.
 243   *
 244   * @since 2.1.0
 245   *
 246   * @return string
 247   */
 248  function get_locale_stylesheet_uri() {
 249      global $wp_locale;
 250      $stylesheet_dir_uri = get_stylesheet_directory_uri();
 251      $dir = get_stylesheet_directory();
 252      $locale = get_locale();
 253      if ( file_exists("$dir/$locale.css") )
 254          $stylesheet_uri = "$stylesheet_dir_uri/$locale.css";
 255      elseif ( !empty($wp_locale->text_direction) && file_exists("$dir/{$wp_locale->text_direction}.css") )
 256          $stylesheet_uri = "$stylesheet_dir_uri/{$wp_locale->text_direction}.css";
 257      else
 258          $stylesheet_uri = '';
 259      /**
 260       * Filter the localized stylesheet URI.
 261       *
 262       * @since 2.1.0
 263       *
 264       * @param string $stylesheet_uri     Localized stylesheet URI.
 265       * @param string $stylesheet_dir_uri Stylesheet directory URI.
 266       */
 267      return apply_filters( 'locale_stylesheet_uri', $stylesheet_uri, $stylesheet_dir_uri );
 268  }
 269  
 270  /**
 271   * Retrieve name of the current theme.
 272   *
 273   * @since 1.5.0
 274   * @uses apply_filters() Calls 'template' filter on template option.
 275   *
 276   * @return string Template name.
 277   */
 278  function get_template() {
 279      /**
 280       * Filter the name of the current theme.
 281       *
 282       * @since 1.5.0
 283       *
 284       * @param string $template Current theme's directory name.
 285       */
 286      return apply_filters( 'template', get_option( 'template' ) );
 287  }
 288  
 289  /**
 290   * Retrieve current theme directory.
 291   *
 292   * @since 1.5.0
 293   *
 294   * @return string Template directory path.
 295   */
 296  function get_template_directory() {
 297      $template = get_template();
 298      $theme_root = get_theme_root( $template );
 299      $template_dir = "$theme_root/$template";
 300  
 301      /**
 302       * Filter the current theme directory path.
 303       *
 304       * @since 1.5.0
 305       *
 306       * @param string $template_dir The URI of the current theme directory.
 307       * @param string $template     Directory name of the current theme.
 308       * @param string $theme_root   Absolute path to the themes directory.
 309       */
 310      return apply_filters( 'template_directory', $template_dir, $template, $theme_root );
 311  }
 312  
 313  /**
 314   * Retrieve theme directory URI.
 315   *
 316   * @since 1.5.0
 317   *
 318   * @return string Template directory URI.
 319   */
 320  function get_template_directory_uri() {
 321      $template = get_template();
 322      $theme_root_uri = get_theme_root_uri( $template );
 323      $template_dir_uri = "$theme_root_uri/$template";
 324  
 325      /**
 326       * Filter the current theme directory URI.
 327       *
 328       * @since 1.5.0
 329       *
 330       * @param string $template_dir_uri The URI of the current theme directory.
 331       * @param string $template         Directory name of the current theme.
 332       * @param string $theme_root_uri   The themes root URI.
 333       */
 334      return apply_filters( 'template_directory_uri', $template_dir_uri, $template, $theme_root_uri );
 335  }
 336  
 337  /**
 338   * Retrieve theme roots.
 339   *
 340   * @since 2.9.0
 341   *
 342   * @return array|string An array of theme roots keyed by template/stylesheet or a single theme root if all themes have the same root.
 343   */
 344  function get_theme_roots() {
 345      global $wp_theme_directories;
 346  
 347      if ( count($wp_theme_directories) <= 1 )
 348          return '/themes';
 349  
 350      $theme_roots = get_site_transient( 'theme_roots' );
 351      if ( false === $theme_roots ) {
 352          search_theme_directories( true ); // Regenerate the transient.
 353          $theme_roots = get_site_transient( 'theme_roots' );
 354      }
 355      return $theme_roots;
 356  }
 357  
 358  /**
 359   * Register a directory that contains themes.
 360   *
 361   * @since 2.9.0
 362   *
 363   * @param string $directory Either the full filesystem path to a theme folder or a folder within WP_CONTENT_DIR
 364   * @return bool
 365   */
 366  function register_theme_directory( $directory ) {
 367      global $wp_theme_directories;
 368  
 369      if ( ! file_exists( $directory ) ) {
 370          // Try prepending as the theme directory could be relative to the content directory
 371          $directory = WP_CONTENT_DIR . '/' . $directory;
 372          // If this directory does not exist, return and do not register
 373          if ( ! file_exists( $directory ) )
 374              return false;
 375      }
 376  
 377      $wp_theme_directories[] = $directory;
 378  
 379      return true;
 380  }
 381  
 382  /**
 383   * Search all registered theme directories for complete and valid themes.
 384   *
 385   * @since 2.9.0
 386   *
 387   * @param bool $force Optional. Whether to force a new directory scan. Defaults to false.
 388   * @return array Valid themes found
 389   */
 390  function search_theme_directories( $force = false ) {
 391      global $wp_theme_directories;
 392      if ( empty( $wp_theme_directories ) )
 393          return false;
 394  
 395      static $found_themes;
 396      if ( ! $force && isset( $found_themes ) )
 397          return $found_themes;
 398  
 399      $found_themes = array();
 400  
 401      $wp_theme_directories = (array) $wp_theme_directories;
 402  
 403      // Set up maybe-relative, maybe-absolute array of theme directories.
 404      // We always want to return absolute, but we need to cache relative
 405      // to use in get_theme_root().
 406      foreach ( $wp_theme_directories as $theme_root ) {
 407          if ( 0 === strpos( $theme_root, WP_CONTENT_DIR ) )
 408              $relative_theme_roots[ str_replace( WP_CONTENT_DIR, '', $theme_root ) ] = $theme_root;
 409          else
 410              $relative_theme_roots[ $theme_root ] = $theme_root;
 411      }
 412  
 413      /**
 414       * Filter whether to get the cache of the registered theme directories.
 415       *
 416       * @since 3.4.0
 417       *
 418       * @param bool   $cache_expiration Whether to get the cache of the theme directories. Default false.
 419       * @param string $cache_directory  Directory to be searched for the cache.
 420       */
 421      if ( $cache_expiration = apply_filters( 'wp_cache_themes_persistently', false, 'search_theme_directories' ) ) {
 422          $cached_roots = get_site_transient( 'theme_roots' );
 423          if ( is_array( $cached_roots ) ) {
 424              foreach ( $cached_roots as $theme_dir => $theme_root ) {
 425                  // A cached theme root is no longer around, so skip it.
 426                  if ( ! isset( $relative_theme_roots[ $theme_root ] ) )
 427                      continue;
 428                  $found_themes[ $theme_dir ] = array(
 429                      'theme_file' => $theme_dir . '/style.css',
 430                      'theme_root' => $relative_theme_roots[ $theme_root ], // Convert relative to absolute.
 431                  );
 432              }
 433              return $found_themes;
 434          }
 435          if ( ! is_int( $cache_expiration ) )
 436              $cache_expiration = 1800; // half hour
 437      } else {
 438          $cache_expiration = 1800; // half hour
 439      }
 440  
 441      /* Loop the registered theme directories and extract all themes */
 442      foreach ( $wp_theme_directories as $theme_root ) {
 443  
 444          // Start with directories in the root of the current theme directory.
 445          $dirs = @ scandir( $theme_root );
 446          if ( ! $dirs ) {
 447              trigger_error( "$theme_root is not readable", E_USER_NOTICE );
 448              continue;
 449          }
 450          foreach ( $dirs as $dir ) {
 451              if ( ! is_dir( $theme_root . '/' . $dir ) || $dir[0] == '.' || $dir == 'CVS' )
 452                  continue;
 453              if ( file_exists( $theme_root . '/' . $dir . '/style.css' ) ) {
 454                  // wp-content/themes/a-single-theme
 455                  // wp-content/themes is $theme_root, a-single-theme is $dir
 456                  $found_themes[ $dir ] = array(
 457                      'theme_file' => $dir . '/style.css',
 458                      'theme_root' => $theme_root,
 459                  );
 460              } else {
 461                  $found_theme = false;
 462                  // wp-content/themes/a-folder-of-themes/*
 463                  // wp-content/themes is $theme_root, a-folder-of-themes is $dir, then themes are $sub_dirs
 464                  $sub_dirs = @ scandir( $theme_root . '/' . $dir );
 465                  if ( ! $sub_dirs ) {
 466                      trigger_error( "$theme_root/$dir is not readable", E_USER_NOTICE );
 467                      continue;
 468                  }
 469                  foreach ( $sub_dirs as $sub_dir ) {
 470                      if ( ! is_dir( $theme_root . '/' . $dir . '/' . $sub_dir ) || $dir[0] == '.' || $dir == 'CVS' )
 471                          continue;
 472                      if ( ! file_exists( $theme_root . '/' . $dir . '/' . $sub_dir . '/style.css' ) )
 473                          continue;
 474                      $found_themes[ $dir . '/' . $sub_dir ] = array(
 475                          'theme_file' => $dir . '/' . $sub_dir . '/style.css',
 476                          'theme_root' => $theme_root,
 477                      );
 478                      $found_theme = true;
 479                  }
 480                  // Never mind the above, it's just a theme missing a style.css.
 481                  // Return it; WP_Theme will catch the error.
 482                  if ( ! $found_theme )
 483                      $found_themes[ $dir ] = array(
 484                          'theme_file' => $dir . '/style.css',
 485                          'theme_root' => $theme_root,
 486                      );
 487              }
 488          }
 489      }
 490  
 491      asort( $found_themes );
 492  
 493      $theme_roots = array();
 494      $relative_theme_roots = array_flip( $relative_theme_roots );
 495  
 496      foreach ( $found_themes as $theme_dir => $theme_data ) {
 497          $theme_roots[ $theme_dir ] = $relative_theme_roots[ $theme_data['theme_root'] ]; // Convert absolute to relative.
 498      }
 499  
 500      if ( $theme_roots != get_site_transient( 'theme_roots' ) )
 501          set_site_transient( 'theme_roots', $theme_roots, $cache_expiration );
 502  
 503      return $found_themes;
 504  }
 505  
 506  /**
 507   * Retrieve path to themes directory.
 508   *
 509   * Does not have trailing slash.
 510   *
 511   * @since 1.5.0
 512   *
 513   * @param string $stylesheet_or_template The stylesheet or template name of the theme
 514   * @return string Theme path.
 515   */
 516  function get_theme_root( $stylesheet_or_template = false ) {
 517      global $wp_theme_directories;
 518  
 519      if ( $stylesheet_or_template && $theme_root = get_raw_theme_root( $stylesheet_or_template ) ) {
 520          // Always prepend WP_CONTENT_DIR unless the root currently registered as a theme directory.
 521          // This gives relative theme roots the benefit of the doubt when things go haywire.
 522          if ( ! in_array( $theme_root, (array) $wp_theme_directories ) )
 523              $theme_root = WP_CONTENT_DIR . $theme_root;
 524      } else {
 525          $theme_root = WP_CONTENT_DIR . '/themes';
 526      }
 527  
 528      /**
 529       * Filter the absolute path to the themes directory.
 530       *
 531       * @since 1.5.0
 532       *
 533       * @param string $theme_root Absolute path to themes directory.
 534       */
 535      return apply_filters( 'theme_root', $theme_root );
 536  }
 537  
 538  /**
 539   * Retrieve URI for themes directory.
 540   *
 541   * Does not have trailing slash.
 542   *
 543   * @since 1.5.0
 544   *
 545   * @param string $stylesheet_or_template Optional. The stylesheet or template name of the theme.
 546   *     Default is to leverage the main theme root.
 547   * @param string $theme_root Optional. The theme root for which calculations will be based, preventing
 548   *     the need for a get_raw_theme_root() call.
 549   * @return string Themes URI.
 550   */
 551  function get_theme_root_uri( $stylesheet_or_template = false, $theme_root = false ) {
 552      global $wp_theme_directories;
 553  
 554      if ( $stylesheet_or_template && ! $theme_root )
 555          $theme_root = get_raw_theme_root( $stylesheet_or_template );
 556  
 557      if ( $stylesheet_or_template && $theme_root ) {
 558          if ( in_array( $theme_root, (array) $wp_theme_directories ) ) {
 559              // Absolute path. Make an educated guess. YMMV -- but note the filter below.
 560              if ( 0 === strpos( $theme_root, WP_CONTENT_DIR ) )
 561                  $theme_root_uri = content_url( str_replace( WP_CONTENT_DIR, '', $theme_root ) );
 562              elseif ( 0 === strpos( $theme_root, ABSPATH ) )
 563                  $theme_root_uri = site_url( str_replace( ABSPATH, '', $theme_root ) );
 564              elseif ( 0 === strpos( $theme_root, WP_PLUGIN_DIR ) || 0 === strpos( $theme_root, WPMU_PLUGIN_DIR ) )
 565                  $theme_root_uri = plugins_url( basename( $theme_root ), $theme_root );
 566              else
 567                  $theme_root_uri = $theme_root;
 568          } else {
 569              $theme_root_uri = content_url( $theme_root );
 570          }
 571      } else {
 572          $theme_root_uri = content_url( 'themes' );
 573      }
 574  
 575      /**
 576       * Filter the URI for themes directory.
 577       *
 578       * @since 1.5.0
 579       *
 580       * @param string $theme_root_uri         The URI for themes directory.
 581       * @param string $siteurl                WordPress web address which is set in General Options.
 582       * @param string $stylesheet_or_template Stylesheet or template name of the theme.
 583       */
 584      return apply_filters( 'theme_root_uri', $theme_root_uri, get_option( 'siteurl' ), $stylesheet_or_template );
 585  }
 586  
 587  /**
 588   * Get the raw theme root relative to the content directory with no filters applied.
 589   *
 590   * @since 3.1.0
 591   *
 592   * @param string $stylesheet_or_template The stylesheet or template name of the theme
 593   * @param bool $skip_cache Optional. Whether to skip the cache. Defaults to false, meaning the cache is used.
 594   * @return string Theme root
 595   */
 596  function get_raw_theme_root( $stylesheet_or_template, $skip_cache = false ) {
 597      global $wp_theme_directories;
 598  
 599      if ( count($wp_theme_directories) <= 1 )
 600          return '/themes';
 601  
 602      $theme_root = false;
 603  
 604      // If requesting the root for the current theme, consult options to avoid calling get_theme_roots()
 605      if ( ! $skip_cache ) {
 606          if ( get_option('stylesheet') == $stylesheet_or_template )
 607              $theme_root = get_option('stylesheet_root');
 608          elseif ( get_option('template') == $stylesheet_or_template )
 609              $theme_root = get_option('template_root');
 610      }
 611  
 612      if ( empty($theme_root) ) {
 613          $theme_roots = get_theme_roots();
 614          if ( !empty($theme_roots[$stylesheet_or_template]) )
 615              $theme_root = $theme_roots[$stylesheet_or_template];
 616      }
 617  
 618      return $theme_root;
 619  }
 620  
 621  /**
 622   * Display localized stylesheet link element.
 623   *
 624   * @since 2.1.0
 625   */
 626  function locale_stylesheet() {
 627      $stylesheet = get_locale_stylesheet_uri();
 628      if ( empty($stylesheet) )
 629          return;
 630      echo '<link rel="stylesheet" href="' . $stylesheet . '" type="text/css" media="screen" />';
 631  }
 632  
 633  /**
 634   * Start preview theme output buffer.
 635   *
 636   * Will only perform task if the user has permissions and template and preview
 637   * query variables exist.
 638   *
 639   * @since 2.6.0
 640   */
 641  function preview_theme() {
 642      if ( ! (isset($_GET['template']) && isset($_GET['preview'])) )
 643          return;
 644  
 645      if ( !current_user_can( 'switch_themes' ) )
 646          return;
 647  
 648      // Admin Thickbox requests
 649      if ( isset( $_GET['preview_iframe'] ) )
 650          show_admin_bar( false );
 651  
 652      $_GET['template'] = preg_replace('|[^a-z0-9_./-]|i', '', $_GET['template']);
 653  
 654      if ( validate_file($_GET['template']) )
 655          return;
 656  
 657      add_filter( 'template', '_preview_theme_template_filter' );
 658  
 659      if ( isset($_GET['stylesheet']) ) {
 660          $_GET['stylesheet'] = preg_replace('|[^a-z0-9_./-]|i', '', $_GET['stylesheet']);
 661          if ( validate_file($_GET['stylesheet']) )
 662              return;
 663          add_filter( 'stylesheet', '_preview_theme_stylesheet_filter' );
 664      }
 665  
 666      // Prevent theme mods to current theme being used on theme being previewed
 667      add_filter( 'pre_option_theme_mods_' . get_option( 'stylesheet' ), '__return_empty_array' );
 668  
 669      ob_start( 'preview_theme_ob_filter' );
 670  }
 671  add_action('setup_theme', 'preview_theme');
 672  
 673  /**
 674   * Private function to modify the current template when previewing a theme
 675   *
 676   * @since 2.9.0
 677   * @access private
 678   *
 679   * @return string
 680   */
 681  function _preview_theme_template_filter() {
 682      return isset($_GET['template']) ? $_GET['template'] : '';
 683  }
 684  
 685  /**
 686   * Private function to modify the current stylesheet when previewing a theme
 687   *
 688   * @since 2.9.0
 689   * @access private
 690   *
 691   * @return string
 692   */
 693  function _preview_theme_stylesheet_filter() {
 694      return isset($_GET['stylesheet']) ? $_GET['stylesheet'] : '';
 695  }
 696  
 697  /**
 698   * Callback function for ob_start() to capture all links in the theme.
 699   *
 700   * @since 2.6.0
 701   * @access private
 702   *
 703   * @param string $content
 704   * @return string
 705   */
 706  function preview_theme_ob_filter( $content ) {
 707      return preg_replace_callback( "|(<a.*?href=([\"']))(.*?)([\"'].*?>)|", 'preview_theme_ob_filter_callback', $content );
 708  }
 709  
 710  /**
 711   * Manipulates preview theme links in order to control and maintain location.
 712   *
 713   * Callback function for preg_replace_callback() to accept and filter matches.
 714   *
 715   * @since 2.6.0
 716   * @access private
 717   *
 718   * @param array $matches
 719   * @return string
 720   */
 721  function preview_theme_ob_filter_callback( $matches ) {
 722      if ( strpos($matches[4], 'onclick') !== false )
 723          $matches[4] = preg_replace('#onclick=([\'"]).*?(?<!\\\)\\1#i', '', $matches[4]); //Strip out any onclicks from rest of <a>. (?<!\\\) means to ignore the '" if it's escaped by \  to prevent breaking mid-attribute.
 724      if (
 725          ( false !== strpos($matches[3], '/wp-admin/') )
 726      ||
 727          ( false !== strpos( $matches[3], '://' ) && 0 !== strpos( $matches[3], home_url() ) )
 728      ||
 729          ( false !== strpos($matches[3], '/feed/') )
 730      ||
 731          ( false !== strpos($matches[3], '/trackback/') )
 732      )
 733          return $matches[1] . "#$matches[2] onclick=$matches[2]return false;" . $matches[4];
 734  
 735      $stylesheet = isset( $_GET['stylesheet'] ) ? $_GET['stylesheet'] : '';
 736      $template   = isset( $_GET['template'] )   ? $_GET['template']   : '';
 737  
 738      $link = add_query_arg( array( 'preview' => 1, 'template' => $template, 'stylesheet' => $stylesheet, 'preview_iframe' => 1 ), $matches[3] );
 739      if ( 0 === strpos($link, 'preview=1') )
 740          $link = "?$link";
 741      return $matches[1] . esc_attr( $link ) . $matches[4];
 742  }
 743  
 744  /**
 745   * Switches the theme.
 746   *
 747   * Accepts one argument: $stylesheet of the theme. It also accepts an additional function signature
 748   * of two arguments: $template then $stylesheet. This is for backwards compatibility.
 749   *
 750   * @since 2.5.0
 751   *
 752   * @param string $stylesheet Stylesheet name
 753   */
 754  function switch_theme( $stylesheet ) {
 755      global $wp_theme_directories, $sidebars_widgets;
 756  
 757      if ( is_array( $sidebars_widgets ) )
 758          set_theme_mod( 'sidebars_widgets', array( 'time' => time(), 'data' => $sidebars_widgets ) );
 759  
 760      $old_theme  = wp_get_theme();
 761      $new_theme = wp_get_theme( $stylesheet );
 762  
 763      if ( func_num_args() > 1 ) {
 764          $template = $stylesheet;
 765          $stylesheet = func_get_arg( 1 );
 766      } else {
 767          $template = $new_theme->get_template();
 768      }
 769  
 770      update_option( 'template', $template );
 771      update_option( 'stylesheet', $stylesheet );
 772  
 773      if ( count( $wp_theme_directories ) > 1 ) {
 774          update_option( 'template_root', get_raw_theme_root( $template, true ) );
 775          update_option( 'stylesheet_root', get_raw_theme_root( $stylesheet, true ) );
 776      } else {
 777          delete_option( 'template_root' );
 778          delete_option( 'stylesheet_root' );
 779      }
 780  
 781      $new_name  = $new_theme->get('Name');
 782  
 783      update_option( 'current_theme', $new_name );
 784  
 785      if ( is_admin() && false === get_option( 'theme_mods_' . $stylesheet ) ) {
 786          $default_theme_mods = (array) get_option( 'mods_' . $new_name );
 787          add_option( "theme_mods_$stylesheet", $default_theme_mods );
 788      }
 789  
 790      update_option( 'theme_switched', $old_theme->get_stylesheet() );
 791      /**
 792       * Fires after the theme is switched.
 793       *
 794       * @since 1.5.0
 795       *
 796       * @param string   $new_name  Name of the new theme.
 797       * @param WP_Theme $new_theme WP_Theme instance of the new theme.
 798       */
 799      do_action( 'switch_theme', $new_name, $new_theme );
 800  }
 801  
 802  /**
 803   * Checks that current theme files 'index.php' and 'style.css' exists.
 804   *
 805   * Does not check the default theme, which is the fallback and should always exist.
 806   * Will switch theme to the fallback theme if current theme does not validate.
 807   * You can use the 'validate_current_theme' filter to return false to
 808   * disable this functionality.
 809   *
 810   * @since 1.5.0
 811   * @see WP_DEFAULT_THEME
 812   *
 813   * @return bool
 814   */
 815  function validate_current_theme() {
 816      /**
 817       * Filter whether to validate the current theme.
 818       *
 819       * @since 2.7.0
 820       *
 821       * @param bool true Validation flag to check the current theme.
 822       */
 823      if ( defined('WP_INSTALLING') || ! apply_filters( 'validate_current_theme', true ) )
 824          return true;
 825  
 826      if ( get_template() != WP_DEFAULT_THEME && !file_exists(get_template_directory() . '/index.php') ) {
 827          switch_theme( WP_DEFAULT_THEME );
 828          return false;
 829      }
 830  
 831      if ( get_stylesheet() != WP_DEFAULT_THEME && !file_exists(get_template_directory() . '/style.css') ) {
 832          switch_theme( WP_DEFAULT_THEME );
 833          return false;
 834      }
 835  
 836      if ( is_child_theme() && ! file_exists( get_stylesheet_directory() . '/style.css' ) ) {
 837          switch_theme( WP_DEFAULT_THEME );
 838          return false;
 839      }
 840  
 841      return true;
 842  }
 843  
 844  /**
 845   * Retrieve all theme modifications.
 846   *
 847   * @since 3.1.0
 848   *
 849   * @return array Theme modifications.
 850   */
 851  function get_theme_mods() {
 852      $theme_slug = get_option( 'stylesheet' );
 853      if ( false === ( $mods = get_option( "theme_mods_$theme_slug" ) ) ) {
 854          $theme_name = get_option( 'current_theme' );
 855          if ( false === $theme_name )
 856              $theme_name = wp_get_theme()->get('Name');
 857          $mods = get_option( "mods_$theme_name" ); // Deprecated location.
 858          if ( is_admin() && false !== $mods ) {
 859              update_option( "theme_mods_$theme_slug", $mods );
 860              delete_option( "mods_$theme_name" );
 861          }
 862      }
 863      return $mods;
 864  }
 865  
 866  /**
 867   * Retrieve theme modification value for the current theme.
 868   *
 869   * If the modification name does not exist, then the $default will be passed
 870   * through {@link http://php.net/sprintf sprintf()} PHP function with the first
 871   * string the template directory URI and the second string the stylesheet
 872   * directory URI.
 873   *
 874   * @since 2.1.0
 875   *
 876   * @param string $name Theme modification name.
 877   * @param bool|string $default
 878   * @return string
 879   */
 880  function get_theme_mod( $name, $default = false ) {
 881      $mods = get_theme_mods();
 882  
 883      if ( isset( $mods[$name] ) ) {
 884          /**
 885           * Filter the theme modification, or 'theme_mod', value.
 886           *
 887           * The dynamic portion of the hook name, $name, refers to
 888           * the key name of the modification array. For example,
 889           * 'header_textcolor', 'header_image', and so on depending
 890           * on the theme options.
 891           *
 892           * @since 2.2.0
 893           *
 894           * @param string $current_mod The value of the current theme modification.
 895           */
 896          return apply_filters( "theme_mod_{$name}", $mods[$name] );
 897      }
 898  
 899      if ( is_string( $default ) )
 900          $default = sprintf( $default, get_template_directory_uri(), get_stylesheet_directory_uri() );
 901  
 902      /** This filter is documented in wp-includes/theme.php */
 903      return apply_filters( "theme_mod_{$name}", $default );
 904  }
 905  
 906  /**
 907   * Update theme modification value for the current theme.
 908   *
 909   * @since 2.1.0
 910   *
 911   * @param string $name Theme modification name.
 912   * @param string $value theme modification value.
 913   */
 914  function set_theme_mod( $name, $value ) {
 915      $mods = get_theme_mods();
 916  
 917      $mods[ $name ] = $value;
 918  
 919      $theme = get_option( 'stylesheet' );
 920      update_option( "theme_mods_$theme", $mods );
 921  }
 922  
 923  /**
 924   * Remove theme modification name from current theme list.
 925   *
 926   * If removing the name also removes all elements, then the entire option will
 927   * be removed.
 928   *
 929   * @since 2.1.0
 930   *
 931   * @param string $name Theme modification name.
 932   * @return null
 933   */
 934  function remove_theme_mod( $name ) {
 935      $mods = get_theme_mods();
 936  
 937      if ( ! isset( $mods[ $name ] ) )
 938          return;
 939  
 940      unset( $mods[ $name ] );
 941  
 942      if ( empty( $mods ) )
 943          return remove_theme_mods();
 944  
 945      $theme = get_option( 'stylesheet' );
 946      update_option( "theme_mods_$theme", $mods );
 947  }
 948  
 949  /**
 950   * Remove theme modifications option for current theme.
 951   *
 952   * @since 2.1.0
 953   */
 954  function remove_theme_mods() {
 955      delete_option( 'theme_mods_' . get_option( 'stylesheet' ) );
 956  
 957      // Old style.
 958      $theme_name = get_option( 'current_theme' );
 959      if ( false === $theme_name )
 960          $theme_name = wp_get_theme()->get('Name');
 961      delete_option( 'mods_' . $theme_name );
 962  }
 963  
 964  /**
 965   * Retrieve text color for custom header.
 966   *
 967   * @since 2.1.0
 968   *
 969   * @return string
 970   */
 971  function get_header_textcolor() {
 972      return get_theme_mod('header_textcolor', get_theme_support( 'custom-header', 'default-text-color' ) );
 973  }
 974  
 975  /**
 976   * Display text color for custom header.
 977   *
 978   * @since 2.1.0
 979   */
 980  function header_textcolor() {
 981      echo get_header_textcolor();
 982  }
 983  
 984  /**
 985   * Whether to display the header text.
 986   *
 987   * @since 3.4.0
 988   *
 989   * @return bool
 990   */
 991  function display_header_text() {
 992      if ( ! current_theme_supports( 'custom-header', 'header-text' ) )
 993          return false;
 994  
 995      $text_color = get_theme_mod( 'header_textcolor', get_theme_support( 'custom-header', 'default-text-color' ) );
 996      return 'blank' != $text_color;
 997  }
 998  
 999  /**
1000   * Retrieve header image for custom header.
1001   *
1002   * @since 2.1.0
1003   *
1004   * @return string
1005   */
1006  function get_header_image() {
1007      $url = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) );
1008  
1009      if ( 'remove-header' == $url )
1010          return false;
1011  
1012      if ( is_random_header_image() )
1013          $url = get_random_header_image();
1014  
1015      return esc_url_raw( set_url_scheme( $url ) );
1016  }
1017  
1018  /**
1019   * Get random header image data from registered images in theme.
1020   *
1021   * @since 3.4.0
1022   *
1023   * @access private
1024   *
1025   * @return string Path to header image
1026   */
1027  
1028  function _get_random_header_data() {
1029      static $_wp_random_header;
1030  
1031      if ( empty( $_wp_random_header ) ) {
1032          global $_wp_default_headers;
1033          $header_image_mod = get_theme_mod( 'header_image', '' );
1034          $headers = array();
1035  
1036          if ( 'random-uploaded-image' == $header_image_mod )
1037              $headers = get_uploaded_header_images();
1038          elseif ( ! empty( $_wp_default_headers ) ) {
1039              if ( 'random-default-image' == $header_image_mod ) {
1040                  $headers = $_wp_default_headers;
1041              } else {
1042                  if ( current_theme_supports( 'custom-header', 'random-default' ) )
1043                      $headers = $_wp_default_headers;
1044              }
1045          }
1046  
1047          if ( empty( $headers ) )
1048              return new stdClass;
1049  
1050          $_wp_random_header = (object) $headers[ array_rand( $headers ) ];
1051  
1052          $_wp_random_header->url =  sprintf( $_wp_random_header->url, get_template_directory_uri(), get_stylesheet_directory_uri() );
1053          $_wp_random_header->thumbnail_url =  sprintf( $_wp_random_header->thumbnail_url, get_template_directory_uri(), get_stylesheet_directory_uri() );
1054      }
1055      return $_wp_random_header;
1056  }
1057  
1058  /**
1059   * Get random header image url from registered images in theme.
1060   *
1061   * @since 3.2.0
1062   *
1063   * @return string Path to header image
1064   */
1065  
1066  function get_random_header_image() {
1067      $random_image = _get_random_header_data();
1068      if ( empty( $random_image->url ) )
1069          return '';
1070      return $random_image->url;
1071  }
1072  
1073  /**
1074   * Check if random header image is in use.
1075   *
1076   * Always true if user expressly chooses the option in Appearance > Header.
1077   * Also true if theme has multiple header images registered, no specific header image
1078   * is chosen, and theme turns on random headers with add_theme_support().
1079   *
1080   * @since 3.2.0
1081   *
1082   * @param string $type The random pool to use. any|default|uploaded
1083   * @return boolean
1084   */
1085  function is_random_header_image( $type = 'any' ) {
1086      $header_image_mod = get_theme_mod( 'header_image', get_theme_support( 'custom-header', 'default-image' ) );
1087  
1088      if ( 'any' == $type ) {
1089          if ( 'random-default-image' == $header_image_mod || 'random-uploaded-image' == $header_image_mod || ( '' != get_random_header_image() && empty( $header_image_mod ) ) )
1090              return true;
1091      } else {
1092          if ( "random-$type-image" == $header_image_mod )
1093              return true;
1094          elseif ( 'default' == $type && empty( $header_image_mod ) && '' != get_random_header_image() )
1095              return true;
1096      }
1097  
1098      return false;
1099  }
1100  
1101  /**
1102   * Display header image URL.
1103   *
1104   * @since 2.1.0
1105   */
1106  function header_image() {
1107      echo esc_url( get_header_image() );
1108  }
1109  
1110  /**
1111   * Get the header images uploaded for the current theme.
1112   *
1113   * @since 3.2.0
1114   *
1115   * @return array
1116   */
1117  function get_uploaded_header_images() {
1118      $header_images = array();
1119  
1120      // @todo caching
1121      $headers = get_posts( array( 'post_type' => 'attachment', 'meta_key' => '_wp_attachment_is_custom_header', 'meta_value' => get_option('stylesheet'), 'orderby' => 'none', 'nopaging' => true ) );
1122  
1123      if ( empty( $headers ) )
1124          return array();
1125  
1126      foreach ( (array) $headers as $header ) {
1127          $url = esc_url_raw( $header->guid );
1128          $header_data = wp_get_attachment_metadata( $header->ID );
1129          $header_index = basename($url);
1130          $header_images[$header_index] = array();
1131          $header_images[$header_index]['attachment_id'] =  $header->ID;
1132          $header_images[$header_index]['url'] =  $url;
1133          $header_images[$header_index]['thumbnail_url'] =  $url;
1134          if ( isset( $header_data['width'] ) )
1135              $header_images[$header_index]['width'] = $header_data['width'];
1136          if ( isset( $header_data['height'] ) )
1137              $header_images[$header_index]['height'] = $header_data['height'];
1138      }
1139  
1140      return $header_images;
1141  }
1142  
1143  /**
1144   * Get the header image data.
1145   *
1146   * @since 3.4.0
1147   *
1148   * @return object
1149   */
1150  function get_custom_header() {
1151      global $_wp_default_headers;
1152  
1153      if ( is_random_header_image() ) {
1154          $data = _get_random_header_data();
1155      } else {
1156          $data = get_theme_mod( 'header_image_data' );
1157          if ( ! $data && current_theme_supports( 'custom-header', 'default-image' ) ) {
1158              $directory_args = array( get_template_directory_uri(), get_stylesheet_directory_uri() );
1159              $data = array();
1160              $data['url'] = $data['thumbnail_url'] = vsprintf( get_theme_support( 'custom-header', 'default-image' ), $directory_args );
1161              if ( ! empty( $_wp_default_headers ) ) {
1162                  foreach ( (array) $_wp_default_headers as $default_header ) {
1163                      $url = vsprintf( $default_header['url'], $directory_args );
1164                      if ( $data['url'] == $url ) {
1165                          $data = $default_header;
1166                          $data['url'] = $url;
1167                          $data['thumbnail_url'] = vsprintf( $data['thumbnail_url'], $directory_args );
1168                          break;
1169                      }
1170                  }
1171              }
1172          }
1173      }
1174  
1175      $default = array(
1176          'url'           => '',
1177          'thumbnail_url' => '',
1178          'width'         => get_theme_support( 'custom-header', 'width' ),
1179          'height'        => get_theme_support( 'custom-header', 'height' ),
1180      );
1181      return (object) wp_parse_args( $data, $default );
1182  }
1183  
1184  /**
1185   * Register a selection of default headers to be displayed by the custom header admin UI.
1186   *
1187   * @since 3.0.0
1188   *
1189   * @param array $headers Array of headers keyed by a string id. The ids point to arrays containing 'url', 'thumbnail_url', and 'description' keys.
1190   */
1191  function register_default_headers( $headers ) {
1192      global $_wp_default_headers;
1193  
1194      $_wp_default_headers = array_merge( (array) $_wp_default_headers, (array) $headers );
1195  }
1196  
1197  /**
1198   * Unregister default headers.
1199   *
1200   * This function must be called after register_default_headers() has already added the
1201   * header you want to remove.
1202   *
1203   * @see register_default_headers()
1204   * @since 3.0.0
1205   *
1206   * @param string|array $header The header string id (key of array) to remove, or an array thereof.
1207   * @return True on success, false on failure.
1208   */
1209  function unregister_default_headers( $header ) {
1210      global $_wp_default_headers;
1211      if ( is_array( $header ) ) {
1212          array_map( 'unregister_default_headers', $header );
1213      } elseif ( isset( $_wp_default_headers[ $header ] ) ) {
1214          unset( $_wp_default_headers[ $header ] );
1215          return true;
1216      } else {
1217          return false;
1218      }
1219  }
1220  
1221  /**
1222   * Retrieve background image for custom background.
1223   *
1224   * @since 3.0.0
1225   *
1226   * @return string
1227   */
1228  function get_background_image() {
1229      return get_theme_mod('background_image', get_theme_support( 'custom-background', 'default-image' ) );
1230  }
1231  
1232  /**
1233   * Display background image path.
1234   *
1235   * @since 3.0.0
1236   */
1237  function background_image() {
1238      echo get_background_image();
1239  }
1240  
1241  /**
1242   * Retrieve value for custom background color.
1243   *
1244   * @since 3.0.0
1245   *
1246   * @return string
1247   */
1248  function get_background_color() {
1249      return get_theme_mod('background_color', get_theme_support( 'custom-background', 'default-color' ) );
1250  }
1251  
1252  /**
1253   * Display background color value.
1254   *
1255   * @since 3.0.0
1256   */
1257  function background_color() {
1258      echo get_background_color();
1259  }
1260  
1261  /**
1262   * Default custom background callback.
1263   *
1264   * @since 3.0.0
1265   * @access protected
1266   */
1267  function _custom_background_cb() {
1268      // $background is the saved custom image, or the default image.
1269      $background = set_url_scheme( get_background_image() );
1270  
1271      // $color is the saved custom color.
1272      // A default has to be specified in style.css. It will not be printed here.
1273      $color = get_theme_mod( 'background_color' );
1274  
1275      if ( ! $background && ! $color )
1276          return;
1277  
1278      $style = $color ? "background-color: #$color;" : '';
1279  
1280      if ( $background ) {
1281          $image = " background-image: url('$background');";
1282  
1283          $repeat = get_theme_mod( 'background_repeat', get_theme_support( 'custom-background', 'default-repeat' ) );
1284          if ( ! in_array( $repeat, array( 'no-repeat', 'repeat-x', 'repeat-y', 'repeat' ) ) )
1285              $repeat = 'repeat';
1286          $repeat = " background-repeat: $repeat;";
1287  
1288          $position = get_theme_mod( 'background_position_x', get_theme_support( 'custom-background', 'default-position-x' ) );
1289          if ( ! in_array( $position, array( 'center', 'right', 'left' ) ) )
1290              $position = 'left';
1291          $position = " background-position: top $position;";
1292  
1293          $attachment = get_theme_mod( 'background_attachment', get_theme_support( 'custom-background', 'default-attachment' ) );
1294          if ( ! in_array( $attachment, array( 'fixed', 'scroll' ) ) )
1295              $attachment = 'scroll';
1296          $attachment = " background-attachment: $attachment;";
1297  
1298          $style .= $image . $repeat . $position . $attachment;
1299      }
1300  ?>
1301  <style type="text/css" id="custom-background-css">
1302  body.custom-background { <?php echo trim( $style ); ?> }
1303  </style>
1304  <?php
1305  }
1306  
1307  /**
1308   * Add callback for custom TinyMCE editor stylesheets.
1309   *
1310   * The parameter $stylesheet is the name of the stylesheet, relative to
1311   * the theme root. It also accepts an array of stylesheets.
1312   * It is optional and defaults to 'editor-style.css'.
1313   *
1314   * This function automatically adds another stylesheet with -rtl prefix, e.g. editor-style-rtl.css.
1315   * If that file doesn't exist, it is removed before adding the stylesheet(s) to TinyMCE.
1316   * If an array of stylesheets is passed to add_editor_style(),
1317   * RTL is only added for the first stylesheet.
1318   *
1319   * Since version 3.4 the TinyMCE body has .rtl CSS class.
1320   * It is a better option to use that class and add any RTL styles to the main stylesheet.
1321   *
1322   * @since 3.0.0
1323   *
1324   * @param mixed $stylesheet Optional. Stylesheet name or array thereof, relative to theme root.
1325   *     Defaults to 'editor-style.css'
1326   */
1327  function add_editor_style( $stylesheet = 'editor-style.css' ) {
1328  
1329      add_theme_support( 'editor-style' );
1330  
1331      if ( ! is_admin() )
1332          return;
1333  
1334      global $editor_styles;
1335      $editor_styles = (array) $editor_styles;
1336      $stylesheet    = (array) $stylesheet;
1337      if ( is_rtl() ) {
1338          $rtl_stylesheet = str_replace('.css', '-rtl.css', $stylesheet[0]);
1339          $stylesheet[] = $rtl_stylesheet;
1340      }
1341  
1342      $editor_styles = array_merge( $editor_styles, $stylesheet );
1343  }
1344  
1345  /**
1346   * Removes all visual editor stylesheets.
1347   *
1348   * @since 3.1.0
1349   *
1350   * @return bool True on success, false if there were no stylesheets to remove.
1351   */
1352  function remove_editor_styles() {
1353      if ( ! current_theme_supports( 'editor-style' ) )
1354          return false;
1355      _remove_theme_support( 'editor-style' );
1356      if ( is_admin() )
1357          $GLOBALS['editor_styles'] = array();
1358      return true;
1359  }
1360  
1361  /**
1362   * Allows a theme to register its support of a certain feature
1363   *
1364   * Must be called in the theme's functions.php file to work.
1365   * If attached to a hook, it must be after_setup_theme.
1366   * The init hook may be too late for some features.
1367   *
1368   * @since 2.9.0
1369   * @param string $feature the feature being added
1370   */
1371  function add_theme_support( $feature ) {
1372      global $_wp_theme_features;
1373  
1374      if ( func_num_args() == 1 )
1375          $args = true;
1376      else
1377          $args = array_slice( func_get_args(), 1 );
1378  
1379      switch ( $feature ) {
1380          case 'post-formats' :
1381              if ( is_array( $args[0] ) )
1382                  $args[0] = array_intersect( $args[0], array_keys( get_post_format_slugs() ) );
1383              break;
1384  
1385          case 'html5' :
1386              // You can't just pass 'html5', you need to pass an array of types.
1387              if ( empty( $args[0] ) ) {
1388                  $args = array( 0 => array( 'comment-list', 'comment-form', 'search-form' ) );
1389              } elseif ( ! is_array( $args[0] ) ) {
1390                  _doing_it_wrong( "add_theme_support( 'html5' )", 'You need to pass an array of types.', '3.6.1' );
1391                  return false;
1392              }
1393  
1394              // Calling 'html5' again merges, rather than overwrites.
1395              if ( isset( $_wp_theme_features['html5'] ) )
1396                  $args[0] = array_merge( $_wp_theme_features['html5'][0], $args[0] );
1397              break;
1398  
1399          case 'custom-header-uploads' :
1400              return add_theme_support( 'custom-header', array( 'uploads' => true ) );
1401              break;
1402  
1403          case 'custom-header' :
1404              if ( ! is_array( $args ) )
1405                  $args = array( 0 => array() );
1406  
1407              $defaults = array(
1408                  'default-image' => '',
1409                  'random-default' => false,
1410                  'width' => 0,
1411                  'height' => 0,
1412                  'flex-height' => false,
1413                  'flex-width' => false,
1414                  'default-text-color' => '',
1415                  'header-text' => true,
1416                  'uploads' => true,
1417                  'wp-head-callback' => '',
1418                  'admin-head-callback' => '',
1419                  'admin-preview-callback' => '',
1420              );
1421  
1422              $jit = isset( $args[0]['__jit'] );
1423              unset( $args[0]['__jit'] );
1424  
1425              // Merge in data from previous add_theme_support() calls.
1426              // The first value registered wins. (A child theme is set up first.)
1427              if ( isset( $_wp_theme_features['custom-header'] ) )
1428                  $args[0] = wp_parse_args( $_wp_theme_features['custom-header'][0], $args[0] );
1429  
1430              // Load in the defaults at the end, as we need to insure first one wins.
1431              // This will cause all constants to be defined, as each arg will then be set to the default.
1432              if ( $jit )
1433                  $args[0] = wp_parse_args( $args[0], $defaults );
1434  
1435              // If a constant was defined, use that value. Otherwise, define the constant to ensure
1436              // the constant is always accurate (and is not defined later,  overriding our value).
1437              // As stated above, the first value wins.
1438              // Once we get to wp_loaded (just-in-time), define any constants we haven't already.
1439              // Constants are lame. Don't reference them. This is just for backwards compatibility.
1440  
1441              if ( defined( 'NO_HEADER_TEXT' ) )
1442                  $args[0]['header-text'] = ! NO_HEADER_TEXT;
1443              elseif ( isset( $args[0]['header-text'] ) )
1444                  define( 'NO_HEADER_TEXT', empty( $args[0]['header-text'] ) );
1445  
1446              if ( defined( 'HEADER_IMAGE_WIDTH' ) )
1447                  $args[0]['width'] = (int) HEADER_IMAGE_WIDTH;
1448              elseif ( isset( $args[0]['width'] ) )
1449                  define( 'HEADER_IMAGE_WIDTH', (int) $args[0]['width'] );
1450  
1451              if ( defined( 'HEADER_IMAGE_HEIGHT' ) )
1452                  $args[0]['height'] = (int) HEADER_IMAGE_HEIGHT;
1453              elseif ( isset( $args[0]['height'] ) )
1454                  define( 'HEADER_IMAGE_HEIGHT', (int) $args[0]['height'] );
1455  
1456              if ( defined( 'HEADER_TEXTCOLOR' ) )
1457                  $args[0]['default-text-color'] = HEADER_TEXTCOLOR;
1458              elseif ( isset( $args[0]['default-text-color'] ) )
1459                  define( 'HEADER_TEXTCOLOR', $args[0]['default-text-color'] );
1460  
1461              if ( defined( 'HEADER_IMAGE' ) )
1462                  $args[0]['default-image'] = HEADER_IMAGE;
1463              elseif ( isset( $args[0]['default-image'] ) )
1464                  define( 'HEADER_IMAGE', $args[0]['default-image'] );
1465  
1466              if ( $jit && ! empty( $args[0]['default-image'] ) )
1467                  $args[0]['random-default'] = false;
1468  
1469              // If headers are supported, and we still don't have a defined width or height,
1470              // we have implicit flex sizes.
1471              if ( $jit ) {
1472                  if ( empty( $args[0]['width'] ) && empty( $args[0]['flex-width'] ) )
1473                      $args[0]['flex-width'] = true;
1474                  if ( empty( $args[0]['height'] ) && empty( $args[0]['flex-height'] ) )
1475                      $args[0]['flex-height'] = true;
1476              }
1477  
1478              break;
1479  
1480          case 'custom-background' :
1481              if ( ! is_array( $args ) )
1482                  $args = array( 0 => array() );
1483  
1484              $defaults = array(
1485                  'default-image'          => '',
1486                  'default-repeat'         => 'repeat',
1487                  'default-position-x'     => 'left',
1488                  'default-attachment'     => 'scroll',
1489                  'default-color'          => '',
1490                  'wp-head-callback'       => '_custom_background_cb',
1491                  'admin-head-callback'    => '',
1492                  'admin-preview-callback' => '',
1493              );
1494  
1495              $jit = isset( $args[0]['__jit'] );
1496              unset( $args[0]['__jit'] );
1497  
1498              // Merge in data from previous add_theme_support() calls. The first value registered wins.
1499              if ( isset( $_wp_theme_features['custom-background'] ) )
1500                  $args[0] = wp_parse_args( $_wp_theme_features['custom-background'][0], $args[0] );
1501  
1502              if ( $jit )
1503                  $args[0] = wp_parse_args( $args[0], $defaults );
1504  
1505              if ( defined( 'BACKGROUND_COLOR' ) )
1506                  $args[0]['default-color'] = BACKGROUND_COLOR;
1507              elseif ( isset( $args[0]['default-color'] ) || $jit )
1508                  define( 'BACKGROUND_COLOR', $args[0]['default-color'] );
1509  
1510              if ( defined( 'BACKGROUND_IMAGE' ) )
1511                  $args[0]['default-image'] = BACKGROUND_IMAGE;
1512              elseif ( isset( $args[0]['default-image'] ) || $jit )
1513                  define( 'BACKGROUND_IMAGE', $args[0]['default-image'] );
1514  
1515              break;
1516      }
1517  
1518      $_wp_theme_features[ $feature ] = $args;
1519  }
1520  
1521  /**
1522   * Registers the internal custom header and background routines.
1523   *
1524   * @since 3.4.0
1525   * @access private
1526   */
1527  function _custom_header_background_just_in_time() {
1528      global $custom_image_header, $custom_background;
1529  
1530      if ( current_theme_supports( 'custom-header' ) ) {
1531          // In case any constants were defined after an add_custom_image_header() call, re-run.
1532          add_theme_support( 'custom-header', array( '__jit' => true ) );
1533  
1534          $args = get_theme_support( 'custom-header' );
1535          if ( $args[0]['wp-head-callback'] )
1536              add_action( 'wp_head', $args[0]['wp-head-callback'] );
1537  
1538          if ( is_admin() ) {
1539              require_once ( ABSPATH . 'wp-admin/custom-header.php' );
1540              $custom_image_header = new Custom_Image_Header( $args[0]['admin-head-callback'], $args[0]['admin-preview-callback'] );
1541          }
1542      }
1543  
1544      if ( current_theme_supports( 'custom-background' ) ) {
1545          // In case any constants were defined after an add_custom_background() call, re-run.
1546          add_theme_support( 'custom-background', array( '__jit' => true ) );
1547  
1548          $args = get_theme_support( 'custom-background' );
1549          add_action( 'wp_head', $args[0]['wp-head-callback'] );
1550  
1551          if ( is_admin() ) {
1552              require_once ( ABSPATH . 'wp-admin/custom-background.php' );
1553              $custom_background = new Custom_Background( $args[0]['admin-head-callback'], $args[0]['admin-preview-callback'] );
1554          }
1555      }
1556  }
1557  add_action( 'wp_loaded', '_custom_header_background_just_in_time' );
1558  
1559  /**
1560   * Gets the theme support arguments passed when registering that support
1561   *
1562   * @since 3.1
1563   * @param string $feature the feature to check
1564   * @return array The array of extra arguments
1565   */
1566  function get_theme_support( $feature ) {
1567      global $_wp_theme_features;
1568      if ( ! isset( $_wp_theme_features[ $feature ] ) )
1569          return false;
1570  
1571      if ( func_num_args() <= 1 )
1572          return $_wp_theme_features[ $feature ];
1573  
1574      $args = array_slice( func_get_args(), 1 );
1575      switch ( $feature ) {
1576          case 'custom-header' :
1577          case 'custom-background' :
1578              if ( isset( $_wp_theme_features[ $feature ][0][ $args[0] ] ) )
1579                  return $_wp_theme_features[ $feature ][0][ $args[0] ];
1580              return false;
1581              break;
1582          default :
1583              return $_wp_theme_features[ $feature ];
1584              break;
1585      }
1586  }
1587  
1588  /**
1589   * Allows a theme to de-register its support of a certain feature
1590   *
1591   * Should be called in the theme's functions.php file. Generally would
1592   * be used for child themes to override support from the parent theme.
1593   *
1594   * @since 3.0.0
1595   * @see add_theme_support()
1596   * @param string $feature the feature being added
1597   * @return bool Whether feature was removed.
1598   */
1599  function remove_theme_support( $feature ) {
1600      // Blacklist: for internal registrations not used directly by themes.
1601      if ( in_array( $feature, array( 'editor-style', 'widgets', 'menus' ) ) )
1602          return false;
1603  
1604      return _remove_theme_support( $feature );
1605  }
1606  
1607  /**
1608   * Do not use. Removes theme support internally, ignorant of the blacklist.
1609   *
1610   * @access private
1611   * @since 3.1.0
1612   */
1613  function _remove_theme_support( $feature ) {
1614      global $_wp_theme_features;
1615  
1616      switch ( $feature ) {
1617          case 'custom-header-uploads' :
1618              if ( ! isset( $_wp_theme_features['custom-header'] ) )
1619                  return false;
1620              add_theme_support( 'custom-header', array( 'uploads' => false ) );
1621              return; // Do not continue - custom-header-uploads no longer exists.
1622      }
1623  
1624      if ( ! isset( $_wp_theme_features[ $feature ] ) )
1625          return false;
1626  
1627      switch ( $feature ) {
1628          case 'custom-header' :
1629              if ( ! did_action( 'wp_loaded' ) )
1630                  break;
1631              $support = get_theme_support( 'custom-header' );
1632              if ( $support[0]['wp-head-callback'] )
1633                  remove_action( 'wp_head', $support[0]['wp-head-callback'] );
1634              remove_action( 'admin_menu', array( $GLOBALS['custom_image_header'], 'init' ) );
1635              unset( $GLOBALS['custom_image_header'] );
1636              break;
1637  
1638          case 'custom-background' :
1639              if ( ! did_action( 'wp_loaded' ) )
1640                  break;
1641              $support = get_theme_support( 'custom-background' );
1642              remove_action( 'wp_head', $support[0]['wp-head-callback'] );
1643              remove_action( 'admin_menu', array( $GLOBALS['custom_background'], 'init' ) );
1644              unset( $GLOBALS['custom_background'] );
1645              break;
1646      }
1647  
1648      unset( $_wp_theme_features[ $feature ] );
1649      return true;
1650  }
1651  
1652  /**
1653   * Checks a theme's support for a given feature
1654   *
1655   * @since 2.9.0
1656   * @param string $feature the feature being checked
1657   * @return boolean
1658   */
1659  function current_theme_supports( $feature ) {
1660      global $_wp_theme_features;
1661  
1662      if ( 'custom-header-uploads' == $feature )
1663          return current_theme_supports( 'custom-header', 'uploads' );
1664  
1665      if ( !isset( $_wp_theme_features[$feature] ) )
1666          return false;
1667  
1668      // If no args passed then no extra checks need be performed
1669      if ( func_num_args() <= 1 )
1670          return true;
1671  
1672      $args = array_slice( func_get_args(), 1 );
1673  
1674      switch ( $feature ) {
1675          case 'post-thumbnails':
1676              // post-thumbnails can be registered for only certain content/post types by passing
1677              // an array of types to add_theme_support(). If no array was passed, then
1678              // any type is accepted
1679              if ( true === $_wp_theme_features[$feature] )  // Registered for all types
1680                  return true;
1681              $content_type = $args[0];
1682              return in_array( $content_type, $_wp_theme_features[$feature][0] );
1683              break;
1684  
1685          case 'html5':
1686          case 'post-formats':
1687              // specific post formats can be registered by passing an array of types to
1688              // add_theme_support()
1689  
1690              // Specific areas of HTML5 support *must* be passed via an array to add_theme_support()
1691  
1692              $type = $args[0];
1693              return in_array( $type, $_wp_theme_features[$feature][0] );
1694              break;
1695  
1696          case 'custom-header':
1697          case 'custom-background' :
1698              // specific custom header and background capabilities can be registered by passing
1699              // an array to add_theme_support()
1700              $header_support = $args[0];
1701              return ( isset( $_wp_theme_features[$feature][0][$header_support] ) && $_wp_theme_features[$feature][0][$header_support] );
1702              break;
1703      }
1704  
1705      /**
1706       * Filter whether the current theme supports a specific feature.
1707       *
1708       * The dynamic portion of the hook name, $feature, refers to
1709       * the specific theme feature. Possible values include 'post-formats',
1710       * 'post-thumbnails', 'custom-background', 'custom-header', 'menus',
1711       * 'automatic-feed-links', and 'html5'.
1712       *
1713       * @since 3.4.0
1714       *
1715       * @param bool   true     Whether the current theme supports the given feature. Default true.
1716       * @param array  $args    Array of arguments for the feature.
1717       * @param string $feature The theme feature.
1718       */
1719      return apply_filters( "current_theme_supports-{$feature}", true, $args, $_wp_theme_features[$feature] );
1720  }
1721  
1722  /**
1723   * Checks a theme's support for a given feature before loading the functions which implement it.
1724   *
1725   * @since 2.9.0
1726   * @param string $feature the feature being checked
1727   * @param string $include the file containing the functions that implement the feature
1728   */
1729  function require_if_theme_supports( $feature, $include) {
1730      if ( current_theme_supports( $feature ) )
1731          require ( $include );
1732  }
1733  
1734  /**
1735   * Checks an attachment being deleted to see if it's a header or background image.
1736   *
1737   * If true it removes the theme modification which would be pointing at the deleted
1738   * attachment
1739   *
1740   * @access private
1741   * @since 3.0.0
1742   * @param int $id the attachment id
1743   */
1744  function _delete_attachment_theme_mod( $id ) {
1745      $attachment_image = wp_get_attachment_url( $id );
1746      $header_image = get_header_image();
1747      $background_image = get_background_image();
1748  
1749      if ( $header_image && $header_image == $attachment_image )
1750          remove_theme_mod( 'header_image' );
1751  
1752      if ( $background_image && $background_image == $attachment_image )
1753          remove_theme_mod( 'background_image' );
1754  }
1755  
1756  add_action( 'delete_attachment', '_delete_attachment_theme_mod' );
1757  
1758  /**
1759   * Checks if a theme has been changed and runs 'after_switch_theme' hook on the next WP load
1760   *
1761   * @since 3.3.0
1762   */
1763  function check_theme_switched() {
1764      if ( $stylesheet = get_option( 'theme_switched' ) ) {
1765          $old_theme = wp_get_theme( $stylesheet );
1766  
1767          if ( $old_theme->exists() ) {
1768              /**
1769               * Fires on the first WP load after a theme switch if the old theme still exists.
1770               *
1771               * This action fires multiple times and the parameters differs
1772               * according to the context, if the old theme exists or not.
1773               * If the old theme is missing, the parameter will be the slug
1774               * of the old theme.
1775               *
1776               * @since 3.3.0
1777               *
1778               * @param string   $old_name  Old theme name.
1779               * @param WP_Theme $old_theme WP_Theme instance of the old theme.
1780               */
1781              do_action( 'after_switch_theme', $old_theme->get( 'Name' ), $old_theme );
1782          } else {
1783              /** This action is documented in wp-includes/theme.php */
1784              do_action( 'after_switch_theme', $stylesheet );
1785          }
1786  
1787          update_option( 'theme_switched', false );
1788      }
1789  }
1790  
1791  /**
1792   * Includes and instantiates the WP_Customize_Manager class.
1793   *
1794   * Fires when ?wp_customize=on or on wp-admin/customize.php.
1795   *
1796   * @since 3.4.0
1797   */
1798  function _wp_customize_include() {
1799      if ( ! ( ( isset( $_REQUEST['wp_customize'] ) && 'on' == $_REQUEST['wp_customize'] )
1800          || ( is_admin() && 'customize.php' == basename( $_SERVER['PHP_SELF'] ) )
1801      ) )
1802          return;
1803  
1804      require ( ABSPATH . WPINC . '/class-wp-customize-manager.php' );
1805      // Init Customize class
1806      $GLOBALS['wp_customize'] = new WP_Customize_Manager;
1807  }
1808  add_action( 'plugins_loaded', '_wp_customize_include' );
1809  
1810  /**
1811   * Adds settings for the customize-loader script.
1812   *
1813   * @since 3.4.0
1814   */
1815  function _wp_customize_loader_settings() {
1816      global $wp_scripts;
1817  
1818      $admin_origin = parse_url( admin_url() );
1819      $home_origin  = parse_url( home_url() );
1820      $cross_domain = ( strtolower( $admin_origin[ 'host' ] ) != strtolower( $home_origin[ 'host' ] ) );
1821  
1822      $browser = array(
1823          'mobile' => wp_is_mobile(),
1824          'ios'    => wp_is_mobile() && preg_match( '/iPad|iPod|iPhone/', $_SERVER['HTTP_USER_AGENT'] ),
1825      );
1826  
1827      $settings = array(
1828          'url'           => esc_url( admin_url( 'customize.php' ) ),
1829          'isCrossDomain' => $cross_domain,
1830          'browser'       => $browser,
1831      );
1832  
1833      $script = 'var _wpCustomizeLoaderSettings = ' . json_encode( $settings ) . ';';
1834  
1835      $data = $wp_scripts->get_data( 'customize-loader', 'data' );
1836      if ( $data )
1837          $script = "$data\n$script";
1838  
1839      $wp_scripts->add_data( 'customize-loader', 'data', $script );
1840  }
1841  add_action( 'admin_enqueue_scripts', '_wp_customize_loader_settings' );
1842  
1843  /**
1844   * Returns a URL to load the theme customizer.
1845   *
1846   * @since 3.4.0
1847   *
1848   * @param string $stylesheet Optional. Theme to customize. Defaults to current theme.
1849   *     The theme's stylesheet will be urlencoded if necessary.
1850   */
1851  function wp_customize_url( $stylesheet = null ) {
1852      $url = admin_url( 'customize.php' );
1853      if ( $stylesheet )
1854          $url .= '?theme=' . urlencode( $stylesheet );
1855      return esc_url( $url );
1856  }
1857  
1858  /**
1859   * Prints a script to check whether or not the customizer is supported,
1860   * and apply either the no-customize-support or customize-support class
1861   * to the body.
1862   *
1863   * This function MUST be called inside the body tag.
1864   *
1865   * Ideally, call this function immediately after the body tag is opened.
1866   * This prevents a flash of unstyled content.
1867   *
1868   * It is also recommended that you add the "no-customize-support" class
1869   * to the body tag by default.
1870   *
1871   * @since 3.4.0
1872   */
1873  function wp_customize_support_script() {
1874      $admin_origin = parse_url( admin_url() );
1875      $home_origin  = parse_url( home_url() );
1876      $cross_domain = ( strtolower( $admin_origin[ 'host' ] ) != strtolower( $home_origin[ 'host' ] ) );
1877  
1878      ?>
1879      <script type="text/javascript">
1880          (function() {
1881              var request, b = document.body, c = 'className', cs = 'customize-support', rcs = new RegExp('(^|\\s+)(no-)?'+cs+'(\\s+|$)');
1882  
1883  <?php        if ( $cross_domain ): ?>
1884              request = (function(){ var xhr = new XMLHttpRequest(); return ('withCredentials' in xhr); })();
1885  <?php        else: ?>
1886              request = true;
1887  <?php        endif; ?>
1888  
1889              b[c] = b[c].replace( rcs, ' ' );
1890              b[c] += ( window.postMessage && request ? ' ' : ' no-' ) + cs;
1891          }());
1892      </script>
1893      <?php
1894  }


Generated: Tue Mar 25 01:41:18 2014 WordPress honlapkészítés: online1.hu