[ Index ]

WordPress Cross Reference

title

Body

[close]

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

   1  <?php
   2  /**
   3   * WP_Theme Class
   4   *
   5   * @package WordPress
   6   * @subpackage Theme
   7   */
   8  
   9  final class WP_Theme implements ArrayAccess {
  10  
  11      /**
  12       * Headers for style.css files.
  13       *
  14       * @static
  15       * @access private
  16       * @var array
  17       */
  18      private static $file_headers = array(
  19          'Name'        => 'Theme Name',
  20          'ThemeURI'    => 'Theme URI',
  21          'Description' => 'Description',
  22          'Author'      => 'Author',
  23          'AuthorURI'   => 'Author URI',
  24          'Version'     => 'Version',
  25          'Template'    => 'Template',
  26          'Status'      => 'Status',
  27          'Tags'        => 'Tags',
  28          'TextDomain'  => 'Text Domain',
  29          'DomainPath'  => 'Domain Path',
  30      );
  31  
  32      /**
  33       * Default themes.
  34       *
  35       * @static
  36       * @access private
  37       * @var array
  38       */
  39      private static $default_themes = array(
  40          'classic'        => 'WordPress Classic',
  41          'default'        => 'WordPress Default',
  42          'twentyten'      => 'Twenty Ten',
  43          'twentyeleven'   => 'Twenty Eleven',
  44          'twentytwelve'   => 'Twenty Twelve',
  45          'twentythirteen' => 'Twenty Thirteen',
  46          'twentyfourteen' => 'Twenty Fourteen',
  47      );
  48  
  49      /**
  50       * Renamed theme tags.
  51       */
  52      private static $tag_map = array(
  53          'fixed-width'    => 'fixed-layout',
  54          'flexible-width' => 'fluid-layout',
  55      );
  56  
  57      /**
  58       * Absolute path to the theme root, usually wp-content/themes
  59       *
  60       * @access private
  61       * @var string
  62       */
  63      private $theme_root;
  64  
  65      /**
  66       * Header data from the theme's style.css file.
  67       *
  68       * @access private
  69       * @var array
  70       */
  71      private $headers = array();
  72  
  73      /**
  74       * Header data from the theme's style.css file after being sanitized.
  75       *
  76       * @access private
  77       * @var array
  78       */
  79      private $headers_sanitized;
  80  
  81      /**
  82       * Header name from the theme's style.css after being translated.
  83       *
  84       * Cached due to sorting functions running over the translated name.
  85       */
  86      private $name_translated;
  87  
  88      /**
  89       * Errors encountered when initializing the theme.
  90       *
  91       * @access private
  92       * @var WP_Error
  93       */
  94      private $errors;
  95  
  96      /**
  97       * The directory name of the theme's files, inside the theme root.
  98       *
  99       * In the case of a child theme, this is directory name of the child theme.
 100       * Otherwise, 'stylesheet' is the same as 'template'.
 101       *
 102       * @access private
 103       * @var string
 104       */
 105      private $stylesheet;
 106  
 107      /**
 108       * The directory name of the theme's files, inside the theme root.
 109       *
 110       * In the case of a child theme, this is the directory name of the parent theme.
 111       * Otherwise, 'template' is the same as 'stylesheet'.
 112       *
 113       * @access private
 114       * @var string
 115       */
 116      private $template;
 117  
 118      /**
 119       * A reference to the parent theme, in the case of a child theme.
 120       *
 121       * @access private
 122       * @var WP_Theme
 123       */
 124      private $parent;
 125  
 126      /**
 127       * URL to the theme root, usually an absolute URL to wp-content/themes
 128       *
 129       * @access private
 130       * var string
 131       */
 132      private $theme_root_uri;
 133  
 134      /**
 135       * Flag for whether the theme's textdomain is loaded.
 136       *
 137       * @access private
 138       * @var bool
 139       */
 140      private $textdomain_loaded;
 141  
 142      /**
 143       * Stores an md5 hash of the theme root, to function as the cache key.
 144       *
 145       * @access private
 146       * @var string
 147       */
 148      private $cache_hash;
 149  
 150      /**
 151       * Flag for whether the themes cache bucket should be persistently cached.
 152       *
 153       * Default is false. Can be set with the wp_cache_themes_persistently filter.
 154       *
 155       * @access private
 156       * @var bool
 157       */
 158      private static $persistently_cache;
 159  
 160      /**
 161       * Expiration time for the themes cache bucket.
 162       *
 163       * By default the bucket is not cached, so this value is useless.
 164       *
 165       * @access private
 166       * @var bool
 167       */
 168      private static $cache_expiration = 1800;
 169  
 170      /**
 171       * Constructor for WP_Theme.
 172       *
 173       * @param string $theme_dir Directory of the theme within the theme_root.
 174       * @param string $theme_root Theme root.
 175       * @param WP_Error|null $_child If this theme is a parent theme, the child may be passed for validation purposes.
 176       */
 177  	public function __construct( $theme_dir, $theme_root, $_child = null ) {
 178          global $wp_theme_directories;
 179  
 180          // Initialize caching on first run.
 181          if ( ! isset( self::$persistently_cache ) ) {
 182              self::$persistently_cache = apply_filters( 'wp_cache_themes_persistently', false, 'WP_Theme' );
 183              if ( self::$persistently_cache ) {
 184                  wp_cache_add_global_groups( 'themes' );
 185                  if ( is_int( self::$persistently_cache ) )
 186                      self::$cache_expiration = self::$persistently_cache;
 187              } else {
 188                  wp_cache_add_non_persistent_groups( 'themes' );
 189              }
 190          }
 191  
 192          $this->theme_root = $theme_root;
 193          $this->stylesheet = $theme_dir;
 194  
 195          // Correct a situation where the theme is 'some-directory/some-theme' but 'some-directory' was passed in as part of the theme root instead.
 196          if ( ! in_array( $theme_root, (array) $wp_theme_directories ) && in_array( dirname( $theme_root ), (array) $wp_theme_directories ) ) {
 197              $this->stylesheet = basename( $this->theme_root ) . '/' . $this->stylesheet;
 198              $this->theme_root = dirname( $theme_root );
 199          }
 200  
 201          $this->cache_hash = md5( $this->theme_root . '/' . $this->stylesheet );
 202          $theme_file = $this->stylesheet . '/style.css';
 203  
 204          $cache = $this->cache_get( 'theme' );
 205  
 206          if ( is_array( $cache ) ) {
 207              foreach ( array( 'errors', 'headers', 'template' ) as $key ) {
 208                  if ( isset( $cache[ $key ] ) )
 209                      $this->$key = $cache[ $key ];
 210              }
 211              if ( $this->errors )
 212                  return;
 213              if ( isset( $cache['theme_root_template'] ) )
 214                  $theme_root_template = $cache['theme_root_template'];
 215          } elseif ( ! file_exists( $this->theme_root . '/' . $theme_file ) ) {
 216              $this->headers['Name'] = $this->stylesheet;
 217              if ( ! file_exists( $this->theme_root . '/' . $this->stylesheet ) )
 218                  $this->errors = new WP_Error( 'theme_not_found', sprintf( __( 'The theme directory "%s" does not exist.' ), $this->stylesheet ) );
 219              else
 220                  $this->errors = new WP_Error( 'theme_no_stylesheet', __( 'Stylesheet is missing.' ) );
 221              $this->template = $this->stylesheet;
 222              $this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
 223              if ( ! file_exists( $this->theme_root ) ) // Don't cache this one.
 224                  $this->errors->add( 'theme_root_missing', __( 'ERROR: The themes directory is either empty or doesn&#8217;t exist. Please check your installation.' ) );
 225              return;
 226          } elseif ( ! is_readable( $this->theme_root . '/' . $theme_file ) ) {
 227              $this->headers['Name'] = $this->stylesheet;
 228              $this->errors = new WP_Error( 'theme_stylesheet_not_readable', __( 'Stylesheet is not readable.' ) );
 229              $this->template = $this->stylesheet;
 230              $this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
 231              return;
 232          } else {
 233              $this->headers = get_file_data( $this->theme_root . '/' . $theme_file, self::$file_headers, 'theme' );
 234              // Default themes always trump their pretenders.
 235              // Properly identify default themes that are inside a directory within wp-content/themes.
 236              if ( $default_theme_slug = array_search( $this->headers['Name'], self::$default_themes ) ) {
 237                  if ( basename( $this->stylesheet ) != $default_theme_slug )
 238                      $this->headers['Name'] .= '/' . $this->stylesheet;
 239              }
 240          }
 241  
 242          // (If template is set from cache [and there are no errors], we know it's good.)
 243          if ( ! $this->template && ! ( $this->template = $this->headers['Template'] ) ) {
 244              $this->template = $this->stylesheet;
 245              if ( ! file_exists( $this->theme_root . '/' . $this->stylesheet . '/index.php' ) ) {
 246                  $this->errors = new WP_Error( 'theme_no_index', __( 'Template is missing.' ) );
 247                  $this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
 248                  return;
 249              }
 250          }
 251  
 252          // If we got our data from cache, we can assume that 'template' is pointing to the right place.
 253          if ( ! is_array( $cache ) && $this->template != $this->stylesheet && ! file_exists( $this->theme_root . '/' . $this->template . '/index.php' ) ) {
 254              // If we're in a directory of themes inside /themes, look for the parent nearby.
 255              // wp-content/themes/directory-of-themes/*
 256              $parent_dir = dirname( $this->stylesheet );
 257              if ( '.' != $parent_dir && file_exists( $this->theme_root . '/' . $parent_dir . '/' . $this->template . '/index.php' ) ) {
 258                  $this->template = $parent_dir . '/' . $this->template;
 259              } elseif ( ( $directories = search_theme_directories() ) && isset( $directories[ $this->template ] ) ) {
 260                  // Look for the template in the search_theme_directories() results, in case it is in another theme root.
 261                  // We don't look into directories of themes, just the theme root.
 262                  $theme_root_template = $directories[ $this->template ]['theme_root'];
 263              } else {
 264                  // Parent theme is missing.
 265                  $this->errors = new WP_Error( 'theme_no_parent', sprintf( __( 'The parent theme is missing. Please install the "%s" parent theme.' ), $this->template ) );
 266                  $this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
 267                  $this->parent = new WP_Theme( $this->template, $this->theme_root, $this );
 268                  return;
 269              }
 270          }
 271  
 272          // Set the parent, if we're a child theme.
 273          if ( $this->template != $this->stylesheet ) {
 274              // If we are a parent, then there is a problem. Only two generations allowed! Cancel things out.
 275              if ( is_a( $_child, 'WP_Theme' ) && $_child->template == $this->stylesheet ) {
 276                  $_child->parent = null;
 277                  $_child->errors = new WP_Error( 'theme_parent_invalid', sprintf( __( 'The "%s" theme is not a valid parent theme.' ), $_child->template ) );
 278                  $_child->cache_add( 'theme', array( 'headers' => $_child->headers, 'errors' => $_child->errors, 'stylesheet' => $_child->stylesheet, 'template' => $_child->template ) );
 279                  // The two themes actually reference each other with the Template header.
 280                  if ( $_child->stylesheet == $this->template ) {
 281                      $this->errors = new WP_Error( 'theme_parent_invalid', sprintf( __( 'The "%s" theme is not a valid parent theme.' ), $this->template ) );
 282                      $this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
 283                  }
 284                  return;
 285              }
 286              // Set the parent. Pass the current instance so we can do the crazy checks above and assess errors.
 287              $this->parent = new WP_Theme( $this->template, isset( $theme_root_template ) ? $theme_root_template : $this->theme_root, $this );
 288          }
 289  
 290          // We're good. If we didn't retrieve from cache, set it.
 291          if ( ! is_array( $cache ) ) {
 292              $cache = array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template );
 293              // If the parent theme is in another root, we'll want to cache this. Avoids an entire branch of filesystem calls above.
 294              if ( isset( $theme_root_template ) )
 295                  $cache['theme_root_template'] = $theme_root_template;
 296              $this->cache_add( 'theme', $cache );
 297          }
 298      }
 299  
 300      /**
 301       * When converting the object to a string, the theme name is returned.
 302       *
 303       * @return string Theme name, ready for display (translated)
 304       */
 305  	public function __toString() {
 306          return (string) $this->display('Name');
 307      }
 308  
 309      /**
 310       * __isset() magic method for properties formerly returned by current_theme_info()
 311       */
 312  	public function __isset( $offset ) {
 313          static $properties = array(
 314              'name', 'title', 'version', 'parent_theme', 'template_dir', 'stylesheet_dir', 'template', 'stylesheet',
 315              'screenshot', 'description', 'author', 'tags', 'theme_root', 'theme_root_uri',
 316          );
 317  
 318          return in_array( $offset, $properties );
 319      }
 320  
 321      /**
 322       * __get() magic method for properties formerly returned by current_theme_info()
 323       */
 324  	public function __get( $offset ) {
 325          switch ( $offset ) {
 326              case 'name' :
 327              case 'title' :
 328                  return $this->get('Name');
 329              case 'version' :
 330                  return $this->get('Version');
 331              case 'parent_theme' :
 332                  return $this->parent() ? $this->parent()->get('Name') : '';
 333              case 'template_dir' :
 334                  return $this->get_template_directory();
 335              case 'stylesheet_dir' :
 336                  return $this->get_stylesheet_directory();
 337              case 'template' :
 338                  return $this->get_template();
 339              case 'stylesheet' :
 340                  return $this->get_stylesheet();
 341              case 'screenshot' :
 342                  return $this->get_screenshot( 'relative' );
 343              // 'author' and 'description' did not previously return translated data.
 344              case 'description' :
 345                  return $this->display('Description');
 346              case 'author' :
 347                  return $this->display('Author');
 348              case 'tags' :
 349                  return $this->get( 'Tags' );
 350              case 'theme_root' :
 351                  return $this->get_theme_root();
 352              case 'theme_root_uri' :
 353                  return $this->get_theme_root_uri();
 354              // For cases where the array was converted to an object.
 355              default :
 356                  return $this->offsetGet( $offset );
 357          }
 358      }
 359  
 360      /**
 361       * Method to implement ArrayAccess for keys formerly returned by get_themes()
 362       */
 363  	public function offsetSet( $offset, $value ) {}
 364  
 365      /**
 366       * Method to implement ArrayAccess for keys formerly returned by get_themes()
 367       */
 368  	public function offsetUnset( $offset ) {}
 369  
 370      /**
 371       * Method to implement ArrayAccess for keys formerly returned by get_themes()
 372       */
 373  	public function offsetExists( $offset ) {
 374          static $keys = array(
 375              'Name', 'Version', 'Status', 'Title', 'Author', 'Author Name', 'Author URI', 'Description',
 376              'Template', 'Stylesheet', 'Template Files', 'Stylesheet Files', 'Template Dir', 'Stylesheet Dir',
 377               'Screenshot', 'Tags', 'Theme Root', 'Theme Root URI', 'Parent Theme',
 378          );
 379  
 380          return in_array( $offset, $keys );
 381      }
 382  
 383      /**
 384       * Method to implement ArrayAccess for keys formerly returned by get_themes().
 385       *
 386       * Author, Author Name, Author URI, and Description did not previously return
 387       * translated data. We are doing so now as it is safe to do. However, as
 388       * Name and Title could have been used as the key for get_themes(), both remain
 389       * untranslated for back compatibility. This means that ['Name'] is not ideal,
 390       * and care should be taken to use $theme->display('Name') to get a properly
 391       * translated header.
 392       */
 393  	public function offsetGet( $offset ) {
 394          switch ( $offset ) {
 395              case 'Name' :
 396              case 'Title' :
 397                  // See note above about using translated data. get() is not ideal.
 398                  // It is only for backwards compatibility. Use display().
 399                  return $this->get('Name');
 400              case 'Author' :
 401                  return $this->display( 'Author');
 402              case 'Author Name' :
 403                  return $this->display( 'Author', false);
 404              case 'Author URI' :
 405                  return $this->display('AuthorURI');
 406              case 'Description' :
 407                  return $this->display( 'Description');
 408              case 'Version' :
 409              case 'Status' :
 410                  return $this->get( $offset );
 411              case 'Template' :
 412                  return $this->get_template();
 413              case 'Stylesheet' :
 414                  return $this->get_stylesheet();
 415              case 'Template Files' :
 416                  return $this->get_files( 'php', 1, true );
 417              case 'Stylesheet Files' :
 418                  return $this->get_files( 'css', 0, false );
 419              case 'Template Dir' :
 420                  return $this->get_template_directory();
 421              case 'Stylesheet Dir' :
 422                  return $this->get_stylesheet_directory();
 423              case 'Screenshot' :
 424                  return $this->get_screenshot( 'relative' );
 425              case 'Tags' :
 426                  return $this->get('Tags');
 427              case 'Theme Root' :
 428                  return $this->get_theme_root();
 429              case 'Theme Root URI' :
 430                  return $this->get_theme_root_uri();
 431              case 'Parent Theme' :
 432                  return $this->parent() ? $this->parent()->get('Name') : '';
 433              default :
 434                  return null;
 435          }
 436      }
 437  
 438      /**
 439       * Returns errors property.
 440       *
 441       * @since 3.4.0
 442       * @access public
 443       *
 444       * @return WP_Error|bool WP_Error if there are errors, or false.
 445       */
 446  	public function errors() {
 447          return is_wp_error( $this->errors ) ? $this->errors : false;
 448      }
 449  
 450      /**
 451       * Whether the theme exists.
 452       *
 453       * A theme with errors exists. A theme with the error of 'theme_not_found',
 454       * meaning that the theme's directory was not found, does not exist.
 455       *
 456       * @since 3.4.0
 457       * @access public
 458       *
 459       * @return bool Whether the theme exists.
 460       */
 461  	public function exists() {
 462          return ! ( $this->errors() && in_array( 'theme_not_found', $this->errors()->get_error_codes() ) );
 463      }
 464  
 465      /**
 466       * Returns reference to the parent theme.
 467       *
 468       * @since 3.4.0
 469       * @access public
 470       *
 471       * @return WP_Theme|bool Parent theme, or false if the current theme is not a child theme.
 472       */
 473  	public function parent() {
 474          return isset( $this->parent ) ? $this->parent : false;
 475      }
 476  
 477      /**
 478       * Adds theme data to cache.
 479       *
 480       * Cache entries keyed by the theme and the type of data.
 481       *
 482       * @access private
 483       * @since 3.4.0
 484       *
 485       * @param string $key Type of data to store (theme, screenshot, headers, page_templates)
 486       * @param string $data Data to store
 487       * @return bool Return value from wp_cache_add()
 488       */
 489  	private function cache_add( $key, $data ) {
 490          return wp_cache_add( $key . '-' . $this->cache_hash, $data, 'themes', self::$cache_expiration );
 491      }
 492  
 493      /**
 494       * Gets theme data from cache.
 495       *
 496       * Cache entries are keyed by the theme and the type of data.
 497       *
 498       * @access private
 499       * @since 3.4.0
 500       *
 501       * @param string $key Type of data to retrieve (theme, screenshot, headers, page_templates)
 502       * @return mixed Retrieved data
 503       */
 504  	private function cache_get( $key ) {
 505          return wp_cache_get( $key . '-' . $this->cache_hash, 'themes' );
 506      }
 507  
 508      /**
 509       * Clears the cache for the theme.
 510       *
 511       * @access public
 512       * @since 3.4.0
 513       */
 514  	public function cache_delete() {
 515          foreach ( array( 'theme', 'screenshot', 'headers', 'page_templates' ) as $key )
 516              wp_cache_delete( $key . '-' . $this->cache_hash, 'themes' );
 517          $this->template = $this->textdomain_loaded = $this->theme_root_uri = $this->parent = $this->errors = $this->headers_sanitized = $this->name_translated = null;
 518          $this->headers = array();
 519          $this->__construct( $this->stylesheet, $this->theme_root );
 520      }
 521  
 522      /**
 523       * Get a raw, unformatted theme header.
 524       *
 525       * The header is sanitized, but is not translated, and is not marked up for display.
 526       * To get a theme header for display, use the display() method.
 527       *
 528       * Use the get_template() method, not the 'Template' header, for finding the template.
 529       * The 'Template' header is only good for what was written in the style.css, while
 530       * get_template() takes into account where WordPress actually located the theme and
 531       * whether it is actually valid.
 532       *
 533       * @access public
 534       * @since 3.4.0
 535       *
 536       * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags.
 537       * @return string String on success, false on failure.
 538       */
 539  	public function get( $header ) {
 540          if ( ! isset( $this->headers[ $header ] ) )
 541              return false;
 542  
 543          if ( ! isset( $this->headers_sanitized ) ) {
 544              $this->headers_sanitized = $this->cache_get( 'headers' );
 545              if ( ! is_array( $this->headers_sanitized ) )
 546                  $this->headers_sanitized = array();
 547          }
 548  
 549          if ( isset( $this->headers_sanitized[ $header ] ) )
 550              return $this->headers_sanitized[ $header ];
 551  
 552          // If themes are a persistent group, sanitize everything and cache it. One cache add is better than many cache sets.
 553          if ( self::$persistently_cache ) {
 554              foreach ( array_keys( $this->headers ) as $_header )
 555                  $this->headers_sanitized[ $_header ] = $this->sanitize_header( $_header, $this->headers[ $_header ] );
 556              $this->cache_add( 'headers', $this->headers_sanitized );
 557          } else {
 558              $this->headers_sanitized[ $header ] = $this->sanitize_header( $header, $this->headers[ $header ] );
 559          }
 560  
 561          return $this->headers_sanitized[ $header ];
 562      }
 563  
 564      /**
 565       * Gets a theme header, formatted and translated for display.
 566       *
 567       * @access public
 568       * @since 3.4.0
 569       *
 570       * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags.
 571       * @param bool $markup Optional. Whether to mark up the header. Defaults to true.
 572       * @param bool $translate Optional. Whether to translate the header. Defaults to true.
 573       * @return string Processed header, false on failure.
 574       */
 575  	public function display( $header, $markup = true, $translate = true ) {
 576          $value = $this->get( $header );
 577  
 578          if ( $translate && ( empty( $value ) || ! $this->load_textdomain() ) )
 579              $translate = false;
 580  
 581          if ( $translate )
 582              $value = $this->translate_header( $header, $value );
 583  
 584          if ( $markup )
 585              $value = $this->markup_header( $header, $value, $translate );
 586  
 587          return $value;
 588      }
 589  
 590      /**
 591       * Sanitize a theme header.
 592       *
 593       * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags.
 594       * @param string $value Value to sanitize.
 595       */
 596  	private function sanitize_header( $header, $value ) {
 597          switch ( $header ) {
 598              case 'Status' :
 599                  if ( ! $value ) {
 600                      $value = 'publish';
 601                      break;
 602                  }
 603                  // Fall through otherwise.
 604              case 'Name' :
 605                  static $header_tags = array(
 606                      'abbr'    => array( 'title' => true ),
 607                      'acronym' => array( 'title' => true ),
 608                      'code'    => true,
 609                      'em'      => true,
 610                      'strong'  => true,
 611                  );
 612                  $value = wp_kses( $value, $header_tags );
 613                  break;
 614              case 'Author' :
 615                  // There shouldn't be anchor tags in Author, but some themes like to be challenging.
 616              case 'Description' :
 617                  static $header_tags_with_a = array(
 618                      'a'       => array( 'href' => true, 'title' => true ),
 619                      'abbr'    => array( 'title' => true ),
 620                      'acronym' => array( 'title' => true ),
 621                      'code'    => true,
 622                      'em'      => true,
 623                      'strong'  => true,
 624                  );
 625                  $value = wp_kses( $value, $header_tags_with_a );
 626                  break;
 627              case 'ThemeURI' :
 628              case 'AuthorURI' :
 629                  $value = esc_url_raw( $value );
 630                  break;
 631              case 'Tags' :
 632                  $value = array_filter( array_map( 'trim', explode( ',', strip_tags( $value ) ) ) );
 633                  break;
 634          }
 635  
 636          return $value;
 637      }
 638  
 639      /**
 640       * Mark up a theme header.
 641       *
 642       * @access private
 643       * @since 3.4.0
 644       *
 645       * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags.
 646       * @param string $value Value to mark up.
 647       * @param string $translate Whether the header has been translated.
 648       * @return string Value, marked up.
 649       */
 650  	private function markup_header( $header, $value, $translate ) {
 651          switch ( $header ) {
 652              case 'Name' :
 653                  if ( empty( $value ) )
 654                      $value = $this->get_stylesheet();
 655                  break;
 656              case 'Description' :
 657                  $value = wptexturize( $value );
 658                  break;
 659              case 'Author' :
 660                  if ( $this->get('AuthorURI') ) {
 661                      static $attr = null;
 662                      if ( ! isset( $attr ) )
 663                          $attr = esc_attr__( 'Visit author homepage' );
 664                      $value = sprintf( '<a href="%1$s" title="%2$s">%3$s</a>', $this->display( 'AuthorURI', true, $translate ), $attr, $value );
 665                  } elseif ( ! $value ) {
 666                      $value = __( 'Anonymous' );
 667                  }
 668                  break;
 669              case 'Tags' :
 670                  static $comma = null;
 671                  if ( ! isset( $comma ) ) {
 672                      /* translators: used between list items, there is a space after the comma */
 673                      $comma = __( ', ' );
 674                  }
 675                  $value = implode( $comma, $value );
 676                  break;
 677              case 'ThemeURI' :
 678              case 'AuthorURI' :
 679                  $value = esc_url( $value );
 680                  break;
 681          }
 682  
 683          return $value;
 684      }
 685  
 686      /**
 687       * Translate a theme header.
 688       *
 689       * @access private
 690       * @since 3.4.0
 691       *
 692       * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags.
 693       * @param string $value Value to translate.
 694       * @return string Translated value.
 695       */
 696  	private function translate_header( $header, $value ) {
 697          switch ( $header ) {
 698              case 'Name' :
 699                  // Cached for sorting reasons.
 700                  if ( isset( $this->name_translated ) )
 701                      return $this->name_translated;
 702                  $this->name_translated = translate( $value, $this->get('TextDomain' ) );
 703                  return $this->name_translated;
 704              case 'Tags' :
 705                  if ( empty( $value ) || ! function_exists( 'get_theme_feature_list' ) )
 706                      return $value;
 707  
 708                  static $tags_list;
 709                  if ( ! isset( $tags_list ) ) {
 710                      $tags_list = array();
 711                      $feature_list = get_theme_feature_list( false ); // No API
 712                      foreach ( $feature_list as $tags )
 713                          $tags_list += $tags;
 714                  }
 715  
 716                  foreach ( $value as &$tag ) {
 717                      if ( isset( $tags_list[ $tag ] ) ) {
 718                          $tag = $tags_list[ $tag ];
 719                      } elseif ( isset( self::$tag_map[ $tag ] ) ) {
 720                          $tag = $tags_list[ self::$tag_map[ $tag ] ];
 721                      }
 722                  }
 723  
 724                  return $value;
 725                  break;
 726              default :
 727                  $value = translate( $value, $this->get('TextDomain') );
 728          }
 729          return $value;
 730      }
 731  
 732      /**
 733       * The directory name of the theme's "stylesheet" files, inside the theme root.
 734       *
 735       * In the case of a child theme, this is directory name of the child theme.
 736       * Otherwise, get_stylesheet() is the same as get_template().
 737       *
 738       * @since 3.4.0
 739       * @access public
 740       *
 741       * @return string Stylesheet
 742       */
 743  	public function get_stylesheet() {
 744          return $this->stylesheet;
 745      }
 746  
 747      /**
 748       * The directory name of the theme's "template" files, inside the theme root.
 749       *
 750       * In the case of a child theme, this is the directory name of the parent theme.
 751       * Otherwise, the get_template() is the same as get_stylesheet().
 752       *
 753       * @since 3.4.0
 754       * @access public
 755       *
 756       * @return string Template
 757       */
 758  	public function get_template() {
 759          return $this->template;
 760      }
 761  
 762      /**
 763       * Returns the absolute path to the directory of a theme's "stylesheet" files.
 764       *
 765       * In the case of a child theme, this is the absolute path to the directory
 766       * of the child theme's files.
 767       *
 768       * @since 3.4.0
 769       * @access public
 770       *
 771       * @return string Absolute path of the stylesheet directory.
 772       */
 773  	public function get_stylesheet_directory() {
 774          if ( $this->errors() && in_array( 'theme_root_missing', $this->errors()->get_error_codes() ) )
 775              return '';
 776  
 777          return $this->theme_root . '/' . $this->stylesheet;
 778      }
 779  
 780      /**
 781       * Returns the absolute path to the directory of a theme's "template" files.
 782       *
 783       * In the case of a child theme, this is the absolute path to the directory
 784       * of the parent theme's files.
 785       *
 786       * @since 3.4.0
 787       * @access public
 788       *
 789       * @return string Absolute path of the template directory.
 790       */
 791  	public function get_template_directory() {
 792          if ( $this->parent() )
 793              $theme_root = $this->parent()->theme_root;
 794          else
 795              $theme_root = $this->theme_root;
 796  
 797          return $theme_root . '/' . $this->template;
 798      }
 799  
 800      /**
 801       * Returns the URL to the directory of a theme's "stylesheet" files.
 802       *
 803       * In the case of a child theme, this is the URL to the directory of the
 804       * child theme's files.
 805       *
 806       * @since 3.4.0
 807       * @access public
 808       *
 809       * @return string URL to the stylesheet directory.
 810       */
 811  	public function get_stylesheet_directory_uri() {
 812          return $this->get_theme_root_uri() . '/' . str_replace( '%2F', '/', rawurlencode( $this->stylesheet ) );
 813      }
 814  
 815      /**
 816       * Returns the URL to the directory of a theme's "template" files.
 817       *
 818       * In the case of a child theme, this is the URL to the directory of the
 819       * parent theme's files.
 820       *
 821       * @since 3.4.0
 822       * @access public
 823       *
 824       * @return string URL to the template directory.
 825       */
 826  	public function get_template_directory_uri() {
 827          if ( $this->parent() )
 828              $theme_root_uri = $this->parent()->get_theme_root_uri();
 829          else
 830              $theme_root_uri = $this->get_theme_root_uri();
 831  
 832          return $theme_root_uri . '/' . str_replace( '%2F', '/', rawurlencode( $this->template ) );
 833      }
 834  
 835      /**
 836       * The absolute path to the directory of the theme root.
 837       *
 838       * This is typically the absolute path to wp-content/themes.
 839       *
 840       * @since 3.4.0
 841       * @access public
 842       *
 843       * @return string Theme root.
 844       */
 845  	public function get_theme_root() {
 846          return $this->theme_root;
 847      }
 848  
 849      /**
 850       * Returns the URL to the directory of the theme root.
 851       *
 852       * This is typically the absolute URL to wp-content/themes. This forms the basis
 853       * for all other URLs returned by WP_Theme, so we pass it to the public function
 854       * get_theme_root_uri() and allow it to run the theme_root_uri filter.
 855       *
 856       * @uses get_theme_root_uri()
 857       *
 858       * @since 3.4.0
 859       * @access public
 860       *
 861       * @return string Theme root URI.
 862       */
 863  	public function get_theme_root_uri() {
 864          if ( ! isset( $this->theme_root_uri ) )
 865              $this->theme_root_uri = get_theme_root_uri( $this->stylesheet, $this->theme_root );
 866          return $this->theme_root_uri;
 867      }
 868  
 869      /**
 870       * Returns the main screenshot file for the theme.
 871       *
 872       * The main screenshot is called screenshot.png. gif and jpg extensions are also allowed.
 873       *
 874       * Screenshots for a theme must be in the stylesheet directory. (In the case of child
 875       * themes, parent theme screenshots are not inherited.)
 876       *
 877       * @since 3.4.0
 878       * @access public
 879       *
 880       * @param string $uri Type of URL to return, either 'relative' or an absolute URI. Defaults to absolute URI.
 881       * @return mixed Screenshot file. False if the theme does not have a screenshot.
 882       */
 883  	public function get_screenshot( $uri = 'uri' ) {
 884          $screenshot = $this->cache_get( 'screenshot' );
 885          if ( $screenshot ) {
 886              if ( 'relative' == $uri )
 887                  return $screenshot;
 888              return $this->get_stylesheet_directory_uri() . '/' . $screenshot;
 889          } elseif ( 0 === $screenshot ) {
 890              return false;
 891          }
 892  
 893          foreach ( array( 'png', 'gif', 'jpg', 'jpeg' ) as $ext ) {
 894              if ( file_exists( $this->get_stylesheet_directory() . "/screenshot.$ext" ) ) {
 895                  $this->cache_add( 'screenshot', 'screenshot.' . $ext );
 896                  if ( 'relative' == $uri )
 897                      return 'screenshot.' . $ext;
 898                  return $this->get_stylesheet_directory_uri() . '/' . 'screenshot.' . $ext;
 899              }
 900          }
 901  
 902          $this->cache_add( 'screenshot', 0 );
 903          return false;
 904      }
 905  
 906      /**
 907       * Return files in the theme's directory.
 908       *
 909       * @since 3.4.0
 910       * @access public
 911       *
 912       * @param mixed $type Optional. Array of extensions to return. Defaults to all files (null).
 913       * @param int $depth Optional. How deep to search for files. Defaults to a flat scan (0 depth). -1 depth is infinite.
 914       * @param bool $search_parent Optional. Whether to return parent files. Defaults to false.
 915       * @return array Array of files, keyed by the path to the file relative to the theme's directory, with the values
 916       *     being absolute paths.
 917       */
 918  	public function get_files( $type = null, $depth = 0, $search_parent = false ) {
 919          $files = (array) self::scandir( $this->get_stylesheet_directory(), $type, $depth );
 920  
 921          if ( $search_parent && $this->parent() )
 922              $files += (array) self::scandir( $this->get_template_directory(), $type, $depth );
 923  
 924          return $files;
 925      }
 926  
 927      /**
 928       * Returns the theme's page templates.
 929       *
 930       * @since 3.4.0
 931       * @access public
 932       *
 933       * @return array Array of page templates, keyed by filename, with the value of the translated header name.
 934       */
 935  	public function get_page_templates() {
 936          // If you screw up your current theme and we invalidate your parent, most things still work. Let it slide.
 937          if ( $this->errors() && $this->errors()->get_error_codes() !== array( 'theme_parent_invalid' ) )
 938              return array();
 939  
 940          $page_templates = $this->cache_get( 'page_templates' );
 941  
 942          if ( ! is_array( $page_templates ) ) {
 943              $page_templates = array();
 944  
 945              $files = (array) $this->get_files( 'php', 1 );
 946  
 947              foreach ( $files as $file => $full_path ) {
 948                  if ( ! preg_match( '|Template Name:(.*)$|mi', file_get_contents( $full_path ), $header ) )
 949                      continue;
 950                  $page_templates[ $file ] = _cleanup_header_comment( $header[1] );
 951              }
 952  
 953              $this->cache_add( 'page_templates', $page_templates );
 954          }
 955  
 956          if ( $this->load_textdomain() ) {
 957              foreach ( $page_templates as &$page_template ) {
 958                  $page_template = $this->translate_header( 'Template Name', $page_template );
 959              }
 960          }
 961  
 962          if ( $this->parent() )
 963              $page_templates += $this->parent()->get_page_templates();
 964  
 965          return $page_templates;
 966      }
 967  
 968      /**
 969       * Scans a directory for files of a certain extension.
 970       *
 971       * @since 3.4.0
 972       * @access private
 973       *
 974       * @param string $path Absolute path to search.
 975       * @param mixed  Array of extensions to find, string of a single extension, or null for all extensions.
 976       * @param int $depth How deep to search for files. Optional, defaults to a flat scan (0 depth). -1 depth is infinite.
 977       * @param string $relative_path The basename of the absolute path. Used to control the returned path
 978       *     for the found files, particularly when this function recurses to lower depths.
 979       */
 980  	private static function scandir( $path, $extensions = null, $depth = 0, $relative_path = '' ) {
 981          if ( ! is_dir( $path ) )
 982              return false;
 983  
 984          if ( $extensions ) {
 985              $extensions = (array) $extensions;
 986              $_extensions = implode( '|', $extensions );
 987          }
 988  
 989          $relative_path = trailingslashit( $relative_path );
 990          if ( '/' == $relative_path )
 991              $relative_path = '';
 992  
 993          $results = scandir( $path );
 994          $files = array();
 995  
 996          foreach ( $results as $result ) {
 997              if ( '.' == $result[0] )
 998                  continue;
 999              if ( is_dir( $path . '/' . $result ) ) {
1000                  if ( ! $depth || 'CVS' == $result )
1001                      continue;
1002                  $found = self::scandir( $path . '/' . $result, $extensions, $depth - 1 , $relative_path . $result );
1003                  $files = array_merge_recursive( $files, $found );
1004              } elseif ( ! $extensions || preg_match( '~\.(' . $_extensions . ')$~', $result ) ) {
1005                  $files[ $relative_path . $result ] = $path . '/' . $result;
1006              }
1007          }
1008  
1009          return $files;
1010      }
1011  
1012      /**
1013       * Loads the theme's textdomain.
1014       *
1015       * Translation files are not inherited from the parent theme. Todo: if this fails for the
1016       * child theme, it should probably try to load the parent theme's translations.
1017       *
1018       * @since 3.4.0
1019       * @access public
1020       *
1021       * @return True if the textdomain was successfully loaded or has already been loaded. False if
1022       *     no textdomain was specified in the file headers, or if the domain could not be loaded.
1023       */
1024  	public function load_textdomain() {
1025          if ( isset( $this->textdomain_loaded ) )
1026              return $this->textdomain_loaded;
1027  
1028          $textdomain = $this->get('TextDomain');
1029          if ( ! $textdomain ) {
1030              $this->textdomain_loaded = false;
1031              return false;
1032          }
1033  
1034          if ( is_textdomain_loaded( $textdomain ) ) {
1035              $this->textdomain_loaded = true;
1036              return true;
1037          }
1038  
1039          $path = $this->get_stylesheet_directory();
1040          if ( $domainpath = $this->get('DomainPath') )
1041              $path .= $domainpath;
1042          else
1043              $path .= '/languages';
1044  
1045          $this->textdomain_loaded = load_theme_textdomain( $textdomain, $path );
1046          return $this->textdomain_loaded;
1047      }
1048  
1049      /**
1050       * Whether the theme is allowed (multisite only).
1051       *
1052       * @since 3.4.0
1053       * @access public
1054       *
1055       * @param string $check Optional. Whether to check only the 'network'-wide settings, the 'site'
1056       *     settings, or 'both'. Defaults to 'both'.
1057       * @param int $blog_id Optional. Ignored if only network-wide settings are checked. Defaults to current blog.
1058       * @return bool Whether the theme is allowed for the network. Returns true in single-site.
1059       */
1060  	public function is_allowed( $check = 'both', $blog_id = null ) {
1061          if ( ! is_multisite() )
1062              return true;
1063  
1064          if ( 'both' == $check || 'network' == $check ) {
1065              $allowed = self::get_allowed_on_network();
1066              if ( ! empty( $allowed[ $this->get_stylesheet() ] ) )
1067                  return true;
1068          }
1069  
1070          if ( 'both' == $check || 'site' == $check ) {
1071              $allowed = self::get_allowed_on_site( $blog_id );
1072              if ( ! empty( $allowed[ $this->get_stylesheet() ] ) )
1073                  return true;
1074          }
1075  
1076          return false;
1077      }
1078  
1079      /**
1080       * Returns array of stylesheet names of themes allowed on the site or network.
1081       *
1082       * @since 3.4.0
1083       * @access public
1084       *
1085       * @param int $blog_id Optional. Defaults to current blog.
1086       * @return array Array of stylesheet names.
1087       */
1088  	public static function get_allowed( $blog_id = null ) {
1089          $network = (array) apply_filters( 'allowed_themes', self::get_allowed_on_network() );
1090          return $network + self::get_allowed_on_site( $blog_id );
1091      }
1092  
1093      /**
1094       * Returns array of stylesheet names of themes allowed on the network.
1095       *
1096       * @since 3.4.0
1097       * @access public
1098       *
1099       * @return array Array of stylesheet names.
1100       */
1101  	public static function get_allowed_on_network() {
1102          static $allowed_themes;
1103          if ( ! isset( $allowed_themes ) )
1104              $allowed_themes = (array) get_site_option( 'allowedthemes' );
1105          return $allowed_themes;
1106      }
1107  
1108      /**
1109       * Returns array of stylesheet names of themes allowed on the site.
1110       *
1111       * @since 3.4.0
1112       * @access public
1113       *
1114       * @param int $blog_id Optional. Defaults to current blog.
1115       * @return array Array of stylesheet names.
1116       */
1117  	public static function get_allowed_on_site( $blog_id = null ) {
1118          static $allowed_themes = array();
1119  
1120          if ( ! $blog_id || ! is_multisite() )
1121              $blog_id = get_current_blog_id();
1122  
1123          if ( isset( $allowed_themes[ $blog_id ] ) )
1124              return $allowed_themes[ $blog_id ];
1125  
1126          $current = $blog_id == get_current_blog_id();
1127  
1128          if ( $current ) {
1129              $allowed_themes[ $blog_id ] = get_option( 'allowedthemes' );
1130          } else {
1131              switch_to_blog( $blog_id );
1132              $allowed_themes[ $blog_id ] = get_option( 'allowedthemes' );
1133              restore_current_blog();
1134          }
1135  
1136          // This is all super old MU back compat joy.
1137          // 'allowedthemes' keys things by stylesheet. 'allowed_themes' keyed things by name.
1138          if ( false === $allowed_themes[ $blog_id ] ) {
1139              if ( $current ) {
1140                  $allowed_themes[ $blog_id ] = get_option( 'allowed_themes' );
1141              } else {
1142                  switch_to_blog( $blog_id );
1143                  $allowed_themes[ $blog_id ] = get_option( 'allowed_themes' );
1144                  restore_current_blog();
1145              }
1146  
1147              if ( ! is_array( $allowed_themes[ $blog_id ] ) || empty( $allowed_themes[ $blog_id ] ) ) {
1148                  $allowed_themes[ $blog_id ] = array();
1149              } else {
1150                  $converted = array();
1151                  $themes = wp_get_themes();
1152                  foreach ( $themes as $stylesheet => $theme_data ) {
1153                      if ( isset( $allowed_themes[ $blog_id ][ $theme_data->get('Name') ] ) )
1154                          $converted[ $stylesheet ] = true;
1155                  }
1156                  $allowed_themes[ $blog_id ] = $converted;
1157              }
1158              // Set the option so we never have to go through this pain again.
1159              if ( is_admin() && $allowed_themes[ $blog_id ] ) {
1160                  if ( $current ) {
1161                      update_option( 'allowedthemes', $allowed_themes[ $blog_id ] );
1162                      delete_option( 'allowed_themes' );
1163                  } else {
1164                      switch_to_blog( $blog_id );
1165                      update_option( 'allowedthemes', $allowed_themes[ $blog_id ] );
1166                      delete_option( 'allowed_themes' );
1167                      restore_current_blog();
1168                  }
1169              }
1170          }
1171  
1172          return (array) $allowed_themes[ $blog_id ];
1173      }
1174  
1175      /**
1176       * Sort themes by name.
1177       *
1178       * @since 3.4.0
1179       * @access public
1180       */
1181  	public static function sort_by_name( &$themes ) {
1182          if ( 0 === strpos( get_locale(), 'en_' ) ) {
1183              uasort( $themes, array( 'WP_Theme', '_name_sort' ) );
1184          } else {
1185              uasort( $themes, array( 'WP_Theme', '_name_sort_i18n' ) );
1186          }
1187      }
1188  
1189      /**
1190       * Callback function for usort() to naturally sort themes by name.
1191       *
1192       * Accesses the Name header directly from the class for maximum speed.
1193       * Would choke on HTML but we don't care enough to slow it down with strip_tags().
1194       *
1195       * @since 3.4.0
1196       * @access private
1197       */
1198  	private static function _name_sort( $a, $b ) {
1199          return strnatcasecmp( $a->headers['Name'], $b->headers['Name'] );
1200      }
1201  
1202      /**
1203       * Name sort (with translation).
1204       *
1205       * @since 3.4.0
1206       * @access private
1207       */
1208  	private static function _name_sort_i18n( $a, $b ) {
1209          // Don't mark up; Do translate.
1210          return strnatcasecmp( $a->display( 'Name', false, true ), $b->display( 'Name', false, true ) );
1211      }
1212  }


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