wp/wp-includes/class-wp-theme.php
changeset 9 177826044cd9
parent 7 cf61fcea0001
child 16 a86126ab1dd4
equal deleted inserted replaced
8:c7c34916027a 9:177826044cd9
    19 	public $update = false;
    19 	public $update = false;
    20 
    20 
    21 	/**
    21 	/**
    22 	 * Headers for style.css files.
    22 	 * Headers for style.css files.
    23 	 *
    23 	 *
    24 	 * @static
       
    25 	 * @var array
    24 	 * @var array
    26 	 */
    25 	 */
    27 	private static $file_headers = array(
    26 	private static $file_headers = array(
    28 		'Name'        => 'Theme Name',
    27 		'Name'        => 'Theme Name',
    29 		'ThemeURI'    => 'Theme URI',
    28 		'ThemeURI'    => 'Theme URI',
    39 	);
    38 	);
    40 
    39 
    41 	/**
    40 	/**
    42 	 * Default themes.
    41 	 * Default themes.
    43 	 *
    42 	 *
    44 	 * @static
       
    45 	 * @var array
    43 	 * @var array
    46 	 */
    44 	 */
    47 	private static $default_themes = array(
    45 	private static $default_themes = array(
    48 		'classic'         => 'WordPress Classic',
    46 		'classic'         => 'WordPress Classic',
    49 		'default'         => 'WordPress Default',
    47 		'default'         => 'WordPress Default',
    53 		'twentythirteen'  => 'Twenty Thirteen',
    51 		'twentythirteen'  => 'Twenty Thirteen',
    54 		'twentyfourteen'  => 'Twenty Fourteen',
    52 		'twentyfourteen'  => 'Twenty Fourteen',
    55 		'twentyfifteen'   => 'Twenty Fifteen',
    53 		'twentyfifteen'   => 'Twenty Fifteen',
    56 		'twentysixteen'   => 'Twenty Sixteen',
    54 		'twentysixteen'   => 'Twenty Sixteen',
    57 		'twentyseventeen' => 'Twenty Seventeen',
    55 		'twentyseventeen' => 'Twenty Seventeen',
       
    56 		'twentynineteen'  => 'Twenty Nineteen',
    58 	);
    57 	);
    59 
    58 
    60 	/**
    59 	/**
    61 	 * Renamed theme tags.
    60 	 * Renamed theme tags.
    62 	 *
    61 	 *
    63 	 * @static
       
    64 	 * @var array
    62 	 * @var array
    65 	 */
    63 	 */
    66 	private static $tag_map = array(
    64 	private static $tag_map = array(
    67 		'fixed-width'    => 'fixed-layout',
    65 		'fixed-width'    => 'fixed-layout',
    68 		'flexible-width' => 'fluid-layout',
    66 		'flexible-width' => 'fluid-layout',
   156 	/**
   154 	/**
   157 	 * Flag for whether the themes cache bucket should be persistently cached.
   155 	 * Flag for whether the themes cache bucket should be persistently cached.
   158 	 *
   156 	 *
   159 	 * Default is false. Can be set with the {@see 'wp_cache_themes_persistently'} filter.
   157 	 * Default is false. Can be set with the {@see 'wp_cache_themes_persistently'} filter.
   160 	 *
   158 	 *
   161 	 * @static
       
   162 	 * @var bool
   159 	 * @var bool
   163 	 */
   160 	 */
   164 	private static $persistently_cache;
   161 	private static $persistently_cache;
   165 
   162 
   166 	/**
   163 	/**
   167 	 * Expiration time for the themes cache bucket.
   164 	 * Expiration time for the themes cache bucket.
   168 	 *
   165 	 *
   169 	 * By default the bucket is not cached, so this value is useless.
   166 	 * By default the bucket is not cached, so this value is useless.
   170 	 *
   167 	 *
   171 	 * @static
       
   172 	 * @var bool
   168 	 * @var bool
   173 	 */
   169 	 */
   174 	private static $cache_expiration = 1800;
   170 	private static $cache_expiration = 1800;
   175 
   171 
   176 	/**
   172 	/**
   191 		if ( ! isset( self::$persistently_cache ) ) {
   187 		if ( ! isset( self::$persistently_cache ) ) {
   192 			/** This action is documented in wp-includes/theme.php */
   188 			/** This action is documented in wp-includes/theme.php */
   193 			self::$persistently_cache = apply_filters( 'wp_cache_themes_persistently', false, 'WP_Theme' );
   189 			self::$persistently_cache = apply_filters( 'wp_cache_themes_persistently', false, 'WP_Theme' );
   194 			if ( self::$persistently_cache ) {
   190 			if ( self::$persistently_cache ) {
   195 				wp_cache_add_global_groups( 'themes' );
   191 				wp_cache_add_global_groups( 'themes' );
   196 				if ( is_int( self::$persistently_cache ) )
   192 				if ( is_int( self::$persistently_cache ) ) {
   197 					self::$cache_expiration = self::$persistently_cache;
   193 					self::$cache_expiration = self::$persistently_cache;
       
   194 				}
   198 			} else {
   195 			} else {
   199 				wp_cache_add_non_persistent_groups( 'themes' );
   196 				wp_cache_add_non_persistent_groups( 'themes' );
   200 			}
   197 			}
   201 		}
   198 		}
   202 
   199 
   208 			$this->stylesheet = basename( $this->theme_root ) . '/' . $this->stylesheet;
   205 			$this->stylesheet = basename( $this->theme_root ) . '/' . $this->stylesheet;
   209 			$this->theme_root = dirname( $theme_root );
   206 			$this->theme_root = dirname( $theme_root );
   210 		}
   207 		}
   211 
   208 
   212 		$this->cache_hash = md5( $this->theme_root . '/' . $this->stylesheet );
   209 		$this->cache_hash = md5( $this->theme_root . '/' . $this->stylesheet );
   213 		$theme_file = $this->stylesheet . '/style.css';
   210 		$theme_file       = $this->stylesheet . '/style.css';
   214 
   211 
   215 		$cache = $this->cache_get( 'theme' );
   212 		$cache = $this->cache_get( 'theme' );
   216 
   213 
   217 		if ( is_array( $cache ) ) {
   214 		if ( is_array( $cache ) ) {
   218 			foreach ( array( 'errors', 'headers', 'template' ) as $key ) {
   215 			foreach ( array( 'errors', 'headers', 'template' ) as $key ) {
   219 				if ( isset( $cache[ $key ] ) )
   216 				if ( isset( $cache[ $key ] ) ) {
   220 					$this->$key = $cache[ $key ];
   217 					$this->$key = $cache[ $key ];
   221 			}
   218 				}
   222 			if ( $this->errors )
   219 			}
       
   220 			if ( $this->errors ) {
   223 				return;
   221 				return;
   224 			if ( isset( $cache['theme_root_template'] ) )
   222 			}
       
   223 			if ( isset( $cache['theme_root_template'] ) ) {
   225 				$theme_root_template = $cache['theme_root_template'];
   224 				$theme_root_template = $cache['theme_root_template'];
       
   225 			}
   226 		} elseif ( ! file_exists( $this->theme_root . '/' . $theme_file ) ) {
   226 		} elseif ( ! file_exists( $this->theme_root . '/' . $theme_file ) ) {
   227 			$this->headers['Name'] = $this->stylesheet;
   227 			$this->headers['Name'] = $this->stylesheet;
   228 			if ( ! file_exists( $this->theme_root . '/' . $this->stylesheet ) )
   228 			if ( ! file_exists( $this->theme_root . '/' . $this->stylesheet ) ) {
   229 				$this->errors = new WP_Error( 'theme_not_found', sprintf( __( 'The theme directory "%s" does not exist.' ), esc_html( $this->stylesheet ) ) );
   229 				$this->errors = new WP_Error( 'theme_not_found', sprintf( __( 'The theme directory "%s" does not exist.' ), esc_html( $this->stylesheet ) ) );
   230 			else
   230 			} else {
   231 				$this->errors = new WP_Error( 'theme_no_stylesheet', __( 'Stylesheet is missing.' ) );
   231 				$this->errors = new WP_Error( 'theme_no_stylesheet', __( 'Stylesheet is missing.' ) );
       
   232 			}
   232 			$this->template = $this->stylesheet;
   233 			$this->template = $this->stylesheet;
   233 			$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
   234 			$this->cache_add(
   234 			if ( ! file_exists( $this->theme_root ) ) // Don't cache this one.
   235 				'theme',
       
   236 				array(
       
   237 					'headers'    => $this->headers,
       
   238 					'errors'     => $this->errors,
       
   239 					'stylesheet' => $this->stylesheet,
       
   240 					'template'   => $this->template,
       
   241 				)
       
   242 			);
       
   243 			if ( ! file_exists( $this->theme_root ) ) { // Don't cache this one.
   235 				$this->errors->add( 'theme_root_missing', __( 'ERROR: The themes directory is either empty or doesn’t exist. Please check your installation.' ) );
   244 				$this->errors->add( 'theme_root_missing', __( 'ERROR: The themes directory is either empty or doesn’t exist. Please check your installation.' ) );
       
   245 			}
   236 			return;
   246 			return;
   237 		} elseif ( ! is_readable( $this->theme_root . '/' . $theme_file ) ) {
   247 		} elseif ( ! is_readable( $this->theme_root . '/' . $theme_file ) ) {
   238 			$this->headers['Name'] = $this->stylesheet;
   248 			$this->headers['Name'] = $this->stylesheet;
   239 			$this->errors = new WP_Error( 'theme_stylesheet_not_readable', __( 'Stylesheet is not readable.' ) );
   249 			$this->errors          = new WP_Error( 'theme_stylesheet_not_readable', __( 'Stylesheet is not readable.' ) );
   240 			$this->template = $this->stylesheet;
   250 			$this->template        = $this->stylesheet;
   241 			$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
   251 			$this->cache_add(
       
   252 				'theme',
       
   253 				array(
       
   254 					'headers'    => $this->headers,
       
   255 					'errors'     => $this->errors,
       
   256 					'stylesheet' => $this->stylesheet,
       
   257 					'template'   => $this->template,
       
   258 				)
       
   259 			);
   242 			return;
   260 			return;
   243 		} else {
   261 		} else {
   244 			$this->headers = get_file_data( $this->theme_root . '/' . $theme_file, self::$file_headers, 'theme' );
   262 			$this->headers = get_file_data( $this->theme_root . '/' . $theme_file, self::$file_headers, 'theme' );
   245 			// Default themes always trump their pretenders.
   263 			// Default themes always trump their pretenders.
   246 			// Properly identify default themes that are inside a directory within wp-content/themes.
   264 			// Properly identify default themes that are inside a directory within wp-content/themes.
   247 			if ( $default_theme_slug = array_search( $this->headers['Name'], self::$default_themes ) ) {
   265 			if ( $default_theme_slug = array_search( $this->headers['Name'], self::$default_themes ) ) {
   248 				if ( basename( $this->stylesheet ) != $default_theme_slug )
   266 				if ( basename( $this->stylesheet ) != $default_theme_slug ) {
   249 					$this->headers['Name'] .= '/' . $this->stylesheet;
   267 					$this->headers['Name'] .= '/' . $this->stylesheet;
       
   268 				}
   250 			}
   269 			}
   251 		}
   270 		}
   252 
   271 
   253 		if ( ! $this->template && $this->stylesheet === $this->headers['Template'] ) {
   272 		if ( ! $this->template && $this->stylesheet === $this->headers['Template'] ) {
   254 			/* translators: %s: Template */
   273 			/* translators: %s: Template */
   255 			$this->errors = new WP_Error( 'theme_child_invalid', sprintf( __( 'The theme defines itself as its parent theme. Please check the %s header.' ), '<code>Template</code>' ) );
   274 			$this->errors = new WP_Error( 'theme_child_invalid', sprintf( __( 'The theme defines itself as its parent theme. Please check the %s header.' ), '<code>Template</code>' ) );
   256 			$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet ) );
   275 			$this->cache_add(
       
   276 				'theme',
       
   277 				array(
       
   278 					'headers'    => $this->headers,
       
   279 					'errors'     => $this->errors,
       
   280 					'stylesheet' => $this->stylesheet,
       
   281 				)
       
   282 			);
   257 
   283 
   258 			return;
   284 			return;
   259 		}
   285 		}
   260 
   286 
   261 		// (If template is set from cache [and there are no errors], we know it's good.)
   287 		// (If template is set from cache [and there are no errors], we know it's good.)
   262 		if ( ! $this->template && ! ( $this->template = $this->headers['Template'] ) ) {
   288 		if ( ! $this->template && ! ( $this->template = $this->headers['Template'] ) ) {
   263 			$this->template = $this->stylesheet;
   289 			$this->template = $this->stylesheet;
   264 			if ( ! file_exists( $this->theme_root . '/' . $this->stylesheet . '/index.php' ) ) {
   290 			if ( ! file_exists( $this->theme_root . '/' . $this->stylesheet . '/index.php' ) ) {
   265 				$error_message = sprintf(
   291 				$error_message = sprintf(
   266 					/* translators: 1: index.php, 2: Codex URL, 3: style.css */
   292 					/* translators: 1: index.php, 2: link to documentation, 3: style.css */
   267 					__( 'Template is missing. Standalone themes need to have a %1$s template file. <a href="%2$s">Child themes</a> need to have a Template header in the %3$s stylesheet.' ),
   293 					__( 'Template is missing. Standalone themes need to have a %1$s template file. <a href="%2$s">Child themes</a> need to have a Template header in the %3$s stylesheet.' ),
   268 					'<code>index.php</code>',
   294 					'<code>index.php</code>',
   269 					__( 'https://codex.wordpress.org/Child_Themes' ),
   295 					__( 'https://developer.wordpress.org/themes/advanced-topics/child-themes/' ),
   270 					'<code>style.css</code>'
   296 					'<code>style.css</code>'
   271 				);
   297 				);
   272 				$this->errors = new WP_Error( 'theme_no_index', $error_message );
   298 				$this->errors = new WP_Error( 'theme_no_index', $error_message );
   273 				$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
   299 				$this->cache_add(
       
   300 					'theme',
       
   301 					array(
       
   302 						'headers'    => $this->headers,
       
   303 						'errors'     => $this->errors,
       
   304 						'stylesheet' => $this->stylesheet,
       
   305 						'template'   => $this->template,
       
   306 					)
       
   307 				);
   274 				return;
   308 				return;
   275 			}
   309 			}
   276 		}
   310 		}
   277 
   311 
   278 		// If we got our data from cache, we can assume that 'template' is pointing to the right place.
   312 		// If we got our data from cache, we can assume that 'template' is pointing to the right place.
   287 				// We don't look into directories of themes, just the theme root.
   321 				// We don't look into directories of themes, just the theme root.
   288 				$theme_root_template = $directories[ $this->template ]['theme_root'];
   322 				$theme_root_template = $directories[ $this->template ]['theme_root'];
   289 			} else {
   323 			} else {
   290 				// Parent theme is missing.
   324 				// Parent theme is missing.
   291 				$this->errors = new WP_Error( 'theme_no_parent', sprintf( __( 'The parent theme is missing. Please install the "%s" parent theme.' ), esc_html( $this->template ) ) );
   325 				$this->errors = new WP_Error( 'theme_no_parent', sprintf( __( 'The parent theme is missing. Please install the "%s" parent theme.' ), esc_html( $this->template ) ) );
   292 				$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
   326 				$this->cache_add(
       
   327 					'theme',
       
   328 					array(
       
   329 						'headers'    => $this->headers,
       
   330 						'errors'     => $this->errors,
       
   331 						'stylesheet' => $this->stylesheet,
       
   332 						'template'   => $this->template,
       
   333 					)
       
   334 				);
   293 				$this->parent = new WP_Theme( $this->template, $this->theme_root, $this );
   335 				$this->parent = new WP_Theme( $this->template, $this->theme_root, $this );
   294 				return;
   336 				return;
   295 			}
   337 			}
   296 		}
   338 		}
   297 
   339 
   299 		if ( $this->template != $this->stylesheet ) {
   341 		if ( $this->template != $this->stylesheet ) {
   300 			// If we are a parent, then there is a problem. Only two generations allowed! Cancel things out.
   342 			// If we are a parent, then there is a problem. Only two generations allowed! Cancel things out.
   301 			if ( $_child instanceof WP_Theme && $_child->template == $this->stylesheet ) {
   343 			if ( $_child instanceof WP_Theme && $_child->template == $this->stylesheet ) {
   302 				$_child->parent = null;
   344 				$_child->parent = null;
   303 				$_child->errors = new WP_Error( 'theme_parent_invalid', sprintf( __( 'The "%s" theme is not a valid parent theme.' ), esc_html( $_child->template ) ) );
   345 				$_child->errors = new WP_Error( 'theme_parent_invalid', sprintf( __( 'The "%s" theme is not a valid parent theme.' ), esc_html( $_child->template ) ) );
   304 				$_child->cache_add( 'theme', array( 'headers' => $_child->headers, 'errors' => $_child->errors, 'stylesheet' => $_child->stylesheet, 'template' => $_child->template ) );
   346 				$_child->cache_add(
       
   347 					'theme',
       
   348 					array(
       
   349 						'headers'    => $_child->headers,
       
   350 						'errors'     => $_child->errors,
       
   351 						'stylesheet' => $_child->stylesheet,
       
   352 						'template'   => $_child->template,
       
   353 					)
       
   354 				);
   305 				// The two themes actually reference each other with the Template header.
   355 				// The two themes actually reference each other with the Template header.
   306 				if ( $_child->stylesheet == $this->template ) {
   356 				if ( $_child->stylesheet == $this->template ) {
   307 					$this->errors = new WP_Error( 'theme_parent_invalid', sprintf( __( 'The "%s" theme is not a valid parent theme.' ), esc_html( $this->template ) ) );
   357 					$this->errors = new WP_Error( 'theme_parent_invalid', sprintf( __( 'The "%s" theme is not a valid parent theme.' ), esc_html( $this->template ) ) );
   308 					$this->cache_add( 'theme', array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template ) );
   358 					$this->cache_add(
       
   359 						'theme',
       
   360 						array(
       
   361 							'headers'    => $this->headers,
       
   362 							'errors'     => $this->errors,
       
   363 							'stylesheet' => $this->stylesheet,
       
   364 							'template'   => $this->template,
       
   365 						)
       
   366 					);
   309 				}
   367 				}
   310 				return;
   368 				return;
   311 			}
   369 			}
   312 			// Set the parent. Pass the current instance so we can do the crazy checks above and assess errors.
   370 			// Set the parent. Pass the current instance so we can do the crazy checks above and assess errors.
   313 			$this->parent = new WP_Theme( $this->template, isset( $theme_root_template ) ? $theme_root_template : $this->theme_root, $this );
   371 			$this->parent = new WP_Theme( $this->template, isset( $theme_root_template ) ? $theme_root_template : $this->theme_root, $this );
   314 		}
   372 		}
   315 
   373 
       
   374 		if ( wp_paused_themes()->get( $this->stylesheet ) && ( ! is_wp_error( $this->errors ) || ! isset( $this->errors->errors['theme_paused'] ) ) ) {
       
   375 			$this->errors = new WP_Error( 'theme_paused', __( 'This theme failed to load properly and was paused within the admin backend.' ) );
       
   376 		}
       
   377 
   316 		// We're good. If we didn't retrieve from cache, set it.
   378 		// We're good. If we didn't retrieve from cache, set it.
   317 		if ( ! is_array( $cache ) ) {
   379 		if ( ! is_array( $cache ) ) {
   318 			$cache = array( 'headers' => $this->headers, 'errors' => $this->errors, 'stylesheet' => $this->stylesheet, 'template' => $this->template );
   380 			$cache = array(
       
   381 				'headers'    => $this->headers,
       
   382 				'errors'     => $this->errors,
       
   383 				'stylesheet' => $this->stylesheet,
       
   384 				'template'   => $this->template,
       
   385 			);
   319 			// If the parent theme is in another root, we'll want to cache this. Avoids an entire branch of filesystem calls above.
   386 			// If the parent theme is in another root, we'll want to cache this. Avoids an entire branch of filesystem calls above.
   320 			if ( isset( $theme_root_template ) )
   387 			if ( isset( $theme_root_template ) ) {
   321 				$cache['theme_root_template'] = $theme_root_template;
   388 				$cache['theme_root_template'] = $theme_root_template;
       
   389 			}
   322 			$this->cache_add( 'theme', $cache );
   390 			$this->cache_add( 'theme', $cache );
   323 		}
   391 		}
   324 	}
   392 	}
   325 
   393 
   326 	/**
   394 	/**
   329 	 * @since  3.4.0
   397 	 * @since  3.4.0
   330 	 *
   398 	 *
   331 	 * @return string Theme name, ready for display (translated)
   399 	 * @return string Theme name, ready for display (translated)
   332 	 */
   400 	 */
   333 	public function __toString() {
   401 	public function __toString() {
   334 		return (string) $this->display('Name');
   402 		return (string) $this->display( 'Name' );
   335 	}
   403 	}
   336 
   404 
   337 	/**
   405 	/**
   338 	 * __isset() magic method for properties formerly returned by current_theme_info()
   406 	 * __isset() magic method for properties formerly returned by current_theme_info()
   339 	 *
   407 	 *
   344 	 * @param string $offset Property to check if set.
   412 	 * @param string $offset Property to check if set.
   345 	 * @return bool Whether the given property is set.
   413 	 * @return bool Whether the given property is set.
   346 	 */
   414 	 */
   347 	public function __isset( $offset ) {
   415 	public function __isset( $offset ) {
   348 		static $properties = array(
   416 		static $properties = array(
   349 			'name', 'title', 'version', 'parent_theme', 'template_dir', 'stylesheet_dir', 'template', 'stylesheet',
   417 			'name',
   350 			'screenshot', 'description', 'author', 'tags', 'theme_root', 'theme_root_uri',
   418 			'title',
       
   419 			'version',
       
   420 			'parent_theme',
       
   421 			'template_dir',
       
   422 			'stylesheet_dir',
       
   423 			'template',
       
   424 			'stylesheet',
       
   425 			'screenshot',
       
   426 			'description',
       
   427 			'author',
       
   428 			'tags',
       
   429 			'theme_root',
       
   430 			'theme_root_uri',
   351 		);
   431 		);
   352 
   432 
   353 		return in_array( $offset, $properties );
   433 		return in_array( $offset, $properties );
   354 	}
   434 	}
   355 
   435 
   361 	 * @param string $offset Property to get.
   441 	 * @param string $offset Property to get.
   362 	 * @return mixed Property value.
   442 	 * @return mixed Property value.
   363 	 */
   443 	 */
   364 	public function __get( $offset ) {
   444 	public function __get( $offset ) {
   365 		switch ( $offset ) {
   445 		switch ( $offset ) {
   366 			case 'name' :
   446 			case 'name':
   367 			case 'title' :
   447 			case 'title':
   368 				return $this->get('Name');
   448 				return $this->get( 'Name' );
   369 			case 'version' :
   449 			case 'version':
   370 				return $this->get('Version');
   450 				return $this->get( 'Version' );
   371 			case 'parent_theme' :
   451 			case 'parent_theme':
   372 				return $this->parent() ? $this->parent()->get('Name') : '';
   452 				return $this->parent() ? $this->parent()->get( 'Name' ) : '';
   373 			case 'template_dir' :
   453 			case 'template_dir':
   374 				return $this->get_template_directory();
   454 				return $this->get_template_directory();
   375 			case 'stylesheet_dir' :
   455 			case 'stylesheet_dir':
   376 				return $this->get_stylesheet_directory();
   456 				return $this->get_stylesheet_directory();
   377 			case 'template' :
   457 			case 'template':
   378 				return $this->get_template();
   458 				return $this->get_template();
   379 			case 'stylesheet' :
   459 			case 'stylesheet':
   380 				return $this->get_stylesheet();
   460 				return $this->get_stylesheet();
   381 			case 'screenshot' :
   461 			case 'screenshot':
   382 				return $this->get_screenshot( 'relative' );
   462 				return $this->get_screenshot( 'relative' );
   383 			// 'author' and 'description' did not previously return translated data.
   463 			// 'author' and 'description' did not previously return translated data.
   384 			case 'description' :
   464 			case 'description':
   385 				return $this->display('Description');
   465 				return $this->display( 'Description' );
   386 			case 'author' :
   466 			case 'author':
   387 				return $this->display('Author');
   467 				return $this->display( 'Author' );
   388 			case 'tags' :
   468 			case 'tags':
   389 				return $this->get( 'Tags' );
   469 				return $this->get( 'Tags' );
   390 			case 'theme_root' :
   470 			case 'theme_root':
   391 				return $this->get_theme_root();
   471 				return $this->get_theme_root();
   392 			case 'theme_root_uri' :
   472 			case 'theme_root_uri':
   393 				return $this->get_theme_root_uri();
   473 				return $this->get_theme_root_uri();
   394 			// For cases where the array was converted to an object.
   474 			// For cases where the array was converted to an object.
   395 			default :
   475 			default:
   396 				return $this->offsetGet( $offset );
   476 				return $this->offsetGet( $offset );
   397 		}
   477 		}
   398 	}
   478 	}
   399 
   479 
   400 	/**
   480 	/**
   426 	 * @param mixed $offset
   506 	 * @param mixed $offset
   427 	 * @return bool
   507 	 * @return bool
   428 	 */
   508 	 */
   429 	public function offsetExists( $offset ) {
   509 	public function offsetExists( $offset ) {
   430 		static $keys = array(
   510 		static $keys = array(
   431 			'Name', 'Version', 'Status', 'Title', 'Author', 'Author Name', 'Author URI', 'Description',
   511 			'Name',
   432 			'Template', 'Stylesheet', 'Template Files', 'Stylesheet Files', 'Template Dir', 'Stylesheet Dir',
   512 			'Version',
   433 			'Screenshot', 'Tags', 'Theme Root', 'Theme Root URI', 'Parent Theme',
   513 			'Status',
       
   514 			'Title',
       
   515 			'Author',
       
   516 			'Author Name',
       
   517 			'Author URI',
       
   518 			'Description',
       
   519 			'Template',
       
   520 			'Stylesheet',
       
   521 			'Template Files',
       
   522 			'Stylesheet Files',
       
   523 			'Template Dir',
       
   524 			'Stylesheet Dir',
       
   525 			'Screenshot',
       
   526 			'Tags',
       
   527 			'Theme Root',
       
   528 			'Theme Root URI',
       
   529 			'Parent Theme',
   434 		);
   530 		);
   435 
   531 
   436 		return in_array( $offset, $keys );
   532 		return in_array( $offset, $keys );
   437 	}
   533 	}
   438 
   534 
   451 	 * @param mixed $offset
   547 	 * @param mixed $offset
   452 	 * @return mixed
   548 	 * @return mixed
   453 	 */
   549 	 */
   454 	public function offsetGet( $offset ) {
   550 	public function offsetGet( $offset ) {
   455 		switch ( $offset ) {
   551 		switch ( $offset ) {
   456 			case 'Name' :
   552 			case 'Name':
   457 			case 'Title' :
   553 			case 'Title':
   458 				/*
   554 				/*
   459 				 * See note above about using translated data. get() is not ideal.
   555 				 * See note above about using translated data. get() is not ideal.
   460 				 * It is only for backward compatibility. Use display().
   556 				 * It is only for backward compatibility. Use display().
   461 				 */
   557 				 */
   462 				return $this->get('Name');
   558 				return $this->get( 'Name' );
   463 			case 'Author' :
   559 			case 'Author':
   464 				return $this->display( 'Author');
   560 				return $this->display( 'Author' );
   465 			case 'Author Name' :
   561 			case 'Author Name':
   466 				return $this->display( 'Author', false);
   562 				return $this->display( 'Author', false );
   467 			case 'Author URI' :
   563 			case 'Author URI':
   468 				return $this->display('AuthorURI');
   564 				return $this->display( 'AuthorURI' );
   469 			case 'Description' :
   565 			case 'Description':
   470 				return $this->display( 'Description');
   566 				return $this->display( 'Description' );
   471 			case 'Version' :
   567 			case 'Version':
   472 			case 'Status' :
   568 			case 'Status':
   473 				return $this->get( $offset );
   569 				return $this->get( $offset );
   474 			case 'Template' :
   570 			case 'Template':
   475 				return $this->get_template();
   571 				return $this->get_template();
   476 			case 'Stylesheet' :
   572 			case 'Stylesheet':
   477 				return $this->get_stylesheet();
   573 				return $this->get_stylesheet();
   478 			case 'Template Files' :
   574 			case 'Template Files':
   479 				return $this->get_files( 'php', 1, true );
   575 				return $this->get_files( 'php', 1, true );
   480 			case 'Stylesheet Files' :
   576 			case 'Stylesheet Files':
   481 				return $this->get_files( 'css', 0, false );
   577 				return $this->get_files( 'css', 0, false );
   482 			case 'Template Dir' :
   578 			case 'Template Dir':
   483 				return $this->get_template_directory();
   579 				return $this->get_template_directory();
   484 			case 'Stylesheet Dir' :
   580 			case 'Stylesheet Dir':
   485 				return $this->get_stylesheet_directory();
   581 				return $this->get_stylesheet_directory();
   486 			case 'Screenshot' :
   582 			case 'Screenshot':
   487 				return $this->get_screenshot( 'relative' );
   583 				return $this->get_screenshot( 'relative' );
   488 			case 'Tags' :
   584 			case 'Tags':
   489 				return $this->get('Tags');
   585 				return $this->get( 'Tags' );
   490 			case 'Theme Root' :
   586 			case 'Theme Root':
   491 				return $this->get_theme_root();
   587 				return $this->get_theme_root();
   492 			case 'Theme Root URI' :
   588 			case 'Theme Root URI':
   493 				return $this->get_theme_root_uri();
   589 				return $this->get_theme_root_uri();
   494 			case 'Parent Theme' :
   590 			case 'Parent Theme':
   495 				return $this->parent() ? $this->parent()->get('Name') : '';
   591 				return $this->parent() ? $this->parent()->get( 'Name' ) : '';
   496 			default :
   592 			default:
   497 				return null;
   593 				return null;
   498 		}
   594 		}
   499 	}
   595 	}
   500 
   596 
   501 	/**
   597 	/**
   567 	 * Clears the cache for the theme.
   663 	 * Clears the cache for the theme.
   568 	 *
   664 	 *
   569 	 * @since 3.4.0
   665 	 * @since 3.4.0
   570 	 */
   666 	 */
   571 	public function cache_delete() {
   667 	public function cache_delete() {
   572 		foreach ( array( 'theme', 'screenshot', 'headers', 'post_templates' ) as $key )
   668 		foreach ( array( 'theme', 'screenshot', 'headers', 'post_templates' ) as $key ) {
   573 			wp_cache_delete( $key . '-' . $this->cache_hash, 'themes' );
   669 			wp_cache_delete( $key . '-' . $this->cache_hash, 'themes' );
       
   670 		}
   574 		$this->template = $this->textdomain_loaded = $this->theme_root_uri = $this->parent = $this->errors = $this->headers_sanitized = $this->name_translated = null;
   671 		$this->template = $this->textdomain_loaded = $this->theme_root_uri = $this->parent = $this->errors = $this->headers_sanitized = $this->name_translated = null;
   575 		$this->headers = array();
   672 		$this->headers  = array();
   576 		$this->__construct( $this->stylesheet, $this->theme_root );
   673 		$this->__construct( $this->stylesheet, $this->theme_root );
   577 	}
   674 	}
   578 
   675 
   579 	/**
   676 	/**
   580 	 * Get a raw, unformatted theme header.
   677 	 * Get a raw, unformatted theme header.
   591 	 *
   688 	 *
   592 	 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags.
   689 	 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags.
   593 	 * @return string|false String on success, false on failure.
   690 	 * @return string|false String on success, false on failure.
   594 	 */
   691 	 */
   595 	public function get( $header ) {
   692 	public function get( $header ) {
   596 		if ( ! isset( $this->headers[ $header ] ) )
   693 		if ( ! isset( $this->headers[ $header ] ) ) {
   597 			return false;
   694 			return false;
       
   695 		}
   598 
   696 
   599 		if ( ! isset( $this->headers_sanitized ) ) {
   697 		if ( ! isset( $this->headers_sanitized ) ) {
   600 			$this->headers_sanitized = $this->cache_get( 'headers' );
   698 			$this->headers_sanitized = $this->cache_get( 'headers' );
   601 			if ( ! is_array( $this->headers_sanitized ) )
   699 			if ( ! is_array( $this->headers_sanitized ) ) {
   602 				$this->headers_sanitized = array();
   700 				$this->headers_sanitized = array();
   603 		}
   701 			}
   604 
   702 		}
   605 		if ( isset( $this->headers_sanitized[ $header ] ) )
   703 
       
   704 		if ( isset( $this->headers_sanitized[ $header ] ) ) {
   606 			return $this->headers_sanitized[ $header ];
   705 			return $this->headers_sanitized[ $header ];
       
   706 		}
   607 
   707 
   608 		// If themes are a persistent group, sanitize everything and cache it. One cache add is better than many cache sets.
   708 		// If themes are a persistent group, sanitize everything and cache it. One cache add is better than many cache sets.
   609 		if ( self::$persistently_cache ) {
   709 		if ( self::$persistently_cache ) {
   610 			foreach ( array_keys( $this->headers ) as $_header )
   710 			foreach ( array_keys( $this->headers ) as $_header ) {
   611 				$this->headers_sanitized[ $_header ] = $this->sanitize_header( $_header, $this->headers[ $_header ] );
   711 				$this->headers_sanitized[ $_header ] = $this->sanitize_header( $_header, $this->headers[ $_header ] );
       
   712 			}
   612 			$this->cache_add( 'headers', $this->headers_sanitized );
   713 			$this->cache_add( 'headers', $this->headers_sanitized );
   613 		} else {
   714 		} else {
   614 			$this->headers_sanitized[ $header ] = $this->sanitize_header( $header, $this->headers[ $header ] );
   715 			$this->headers_sanitized[ $header ] = $this->sanitize_header( $header, $this->headers[ $header ] );
   615 		}
   716 		}
   616 
   717 
   631 		$value = $this->get( $header );
   732 		$value = $this->get( $header );
   632 		if ( false === $value ) {
   733 		if ( false === $value ) {
   633 			return false;
   734 			return false;
   634 		}
   735 		}
   635 
   736 
   636 		if ( $translate && ( empty( $value ) || ! $this->load_textdomain() ) )
   737 		if ( $translate && ( empty( $value ) || ! $this->load_textdomain() ) ) {
   637 			$translate = false;
   738 			$translate = false;
   638 
   739 		}
   639 		if ( $translate )
   740 
       
   741 		if ( $translate ) {
   640 			$value = $this->translate_header( $header, $value );
   742 			$value = $this->translate_header( $header, $value );
   641 
   743 		}
   642 		if ( $markup )
   744 
       
   745 		if ( $markup ) {
   643 			$value = $this->markup_header( $header, $value, $translate );
   746 			$value = $this->markup_header( $header, $value, $translate );
       
   747 		}
   644 
   748 
   645 		return $value;
   749 		return $value;
   646 	}
   750 	}
   647 
   751 
   648 	/**
   752 	/**
   657 	 * @param string $value Value to sanitize.
   761 	 * @param string $value Value to sanitize.
   658 	 * @return mixed
   762 	 * @return mixed
   659 	 */
   763 	 */
   660 	private function sanitize_header( $header, $value ) {
   764 	private function sanitize_header( $header, $value ) {
   661 		switch ( $header ) {
   765 		switch ( $header ) {
   662 			case 'Status' :
   766 			case 'Status':
   663 				if ( ! $value ) {
   767 				if ( ! $value ) {
   664 					$value = 'publish';
   768 					$value = 'publish';
   665 					break;
   769 					break;
   666 				}
   770 				}
   667 				// Fall through otherwise.
   771 				// Fall through otherwise.
   668 			case 'Name' :
   772 			case 'Name':
   669 				static $header_tags = array(
   773 				static $header_tags = array(
   670 					'abbr'    => array( 'title' => true ),
   774 					'abbr'    => array( 'title' => true ),
   671 					'acronym' => array( 'title' => true ),
   775 					'acronym' => array( 'title' => true ),
   672 					'code'    => true,
   776 					'code'    => true,
   673 					'em'      => true,
   777 					'em'      => true,
   674 					'strong'  => true,
   778 					'strong'  => true,
   675 				);
   779 				);
   676 				$value = wp_kses( $value, $header_tags );
   780 				$value              = wp_kses( $value, $header_tags );
   677 				break;
   781 				break;
   678 			case 'Author' :
   782 			case 'Author':
   679 				// There shouldn't be anchor tags in Author, but some themes like to be challenging.
   783 				// There shouldn't be anchor tags in Author, but some themes like to be challenging.
   680 			case 'Description' :
   784 			case 'Description':
   681 				static $header_tags_with_a = array(
   785 				static $header_tags_with_a = array(
   682 					'a'       => array( 'href' => true, 'title' => true ),
   786 					'a'       => array(
       
   787 						'href'  => true,
       
   788 						'title' => true,
       
   789 					),
   683 					'abbr'    => array( 'title' => true ),
   790 					'abbr'    => array( 'title' => true ),
   684 					'acronym' => array( 'title' => true ),
   791 					'acronym' => array( 'title' => true ),
   685 					'code'    => true,
   792 					'code'    => true,
   686 					'em'      => true,
   793 					'em'      => true,
   687 					'strong'  => true,
   794 					'strong'  => true,
   688 				);
   795 				);
   689 				$value = wp_kses( $value, $header_tags_with_a );
   796 				$value                     = wp_kses( $value, $header_tags_with_a );
   690 				break;
   797 				break;
   691 			case 'ThemeURI' :
   798 			case 'ThemeURI':
   692 			case 'AuthorURI' :
   799 			case 'AuthorURI':
   693 				$value = esc_url_raw( $value );
   800 				$value = esc_url_raw( $value );
   694 				break;
   801 				break;
   695 			case 'Tags' :
   802 			case 'Tags':
   696 				$value = array_filter( array_map( 'trim', explode( ',', strip_tags( $value ) ) ) );
   803 				$value = array_filter( array_map( 'trim', explode( ',', strip_tags( $value ) ) ) );
   697 				break;
   804 				break;
   698 			case 'Version' :
   805 			case 'Version':
   699 				$value = strip_tags( $value );
   806 				$value = strip_tags( $value );
   700 				break;
   807 				break;
   701 		}
   808 		}
   702 
   809 
   703 		return $value;
   810 		return $value;
   704 	}
   811 	}
   705 
   812 
   706 	/**
   813 	/**
   707 	 * Mark up a theme header.
   814 	 * Mark up a theme header.
   708 	 *
   815 	 *
   709      * @since 3.4.0
   816 	 * @since 3.4.0
   710 	 *
   817 	 *
   711 	 * @staticvar string $comma
   818 	 * @staticvar string $comma
   712 	 *
   819 	 *
   713 	 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags.
   820 	 * @param string $header Theme header. Name, Description, Author, Version, ThemeURI, AuthorURI, Status, Tags.
   714 	 * @param string $value Value to mark up.
   821 	 * @param string $value Value to mark up.
   715 	 * @param string $translate Whether the header has been translated.
   822 	 * @param string $translate Whether the header has been translated.
   716 	 * @return string Value, marked up.
   823 	 * @return string Value, marked up.
   717 	 */
   824 	 */
   718 	private function markup_header( $header, $value, $translate ) {
   825 	private function markup_header( $header, $value, $translate ) {
   719 		switch ( $header ) {
   826 		switch ( $header ) {
   720 			case 'Name' :
   827 			case 'Name':
   721 				if ( empty( $value ) ) {
   828 				if ( empty( $value ) ) {
   722 					$value = esc_html( $this->get_stylesheet() );
   829 					$value = esc_html( $this->get_stylesheet() );
   723 				}
   830 				}
   724 				break;
   831 				break;
   725 			case 'Description' :
   832 			case 'Description':
   726 				$value = wptexturize( $value );
   833 				$value = wptexturize( $value );
   727 				break;
   834 				break;
   728 			case 'Author' :
   835 			case 'Author':
   729 				if ( $this->get('AuthorURI') ) {
   836 				if ( $this->get( 'AuthorURI' ) ) {
   730 					$value = sprintf( '<a href="%1$s">%2$s</a>', $this->display( 'AuthorURI', true, $translate ), $value );
   837 					$value = sprintf( '<a href="%1$s">%2$s</a>', $this->display( 'AuthorURI', true, $translate ), $value );
   731 				} elseif ( ! $value ) {
   838 				} elseif ( ! $value ) {
   732 					$value = __( 'Anonymous' );
   839 					$value = __( 'Anonymous' );
   733 				}
   840 				}
   734 				break;
   841 				break;
   735 			case 'Tags' :
   842 			case 'Tags':
   736 				static $comma = null;
   843 				static $comma = null;
   737 				if ( ! isset( $comma ) ) {
   844 				if ( ! isset( $comma ) ) {
   738 					/* translators: used between list items, there is a space after the comma */
   845 					/* translators: used between list items, there is a space after the comma */
   739 					$comma = __( ', ' );
   846 					$comma = __( ', ' );
   740 				}
   847 				}
   741 				$value = implode( $comma, $value );
   848 				$value = implode( $comma, $value );
   742 				break;
   849 				break;
   743 			case 'ThemeURI' :
   850 			case 'ThemeURI':
   744 			case 'AuthorURI' :
   851 			case 'AuthorURI':
   745 				$value = esc_url( $value );
   852 				$value = esc_url( $value );
   746 				break;
   853 				break;
   747 		}
   854 		}
   748 
   855 
   749 		return $value;
   856 		return $value;
   760 	 * @param string $value Value to translate.
   867 	 * @param string $value Value to translate.
   761 	 * @return string Translated value.
   868 	 * @return string Translated value.
   762 	 */
   869 	 */
   763 	private function translate_header( $header, $value ) {
   870 	private function translate_header( $header, $value ) {
   764 		switch ( $header ) {
   871 		switch ( $header ) {
   765 			case 'Name' :
   872 			case 'Name':
   766 				// Cached for sorting reasons.
   873 				// Cached for sorting reasons.
   767 				if ( isset( $this->name_translated ) )
   874 				if ( isset( $this->name_translated ) ) {
   768 					return $this->name_translated;
   875 					return $this->name_translated;
   769 				$this->name_translated = translate( $value, $this->get('TextDomain' ) );
   876 				}
       
   877 				// phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain
       
   878 				$this->name_translated = translate( $value, $this->get( 'TextDomain' ) );
   770 				return $this->name_translated;
   879 				return $this->name_translated;
   771 			case 'Tags' :
   880 			case 'Tags':
   772 				if ( empty( $value ) || ! function_exists( 'get_theme_feature_list' ) ) {
   881 				if ( empty( $value ) || ! function_exists( 'get_theme_feature_list' ) ) {
   773 					return $value;
   882 					return $value;
   774 				}
   883 				}
   775 
   884 
   776 				static $tags_list;
   885 				static $tags_list;
   777 				if ( ! isset( $tags_list ) ) {
   886 				if ( ! isset( $tags_list ) ) {
   778 					$tags_list = array(
   887 					$tags_list = array(
   779 						// As of 4.6, deprecated tags which are only used to provide translation for older themes.
   888 						// As of 4.6, deprecated tags which are only used to provide translation for older themes.
   780 						'black' => __( 'Black' ), 'blue' => __( 'Blue' ), 'brown'  => __( 'Brown' ),
   889 						'black'             => __( 'Black' ),
   781 						'gray' => __( 'Gray' ), 'green'  => __( 'Green' ), 'orange' => __( 'Orange' ),
   890 						'blue'              => __( 'Blue' ),
   782 						'pink' => __( 'Pink' ), 'purple' => __( 'Purple' ), 'red' => __( 'Red' ),
   891 						'brown'             => __( 'Brown' ),
   783 						'silver' => __( 'Silver' ), 'tan' => __( 'Tan' ), 'white' => __( 'White' ),
   892 						'gray'              => __( 'Gray' ),
   784 						'yellow' => __( 'Yellow' ), 'dark' => __( 'Dark' ), 'light' => __( 'Light' ),
   893 						'green'             => __( 'Green' ),
   785 						'fixed-layout' => __( 'Fixed Layout' ), 'fluid-layout' => __( 'Fluid Layout' ),
   894 						'orange'            => __( 'Orange' ),
   786 						'responsive-layout' => __( 'Responsive Layout' ), 'blavatar' => __( 'Blavatar' ),
   895 						'pink'              => __( 'Pink' ),
   787 						'photoblogging' => __( 'Photoblogging' ), 'seasonal' => __( 'Seasonal' ),
   896 						'purple'            => __( 'Purple' ),
       
   897 						'red'               => __( 'Red' ),
       
   898 						'silver'            => __( 'Silver' ),
       
   899 						'tan'               => __( 'Tan' ),
       
   900 						'white'             => __( 'White' ),
       
   901 						'yellow'            => __( 'Yellow' ),
       
   902 						'dark'              => __( 'Dark' ),
       
   903 						'light'             => __( 'Light' ),
       
   904 						'fixed-layout'      => __( 'Fixed Layout' ),
       
   905 						'fluid-layout'      => __( 'Fluid Layout' ),
       
   906 						'responsive-layout' => __( 'Responsive Layout' ),
       
   907 						'blavatar'          => __( 'Blavatar' ),
       
   908 						'photoblogging'     => __( 'Photoblogging' ),
       
   909 						'seasonal'          => __( 'Seasonal' ),
   788 					);
   910 					);
   789 
   911 
   790 					$feature_list = get_theme_feature_list( false ); // No API
   912 					$feature_list = get_theme_feature_list( false ); // No API
   791 					foreach ( $feature_list as $tags ) {
   913 					foreach ( $feature_list as $tags ) {
   792 						$tags_list += $tags;
   914 						$tags_list += $tags;
   801 					}
   923 					}
   802 				}
   924 				}
   803 
   925 
   804 				return $value;
   926 				return $value;
   805 
   927 
   806 			default :
   928 			default:
   807 				$value = translate( $value, $this->get('TextDomain') );
   929 				// phpcs:ignore WordPress.WP.I18n.LowLevelTranslationFunction,WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain
       
   930 				$value = translate( $value, $this->get( 'TextDomain' ) );
   808 		}
   931 		}
   809 		return $value;
   932 		return $value;
   810 	}
   933 	}
   811 
   934 
   812 	/**
   935 	/**
   846 	 * @since 3.4.0
   969 	 * @since 3.4.0
   847 	 *
   970 	 *
   848 	 * @return string Absolute path of the stylesheet directory.
   971 	 * @return string Absolute path of the stylesheet directory.
   849 	 */
   972 	 */
   850 	public function get_stylesheet_directory() {
   973 	public function get_stylesheet_directory() {
   851 		if ( $this->errors() && in_array( 'theme_root_missing', $this->errors()->get_error_codes() ) )
   974 		if ( $this->errors() && in_array( 'theme_root_missing', $this->errors()->get_error_codes() ) ) {
   852 			return '';
   975 			return '';
       
   976 		}
   853 
   977 
   854 		return $this->theme_root . '/' . $this->stylesheet;
   978 		return $this->theme_root . '/' . $this->stylesheet;
   855 	}
   979 	}
   856 
   980 
   857 	/**
   981 	/**
   863 	 * @since 3.4.0
   987 	 * @since 3.4.0
   864 	 *
   988 	 *
   865 	 * @return string Absolute path of the template directory.
   989 	 * @return string Absolute path of the template directory.
   866 	 */
   990 	 */
   867 	public function get_template_directory() {
   991 	public function get_template_directory() {
   868 		if ( $this->parent() )
   992 		if ( $this->parent() ) {
   869 			$theme_root = $this->parent()->theme_root;
   993 			$theme_root = $this->parent()->theme_root;
   870 		else
   994 		} else {
   871 			$theme_root = $this->theme_root;
   995 			$theme_root = $this->theme_root;
       
   996 		}
   872 
   997 
   873 		return $theme_root . '/' . $this->template;
   998 		return $theme_root . '/' . $this->template;
   874 	}
   999 	}
   875 
  1000 
   876 	/**
  1001 	/**
   896 	 * @since 3.4.0
  1021 	 * @since 3.4.0
   897 	 *
  1022 	 *
   898 	 * @return string URL to the template directory.
  1023 	 * @return string URL to the template directory.
   899 	 */
  1024 	 */
   900 	public function get_template_directory_uri() {
  1025 	public function get_template_directory_uri() {
   901 		if ( $this->parent() )
  1026 		if ( $this->parent() ) {
   902 			$theme_root_uri = $this->parent()->get_theme_root_uri();
  1027 			$theme_root_uri = $this->parent()->get_theme_root_uri();
   903 		else
  1028 		} else {
   904 			$theme_root_uri = $this->get_theme_root_uri();
  1029 			$theme_root_uri = $this->get_theme_root_uri();
       
  1030 		}
   905 
  1031 
   906 		return $theme_root_uri . '/' . str_replace( '%2F', '/', rawurlencode( $this->template ) );
  1032 		return $theme_root_uri . '/' . str_replace( '%2F', '/', rawurlencode( $this->template ) );
   907 	}
  1033 	}
   908 
  1034 
   909 	/**
  1035 	/**
   929 	 * @since 3.4.0
  1055 	 * @since 3.4.0
   930 	 *
  1056 	 *
   931 	 * @return string Theme root URI.
  1057 	 * @return string Theme root URI.
   932 	 */
  1058 	 */
   933 	public function get_theme_root_uri() {
  1059 	public function get_theme_root_uri() {
   934 		if ( ! isset( $this->theme_root_uri ) )
  1060 		if ( ! isset( $this->theme_root_uri ) ) {
   935 			$this->theme_root_uri = get_theme_root_uri( $this->stylesheet, $this->theme_root );
  1061 			$this->theme_root_uri = get_theme_root_uri( $this->stylesheet, $this->theme_root );
       
  1062 		}
   936 		return $this->theme_root_uri;
  1063 		return $this->theme_root_uri;
   937 	}
  1064 	}
   938 
  1065 
   939 	/**
  1066 	/**
   940 	 * Returns the main screenshot file for the theme.
  1067 	 * Returns the main screenshot file for the theme.
   950 	 * @return string|false Screenshot file. False if the theme does not have a screenshot.
  1077 	 * @return string|false Screenshot file. False if the theme does not have a screenshot.
   951 	 */
  1078 	 */
   952 	public function get_screenshot( $uri = 'uri' ) {
  1079 	public function get_screenshot( $uri = 'uri' ) {
   953 		$screenshot = $this->cache_get( 'screenshot' );
  1080 		$screenshot = $this->cache_get( 'screenshot' );
   954 		if ( $screenshot ) {
  1081 		if ( $screenshot ) {
   955 			if ( 'relative' == $uri )
  1082 			if ( 'relative' == $uri ) {
   956 				return $screenshot;
  1083 				return $screenshot;
       
  1084 			}
   957 			return $this->get_stylesheet_directory_uri() . '/' . $screenshot;
  1085 			return $this->get_stylesheet_directory_uri() . '/' . $screenshot;
   958 		} elseif ( 0 === $screenshot ) {
  1086 		} elseif ( 0 === $screenshot ) {
   959 			return false;
  1087 			return false;
   960 		}
  1088 		}
   961 
  1089 
   962 		foreach ( array( 'png', 'gif', 'jpg', 'jpeg' ) as $ext ) {
  1090 		foreach ( array( 'png', 'gif', 'jpg', 'jpeg' ) as $ext ) {
   963 			if ( file_exists( $this->get_stylesheet_directory() . "/screenshot.$ext" ) ) {
  1091 			if ( file_exists( $this->get_stylesheet_directory() . "/screenshot.$ext" ) ) {
   964 				$this->cache_add( 'screenshot', 'screenshot.' . $ext );
  1092 				$this->cache_add( 'screenshot', 'screenshot.' . $ext );
   965 				if ( 'relative' == $uri )
  1093 				if ( 'relative' == $uri ) {
   966 					return 'screenshot.' . $ext;
  1094 					return 'screenshot.' . $ext;
       
  1095 				}
   967 				return $this->get_stylesheet_directory_uri() . '/' . 'screenshot.' . $ext;
  1096 				return $this->get_stylesheet_directory_uri() . '/' . 'screenshot.' . $ext;
   968 			}
  1097 			}
   969 		}
  1098 		}
   970 
  1099 
   971 		$this->cache_add( 'screenshot', 0 );
  1100 		$this->cache_add( 'screenshot', 0 );
  1010 		$post_templates = $this->cache_get( 'post_templates' );
  1139 		$post_templates = $this->cache_get( 'post_templates' );
  1011 
  1140 
  1012 		if ( ! is_array( $post_templates ) ) {
  1141 		if ( ! is_array( $post_templates ) ) {
  1013 			$post_templates = array();
  1142 			$post_templates = array();
  1014 
  1143 
  1015 			$files = (array) $this->get_files( 'php', 1, true);
  1144 			$files = (array) $this->get_files( 'php', 1, true );
  1016 
  1145 
  1017 			foreach ( $files as $file => $full_path ) {
  1146 			foreach ( $files as $file => $full_path ) {
  1018 				if ( ! preg_match( '|Template Name:(.*)$|mi', file_get_contents( $full_path ), $header ) ) {
  1147 				if ( ! preg_match( '|Template Name:(.*)$|mi', file_get_contents( $full_path ), $header ) ) {
  1019 					continue;
  1148 					continue;
  1020 				}
  1149 				}
  1087 		 *
  1216 		 *
  1088 		 * @since 3.9.0
  1217 		 * @since 3.9.0
  1089 		 * @since 4.4.0 Converted to allow complete control over the `$page_templates` array.
  1218 		 * @since 4.4.0 Converted to allow complete control over the `$page_templates` array.
  1090 		 * @since 4.7.0 Added the `$post_type` parameter.
  1219 		 * @since 4.7.0 Added the `$post_type` parameter.
  1091 		 *
  1220 		 *
  1092 		 * @param array        $post_templates Array of page templates. Keys are filenames,
  1221 		 * @param string[]     $post_templates Array of page templates. Keys are filenames,
  1093 		 *                                     values are translated names.
  1222 		 *                                     values are translated names.
  1094 		 * @param WP_Theme     $this           The theme object.
  1223 		 * @param WP_Theme     $this           The theme object.
  1095 		 * @param WP_Post|null $post           The post being edited, provided for context, or null.
  1224 		 * @param WP_Post|null $post           The post being edited, provided for context, or null.
  1096 		 * @param string       $post_type      Post type to get the templates for.
  1225 		 * @param string       $post_type      Post type to get the templates for.
  1097 		 */
  1226 		 */
  1102 
  1231 
  1103 	/**
  1232 	/**
  1104 	 * Scans a directory for files of a certain extension.
  1233 	 * Scans a directory for files of a certain extension.
  1105 	 *
  1234 	 *
  1106 	 * @since 3.4.0
  1235 	 * @since 3.4.0
  1107 	 *
       
  1108 	 * @static
       
  1109 	 *
  1236 	 *
  1110 	 * @param string            $path          Absolute path to search.
  1237 	 * @param string            $path          Absolute path to search.
  1111 	 * @param array|string|null $extensions    Optional. Array of extensions to find, string of a single extension,
  1238 	 * @param array|string|null $extensions    Optional. Array of extensions to find, string of a single extension,
  1112 	 *                                         or null for all extensions. Default null.
  1239 	 *                                         or null for all extensions. Default null.
  1113 	 * @param int               $depth         Optional. How many levels deep to search for files. Accepts 0, 1+, or
  1240 	 * @param int               $depth         Optional. How many levels deep to search for files. Accepts 0, 1+, or
  1122 		if ( ! is_dir( $path ) ) {
  1249 		if ( ! is_dir( $path ) ) {
  1123 			return false;
  1250 			return false;
  1124 		}
  1251 		}
  1125 
  1252 
  1126 		if ( $extensions ) {
  1253 		if ( $extensions ) {
  1127 			$extensions = (array) $extensions;
  1254 			$extensions  = (array) $extensions;
  1128 			$_extensions = implode( '|', $extensions );
  1255 			$_extensions = implode( '|', $extensions );
  1129 		}
  1256 		}
  1130 
  1257 
  1131 		$relative_path = trailingslashit( $relative_path );
  1258 		$relative_path = trailingslashit( $relative_path );
  1132 		if ( '/' == $relative_path ) {
  1259 		if ( '/' == $relative_path ) {
  1133 			$relative_path = '';
  1260 			$relative_path = '';
  1134 		}
  1261 		}
  1135 
  1262 
  1136 		$results = scandir( $path );
  1263 		$results = scandir( $path );
  1137 		$files = array();
  1264 		$files   = array();
  1138 
  1265 
  1139 		/**
  1266 		/**
  1140 		 * Filters the array of excluded directories and files while scanning theme folder.
  1267 		 * Filters the array of excluded directories and files while scanning theme folder.
  1141 		 *
  1268 		 *
  1142 		 * @since 4.7.4
  1269 		 * @since 4.7.4
  1143 		 *
  1270 		 *
  1144 		 * @param array $exclusions Array of excluded directories and files.
  1271 		 * @param string[] $exclusions Array of excluded directories and files.
  1145 		 */
  1272 		 */
  1146 		$exclusions = (array) apply_filters( 'theme_scandir_exclusions', array( 'CVS', 'node_modules', 'vendor', 'bower_components' ) );
  1273 		$exclusions = (array) apply_filters( 'theme_scandir_exclusions', array( 'CVS', 'node_modules', 'vendor', 'bower_components' ) );
  1147 
  1274 
  1148 		foreach ( $results as $result ) {
  1275 		foreach ( $results as $result ) {
  1149 			if ( '.' == $result[0] || in_array( $result, $exclusions, true ) ) {
  1276 			if ( '.' == $result[0] || in_array( $result, $exclusions, true ) ) {
  1151 			}
  1278 			}
  1152 			if ( is_dir( $path . '/' . $result ) ) {
  1279 			if ( is_dir( $path . '/' . $result ) ) {
  1153 				if ( ! $depth ) {
  1280 				if ( ! $depth ) {
  1154 					continue;
  1281 					continue;
  1155 				}
  1282 				}
  1156 				$found = self::scandir( $path . '/' . $result, $extensions, $depth - 1 , $relative_path . $result );
  1283 				$found = self::scandir( $path . '/' . $result, $extensions, $depth - 1, $relative_path . $result );
  1157 				$files = array_merge_recursive( $files, $found );
  1284 				$files = array_merge_recursive( $files, $found );
  1158 			} elseif ( ! $extensions || preg_match( '~\.(' . $_extensions . ')$~', $result ) ) {
  1285 			} elseif ( ! $extensions || preg_match( '~\.(' . $_extensions . ')$~', $result ) ) {
  1159 				$files[ $relative_path . $result ] = $path . '/' . $result;
  1286 				$files[ $relative_path . $result ] = $path . '/' . $result;
  1160 			}
  1287 			}
  1161 		}
  1288 		}
  1170 	 * child theme, it should probably try to load the parent theme's translations.
  1297 	 * child theme, it should probably try to load the parent theme's translations.
  1171 	 *
  1298 	 *
  1172 	 * @since 3.4.0
  1299 	 * @since 3.4.0
  1173 	 *
  1300 	 *
  1174 	 * @return bool True if the textdomain was successfully loaded or has already been loaded.
  1301 	 * @return bool True if the textdomain was successfully loaded or has already been loaded.
  1175 	 * 	False if no textdomain was specified in the file headers, or if the domain could not be loaded.
  1302 	 *  False if no textdomain was specified in the file headers, or if the domain could not be loaded.
  1176 	 */
  1303 	 */
  1177 	public function load_textdomain() {
  1304 	public function load_textdomain() {
  1178 		if ( isset( $this->textdomain_loaded ) )
  1305 		if ( isset( $this->textdomain_loaded ) ) {
  1179 			return $this->textdomain_loaded;
  1306 			return $this->textdomain_loaded;
  1180 
  1307 		}
  1181 		$textdomain = $this->get('TextDomain');
  1308 
       
  1309 		$textdomain = $this->get( 'TextDomain' );
  1182 		if ( ! $textdomain ) {
  1310 		if ( ! $textdomain ) {
  1183 			$this->textdomain_loaded = false;
  1311 			$this->textdomain_loaded = false;
  1184 			return false;
  1312 			return false;
  1185 		}
  1313 		}
  1186 
  1314 
  1188 			$this->textdomain_loaded = true;
  1316 			$this->textdomain_loaded = true;
  1189 			return true;
  1317 			return true;
  1190 		}
  1318 		}
  1191 
  1319 
  1192 		$path = $this->get_stylesheet_directory();
  1320 		$path = $this->get_stylesheet_directory();
  1193 		if ( $domainpath = $this->get('DomainPath') )
  1321 		if ( $domainpath = $this->get( 'DomainPath' ) ) {
  1194 			$path .= $domainpath;
  1322 			$path .= $domainpath;
  1195 		else
  1323 		} else {
  1196 			$path .= '/languages';
  1324 			$path .= '/languages';
       
  1325 		}
  1197 
  1326 
  1198 		$this->textdomain_loaded = load_theme_textdomain( $textdomain, $path );
  1327 		$this->textdomain_loaded = load_theme_textdomain( $textdomain, $path );
  1199 		return $this->textdomain_loaded;
  1328 		return $this->textdomain_loaded;
  1200 	}
  1329 	}
  1201 
  1330 
  1203 	 * Whether the theme is allowed (multisite only).
  1332 	 * Whether the theme is allowed (multisite only).
  1204 	 *
  1333 	 *
  1205 	 * @since 3.4.0
  1334 	 * @since 3.4.0
  1206 	 *
  1335 	 *
  1207 	 * @param string $check Optional. Whether to check only the 'network'-wide settings, the 'site'
  1336 	 * @param string $check Optional. Whether to check only the 'network'-wide settings, the 'site'
  1208 	 * 	settings, or 'both'. Defaults to 'both'.
  1337 	 *  settings, or 'both'. Defaults to 'both'.
  1209 	 * @param int $blog_id Optional. Ignored if only network-wide settings are checked. Defaults to current site.
  1338 	 * @param int $blog_id Optional. Ignored if only network-wide settings are checked. Defaults to current site.
  1210 	 * @return bool Whether the theme is allowed for the network. Returns true in single-site.
  1339 	 * @return bool Whether the theme is allowed for the network. Returns true in single-site.
  1211 	 */
  1340 	 */
  1212 	public function is_allowed( $check = 'both', $blog_id = null ) {
  1341 	public function is_allowed( $check = 'both', $blog_id = null ) {
  1213 		if ( ! is_multisite() )
  1342 		if ( ! is_multisite() ) {
  1214 			return true;
  1343 			return true;
       
  1344 		}
  1215 
  1345 
  1216 		if ( 'both' == $check || 'network' == $check ) {
  1346 		if ( 'both' == $check || 'network' == $check ) {
  1217 			$allowed = self::get_allowed_on_network();
  1347 			$allowed = self::get_allowed_on_network();
  1218 			if ( ! empty( $allowed[ $this->get_stylesheet() ] ) )
  1348 			if ( ! empty( $allowed[ $this->get_stylesheet() ] ) ) {
  1219 				return true;
  1349 				return true;
       
  1350 			}
  1220 		}
  1351 		}
  1221 
  1352 
  1222 		if ( 'both' == $check || 'site' == $check ) {
  1353 		if ( 'both' == $check || 'site' == $check ) {
  1223 			$allowed = self::get_allowed_on_site( $blog_id );
  1354 			$allowed = self::get_allowed_on_site( $blog_id );
  1224 			if ( ! empty( $allowed[ $this->get_stylesheet() ] ) )
  1355 			if ( ! empty( $allowed[ $this->get_stylesheet() ] ) ) {
  1225 				return true;
  1356 				return true;
       
  1357 			}
  1226 		}
  1358 		}
  1227 
  1359 
  1228 		return false;
  1360 		return false;
  1229 	}
  1361 	}
  1230 
  1362 
  1250 	/**
  1382 	/**
  1251 	 * Returns array of stylesheet names of themes allowed on the site or network.
  1383 	 * Returns array of stylesheet names of themes allowed on the site or network.
  1252 	 *
  1384 	 *
  1253 	 * @since 3.4.0
  1385 	 * @since 3.4.0
  1254 	 *
  1386 	 *
  1255 	 * @static
       
  1256 	 *
       
  1257 	 * @param int $blog_id Optional. ID of the site. Defaults to the current site.
  1387 	 * @param int $blog_id Optional. ID of the site. Defaults to the current site.
  1258 	 * @return array Array of stylesheet names.
  1388 	 * @return string[] Array of stylesheet names.
  1259 	 */
  1389 	 */
  1260 	public static function get_allowed( $blog_id = null ) {
  1390 	public static function get_allowed( $blog_id = null ) {
  1261 		/**
  1391 		/**
  1262 		 * Filters the array of themes allowed on the network.
  1392 		 * Filters the array of themes allowed on the network.
  1263 		 *
  1393 		 *
  1264 		 * Site is provided as context so that a list of network allowed themes can
  1394 		 * Site is provided as context so that a list of network allowed themes can
  1265 		 * be filtered further.
  1395 		 * be filtered further.
  1266 		 *
  1396 		 *
  1267 		 * @since 4.5.0
  1397 		 * @since 4.5.0
  1268 		 *
  1398 		 *
  1269 		 * @param array $allowed_themes An array of theme stylesheet names.
  1399 		 * @param string[] $allowed_themes An array of theme stylesheet names.
  1270 		 * @param int   $blog_id        ID of the site.
  1400 		 * @param int      $blog_id        ID of the site.
  1271 		 */
  1401 		 */
  1272 		$network = (array) apply_filters( 'network_allowed_themes', self::get_allowed_on_network(), $blog_id );
  1402 		$network = (array) apply_filters( 'network_allowed_themes', self::get_allowed_on_network(), $blog_id );
  1273 		return $network + self::get_allowed_on_site( $blog_id );
  1403 		return $network + self::get_allowed_on_site( $blog_id );
  1274 	}
  1404 	}
  1275 
  1405 
  1276 	/**
  1406 	/**
  1277 	 * Returns array of stylesheet names of themes allowed on the network.
  1407 	 * Returns array of stylesheet names of themes allowed on the network.
  1278 	 *
  1408 	 *
  1279 	 * @since 3.4.0
  1409 	 * @since 3.4.0
  1280 	 *
  1410 	 *
  1281 	 * @static
       
  1282 	 *
       
  1283 	 * @staticvar array $allowed_themes
  1411 	 * @staticvar array $allowed_themes
  1284 	 *
  1412 	 *
  1285 	 * @return array Array of stylesheet names.
  1413 	 * @return string[] Array of stylesheet names.
  1286 	 */
  1414 	 */
  1287 	public static function get_allowed_on_network() {
  1415 	public static function get_allowed_on_network() {
  1288 		static $allowed_themes;
  1416 		static $allowed_themes;
  1289 		if ( ! isset( $allowed_themes ) ) {
  1417 		if ( ! isset( $allowed_themes ) ) {
  1290 			$allowed_themes = (array) get_site_option( 'allowedthemes' );
  1418 			$allowed_themes = (array) get_site_option( 'allowedthemes' );
  1293 		/**
  1421 		/**
  1294 		 * Filters the array of themes allowed on the network.
  1422 		 * Filters the array of themes allowed on the network.
  1295 		 *
  1423 		 *
  1296 		 * @since MU (3.0.0)
  1424 		 * @since MU (3.0.0)
  1297 		 *
  1425 		 *
  1298 		 * @param array $allowed_themes An array of theme stylesheet names.
  1426 		 * @param string[] $allowed_themes An array of theme stylesheet names.
  1299 		 */
  1427 		 */
  1300 		$allowed_themes = apply_filters( 'allowed_themes', $allowed_themes );
  1428 		$allowed_themes = apply_filters( 'allowed_themes', $allowed_themes );
  1301 
  1429 
  1302 		return $allowed_themes;
  1430 		return $allowed_themes;
  1303 	}
  1431 	}
  1305 	/**
  1433 	/**
  1306 	 * Returns array of stylesheet names of themes allowed on the site.
  1434 	 * Returns array of stylesheet names of themes allowed on the site.
  1307 	 *
  1435 	 *
  1308 	 * @since 3.4.0
  1436 	 * @since 3.4.0
  1309 	 *
  1437 	 *
  1310 	 * @static
       
  1311 	 *
       
  1312 	 * @staticvar array $allowed_themes
  1438 	 * @staticvar array $allowed_themes
  1313 	 *
  1439 	 *
  1314 	 * @param int $blog_id Optional. ID of the site. Defaults to the current site.
  1440 	 * @param int $blog_id Optional. ID of the site. Defaults to the current site.
  1315 	 * @return array Array of stylesheet names.
  1441 	 * @return string[] Array of stylesheet names.
  1316 	 */
  1442 	 */
  1317 	public static function get_allowed_on_site( $blog_id = null ) {
  1443 	public static function get_allowed_on_site( $blog_id = null ) {
  1318 		static $allowed_themes = array();
  1444 		static $allowed_themes = array();
  1319 
  1445 
  1320 		if ( ! $blog_id || ! is_multisite() )
  1446 		if ( ! $blog_id || ! is_multisite() ) {
  1321 			$blog_id = get_current_blog_id();
  1447 			$blog_id = get_current_blog_id();
       
  1448 		}
  1322 
  1449 
  1323 		if ( isset( $allowed_themes[ $blog_id ] ) ) {
  1450 		if ( isset( $allowed_themes[ $blog_id ] ) ) {
  1324 			/**
  1451 			/**
  1325 			 * Filters the array of themes allowed on the site.
  1452 			 * Filters the array of themes allowed on the site.
  1326 			 *
  1453 			 *
  1327 			 * @since 4.5.0
  1454 			 * @since 4.5.0
  1328 			 *
  1455 			 *
  1329 			 * @param array $allowed_themes An array of theme stylesheet names.
  1456 			 * @param string[] $allowed_themes An array of theme stylesheet names.
  1330 			 * @param int   $blog_id        ID of the site. Defaults to current site.
  1457 			 * @param int      $blog_id        ID of the site. Defaults to current site.
  1331 			 */
  1458 			 */
  1332 			return (array) apply_filters( 'site_allowed_themes', $allowed_themes[ $blog_id ], $blog_id );
  1459 			return (array) apply_filters( 'site_allowed_themes', $allowed_themes[ $blog_id ], $blog_id );
  1333 		}
  1460 		}
  1334 
  1461 
  1335 		$current = $blog_id == get_current_blog_id();
  1462 		$current = $blog_id == get_current_blog_id();
  1355 
  1482 
  1356 			if ( ! is_array( $allowed_themes[ $blog_id ] ) || empty( $allowed_themes[ $blog_id ] ) ) {
  1483 			if ( ! is_array( $allowed_themes[ $blog_id ] ) || empty( $allowed_themes[ $blog_id ] ) ) {
  1357 				$allowed_themes[ $blog_id ] = array();
  1484 				$allowed_themes[ $blog_id ] = array();
  1358 			} else {
  1485 			} else {
  1359 				$converted = array();
  1486 				$converted = array();
  1360 				$themes = wp_get_themes();
  1487 				$themes    = wp_get_themes();
  1361 				foreach ( $themes as $stylesheet => $theme_data ) {
  1488 				foreach ( $themes as $stylesheet => $theme_data ) {
  1362 					if ( isset( $allowed_themes[ $blog_id ][ $theme_data->get('Name') ] ) )
  1489 					if ( isset( $allowed_themes[ $blog_id ][ $theme_data->get( 'Name' ) ] ) ) {
  1363 						$converted[ $stylesheet ] = true;
  1490 						$converted[ $stylesheet ] = true;
       
  1491 					}
  1364 				}
  1492 				}
  1365 				$allowed_themes[ $blog_id ] = $converted;
  1493 				$allowed_themes[ $blog_id ] = $converted;
  1366 			}
  1494 			}
  1367 			// Set the option so we never have to go through this pain again.
  1495 			// Set the option so we never have to go through this pain again.
  1368 			if ( is_admin() && $allowed_themes[ $blog_id ] ) {
  1496 			if ( is_admin() && $allowed_themes[ $blog_id ] ) {
  1384 
  1512 
  1385 	/**
  1513 	/**
  1386 	 * Enables a theme for all sites on the current network.
  1514 	 * Enables a theme for all sites on the current network.
  1387 	 *
  1515 	 *
  1388 	 * @since 4.6.0
  1516 	 * @since 4.6.0
  1389 	 * @static
  1517 	 *
  1390 	 *
  1518 	 * @param string|string[] $stylesheets Stylesheet name or array of stylesheet names.
  1391 	 * @param string|array $stylesheets Stylesheet name or array of stylesheet names.
       
  1392 	 */
  1519 	 */
  1393 	public static function network_enable_theme( $stylesheets ) {
  1520 	public static function network_enable_theme( $stylesheets ) {
  1394 		if ( ! is_multisite() ) {
  1521 		if ( ! is_multisite() ) {
  1395 			return;
  1522 			return;
  1396 		}
  1523 		}
  1409 
  1536 
  1410 	/**
  1537 	/**
  1411 	 * Disables a theme for all sites on the current network.
  1538 	 * Disables a theme for all sites on the current network.
  1412 	 *
  1539 	 *
  1413 	 * @since 4.6.0
  1540 	 * @since 4.6.0
  1414 	 * @static
  1541 	 *
  1415 	 *
  1542 	 * @param string|string[] $stylesheets Stylesheet name or array of stylesheet names.
  1416 	 * @param string|array $stylesheets Stylesheet name or array of stylesheet names.
       
  1417 	 */
  1543 	 */
  1418 	public static function network_disable_theme( $stylesheets ) {
  1544 	public static function network_disable_theme( $stylesheets ) {
  1419 		if ( ! is_multisite() ) {
  1545 		if ( ! is_multisite() ) {
  1420 			return;
  1546 			return;
  1421 		}
  1547 		}
  1437 	/**
  1563 	/**
  1438 	 * Sorts themes by name.
  1564 	 * Sorts themes by name.
  1439 	 *
  1565 	 *
  1440 	 * @since 3.4.0
  1566 	 * @since 3.4.0
  1441 	 *
  1567 	 *
  1442 	 * @static
  1568 	 * @param WP_Theme[] $themes Array of theme objects to sort (passed by reference).
  1443 	 *
       
  1444 	 * @param array $themes Array of themes to sort (passed by reference).
       
  1445 	 */
  1569 	 */
  1446 	public static function sort_by_name( &$themes ) {
  1570 	public static function sort_by_name( &$themes ) {
  1447 		if ( 0 === strpos( get_user_locale(), 'en_' ) ) {
  1571 		if ( 0 === strpos( get_user_locale(), 'en_' ) ) {
  1448 			uasort( $themes, array( 'WP_Theme', '_name_sort' ) );
  1572 			uasort( $themes, array( 'WP_Theme', '_name_sort' ) );
  1449 		} else {
  1573 		} else {
       
  1574 			foreach ( $themes as $key => $theme ) {
       
  1575 				$theme->translate_header( 'Name', $theme->headers['Name'] );
       
  1576 			}
  1450 			uasort( $themes, array( 'WP_Theme', '_name_sort_i18n' ) );
  1577 			uasort( $themes, array( 'WP_Theme', '_name_sort_i18n' ) );
  1451 		}
  1578 		}
  1452 	}
  1579 	}
  1453 
  1580 
  1454 	/**
  1581 	/**
  1456 	 *
  1583 	 *
  1457 	 * Accesses the Name header directly from the class for maximum speed.
  1584 	 * Accesses the Name header directly from the class for maximum speed.
  1458 	 * Would choke on HTML but we don't care enough to slow it down with strip_tags().
  1585 	 * Would choke on HTML but we don't care enough to slow it down with strip_tags().
  1459 	 *
  1586 	 *
  1460 	 * @since 3.4.0
  1587 	 * @since 3.4.0
  1461 	 *
       
  1462 	 * @static
       
  1463 	 *
  1588 	 *
  1464 	 * @param string $a First name.
  1589 	 * @param string $a First name.
  1465 	 * @param string $b Second name.
  1590 	 * @param string $b Second name.
  1466 	 * @return int Negative if `$a` falls lower in the natural order than `$b`. Zero if they fall equally.
  1591 	 * @return int Negative if `$a` falls lower in the natural order than `$b`. Zero if they fall equally.
  1467 	 *             Greater than 0 if `$a` falls higher in the natural order than `$b`. Used with usort().
  1592 	 *             Greater than 0 if `$a` falls higher in the natural order than `$b`. Used with usort().
  1469 	private static function _name_sort( $a, $b ) {
  1594 	private static function _name_sort( $a, $b ) {
  1470 		return strnatcasecmp( $a->headers['Name'], $b->headers['Name'] );
  1595 		return strnatcasecmp( $a->headers['Name'], $b->headers['Name'] );
  1471 	}
  1596 	}
  1472 
  1597 
  1473 	/**
  1598 	/**
  1474 	 * Name sort (with translation).
  1599 	 * Callback function for usort() to naturally sort themes by translated name.
  1475 	 *
  1600 	 *
  1476 	 * @since 3.4.0
  1601 	 * @since 3.4.0
  1477 	 *
       
  1478 	 * @static
       
  1479 	 *
  1602 	 *
  1480 	 * @param string $a First name.
  1603 	 * @param string $a First name.
  1481 	 * @param string $b Second name.
  1604 	 * @param string $b Second name.
  1482 	 * @return int Negative if `$a` falls lower in the natural order than `$b`. Zero if they fall equally.
  1605 	 * @return int Negative if `$a` falls lower in the natural order than `$b`. Zero if they fall equally.
  1483 	 *             Greater than 0 if `$a` falls higher in the natural order than `$b`. Used with usort().
  1606 	 *             Greater than 0 if `$a` falls higher in the natural order than `$b`. Used with usort().
  1484 	 */
  1607 	 */
  1485 	private static function _name_sort_i18n( $a, $b ) {
  1608 	private static function _name_sort_i18n( $a, $b ) {
  1486 		// Don't mark up; Do translate.
  1609 		return strnatcasecmp( $a->name_translated, $b->name_translated );
  1487 		return strnatcasecmp( $a->display( 'Name', false, true ), $b->display( 'Name', false, true ) );
       
  1488 	}
  1610 	}
  1489 }
  1611 }