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