wp/wp-admin/includes/theme.php
changeset 9 177826044cd9
parent 7 cf61fcea0001
child 16 a86126ab1dd4
equal deleted inserted replaced
8:c7c34916027a 9:177826044cd9
     9 /**
     9 /**
    10  * Remove a theme
    10  * Remove a theme
    11  *
    11  *
    12  * @since 2.8.0
    12  * @since 2.8.0
    13  *
    13  *
    14  * @global WP_Filesystem_Base $wp_filesystem Subclass
    14  * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass.
    15  *
    15  *
    16  * @param string $stylesheet Stylesheet of the theme to delete
    16  * @param string $stylesheet Stylesheet of the theme to delete.
    17  * @param string $redirect Redirect to page when complete.
    17  * @param string $redirect   Redirect to page when complete.
    18  * @return void|bool|WP_Error When void, echoes content.
    18  * @return bool|null|WP_Error True on success, false if `$stylesheet` is empty, WP_Error on failure.
    19  */
    19  *                            Null if filesystem credentials are required to proceed.
    20 function delete_theme($stylesheet, $redirect = '') {
    20  */
       
    21 function delete_theme( $stylesheet, $redirect = '' ) {
    21 	global $wp_filesystem;
    22 	global $wp_filesystem;
    22 
    23 
    23 	if ( empty($stylesheet) )
    24 	if ( empty( $stylesheet ) ) {
    24 		return false;
    25 		return false;
       
    26 	}
    25 
    27 
    26 	if ( empty( $redirect ) ) {
    28 	if ( empty( $redirect ) ) {
    27 		$redirect = wp_nonce_url('themes.php?action=delete&stylesheet=' . urlencode( $stylesheet ), 'delete-theme_' . $stylesheet);
    29 		$redirect = wp_nonce_url( 'themes.php?action=delete&stylesheet=' . urlencode( $stylesheet ), 'delete-theme_' . $stylesheet );
    28 	}
    30 	}
    29 
    31 
    30 	ob_start();
    32 	ob_start();
    31 	$credentials = request_filesystem_credentials( $redirect );
    33 	$credentials = request_filesystem_credentials( $redirect );
    32 	$data = ob_get_clean();
    34 	$data        = ob_get_clean();
    33 
    35 
    34 	if ( false === $credentials ) {
    36 	if ( false === $credentials ) {
    35 		if ( ! empty( $data ) ){
    37 		if ( ! empty( $data ) ) {
    36 			include_once( ABSPATH . 'wp-admin/admin-header.php');
    38 			include_once( ABSPATH . 'wp-admin/admin-header.php' );
    37 			echo $data;
    39 			echo $data;
    38 			include( ABSPATH . 'wp-admin/admin-footer.php');
    40 			include( ABSPATH . 'wp-admin/admin-footer.php' );
    39 			exit;
    41 			exit;
    40 		}
    42 		}
    41 		return;
    43 		return;
    42 	}
    44 	}
    43 
    45 
    44 	if ( ! WP_Filesystem( $credentials ) ) {
    46 	if ( ! WP_Filesystem( $credentials ) ) {
    45 		ob_start();
    47 		ob_start();
    46 		request_filesystem_credentials( $redirect, '', true ); // Failed to connect, Error and request again.
    48 		request_filesystem_credentials( $redirect, '', true ); // Failed to connect, Error and request again.
    47 		$data = ob_get_clean();
    49 		$data = ob_get_clean();
    48 
    50 
    49 		if ( ! empty($data) ) {
    51 		if ( ! empty( $data ) ) {
    50 			include_once( ABSPATH . 'wp-admin/admin-header.php');
    52 			include_once( ABSPATH . 'wp-admin/admin-header.php' );
    51 			echo $data;
    53 			echo $data;
    52 			include( ABSPATH . 'wp-admin/admin-footer.php');
    54 			include( ABSPATH . 'wp-admin/admin-footer.php' );
    53 			exit;
    55 			exit;
    54 		}
    56 		}
    55 		return;
    57 		return;
    56 	}
    58 	}
    57 
    59 
    58 	if ( ! is_object($wp_filesystem) )
    60 	if ( ! is_object( $wp_filesystem ) ) {
    59 		return new WP_Error('fs_unavailable', __('Could not access filesystem.'));
    61 		return new WP_Error( 'fs_unavailable', __( 'Could not access filesystem.' ) );
    60 
    62 	}
    61 	if ( is_wp_error($wp_filesystem->errors) && $wp_filesystem->errors->get_error_code() )
    63 
    62 		return new WP_Error('fs_error', __('Filesystem error.'), $wp_filesystem->errors);
    64 	if ( is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->has_errors() ) {
       
    65 		return new WP_Error( 'fs_error', __( 'Filesystem error.' ), $wp_filesystem->errors );
       
    66 	}
    63 
    67 
    64 	// Get the base plugin folder.
    68 	// Get the base plugin folder.
    65 	$themes_dir = $wp_filesystem->wp_themes_dir();
    69 	$themes_dir = $wp_filesystem->wp_themes_dir();
    66 	if ( empty( $themes_dir ) ) {
    70 	if ( empty( $themes_dir ) ) {
    67 		return new WP_Error( 'fs_no_themes_dir', __( 'Unable to locate WordPress theme directory.' ) );
    71 		return new WP_Error( 'fs_no_themes_dir', __( 'Unable to locate WordPress theme directory.' ) );
    68 	}
    72 	}
    69 
    73 
    70 	$themes_dir = trailingslashit( $themes_dir );
    74 	$themes_dir = trailingslashit( $themes_dir );
    71 	$theme_dir = trailingslashit( $themes_dir . $stylesheet );
    75 	$theme_dir  = trailingslashit( $themes_dir . $stylesheet );
    72 	$deleted = $wp_filesystem->delete( $theme_dir, true );
    76 	$deleted    = $wp_filesystem->delete( $theme_dir, true );
    73 
    77 
    74 	if ( ! $deleted ) {
    78 	if ( ! $deleted ) {
    75 		return new WP_Error( 'could_not_remove_theme', sprintf( __( 'Could not fully remove the theme %s.' ), $stylesheet ) );
    79 		return new WP_Error( 'could_not_remove_theme', sprintf( __( 'Could not fully remove the theme %s.' ), $stylesheet ) );
    76 	}
    80 	}
    77 
    81 
    82 		$translations = $theme_translations[ $stylesheet ];
    86 		$translations = $theme_translations[ $stylesheet ];
    83 
    87 
    84 		foreach ( $translations as $translation => $data ) {
    88 		foreach ( $translations as $translation => $data ) {
    85 			$wp_filesystem->delete( WP_LANG_DIR . '/themes/' . $stylesheet . '-' . $translation . '.po' );
    89 			$wp_filesystem->delete( WP_LANG_DIR . '/themes/' . $stylesheet . '-' . $translation . '.po' );
    86 			$wp_filesystem->delete( WP_LANG_DIR . '/themes/' . $stylesheet . '-' . $translation . '.mo' );
    90 			$wp_filesystem->delete( WP_LANG_DIR . '/themes/' . $stylesheet . '-' . $translation . '.mo' );
       
    91 
       
    92 			$json_translation_files = glob( WP_LANG_DIR . '/themes/' . $stylesheet . '-' . $translation . '-*.json' );
       
    93 			if ( $json_translation_files ) {
       
    94 				array_map( array( $wp_filesystem, 'delete' ), $json_translation_files );
       
    95 			}
    87 		}
    96 		}
    88 	}
    97 	}
    89 
    98 
    90 	// Remove the theme from allowed themes on the network.
    99 	// Remove the theme from allowed themes on the network.
    91 	if ( is_multisite() ) {
   100 	if ( is_multisite() ) {
   120  *
   129  *
   121  * @param string $fullpath Full path to the theme file
   130  * @param string $fullpath Full path to the theme file
   122  * @param string $containingfolder Path of the theme parent folder
   131  * @param string $containingfolder Path of the theme parent folder
   123  * @return string
   132  * @return string
   124  */
   133  */
   125 function _get_template_edit_filename($fullpath, $containingfolder) {
   134 function _get_template_edit_filename( $fullpath, $containingfolder ) {
   126 	return str_replace(dirname(dirname( $containingfolder )) , '', $fullpath);
   135 	return str_replace( dirname( dirname( $containingfolder ) ), '', $fullpath );
   127 }
   136 }
   128 
   137 
   129 /**
   138 /**
   130  * Check if there is an update for a theme available.
   139  * Check if there is an update for a theme available.
   131  *
   140  *
   153  * @return false|string HTML for the update link, or false if invalid info was passed.
   162  * @return false|string HTML for the update link, or false if invalid info was passed.
   154  */
   163  */
   155 function get_theme_update_available( $theme ) {
   164 function get_theme_update_available( $theme ) {
   156 	static $themes_update = null;
   165 	static $themes_update = null;
   157 
   166 
   158 	if ( !current_user_can('update_themes' ) )
   167 	if ( ! current_user_can( 'update_themes' ) ) {
   159 		return false;
   168 		return false;
   160 
   169 	}
   161 	if ( !isset($themes_update) )
   170 
   162 		$themes_update = get_site_transient('update_themes');
   171 	if ( ! isset( $themes_update ) ) {
       
   172 		$themes_update = get_site_transient( 'update_themes' );
       
   173 	}
   163 
   174 
   164 	if ( ! ( $theme instanceof WP_Theme ) ) {
   175 	if ( ! ( $theme instanceof WP_Theme ) ) {
   165 		return false;
   176 		return false;
   166 	}
   177 	}
   167 
   178 
   168 	$stylesheet = $theme->get_stylesheet();
   179 	$stylesheet = $theme->get_stylesheet();
   169 
   180 
   170 	$html = '';
   181 	$html = '';
   171 
   182 
   172 	if ( isset($themes_update->response[ $stylesheet ]) ) {
   183 	if ( isset( $themes_update->response[ $stylesheet ] ) ) {
   173 		$update = $themes_update->response[ $stylesheet ];
   184 		$update      = $themes_update->response[ $stylesheet ];
   174 		$theme_name = $theme->display('Name');
   185 		$theme_name  = $theme->display( 'Name' );
   175 		$details_url = add_query_arg(array('TB_iframe' => 'true', 'width' => 1024, 'height' => 800), $update['url']); //Theme browser inside WP? replace this, Also, theme preview JS will override this on the available list.
   186 		$details_url = add_query_arg(
   176 		$update_url = wp_nonce_url( admin_url( 'update.php?action=upgrade-theme&theme=' . urlencode( $stylesheet ) ), 'upgrade-theme_' . $stylesheet );
   187 			array(
   177 
   188 				'TB_iframe' => 'true',
   178 		if ( !is_multisite() ) {
   189 				'width'     => 1024,
   179 			if ( ! current_user_can('update_themes') ) {
   190 				'height'    => 800,
       
   191 			),
       
   192 			$update['url']
       
   193 		); //Theme browser inside WP? replace this, Also, theme preview JS will override this on the available list.
       
   194 		$update_url  = wp_nonce_url( admin_url( 'update.php?action=upgrade-theme&theme=' . urlencode( $stylesheet ) ), 'upgrade-theme_' . $stylesheet );
       
   195 
       
   196 		if ( ! is_multisite() ) {
       
   197 			if ( ! current_user_can( 'update_themes' ) ) {
   180 				/* translators: 1: theme name, 2: theme details URL, 3: additional link attributes, 4: version number */
   198 				/* translators: 1: theme name, 2: theme details URL, 3: additional link attributes, 4: version number */
   181 				$html = sprintf( '<p><strong>' . __( 'There is a new version of %1$s available. <a href="%2$s" %3$s>View version %4$s details</a>.' ) . '</strong></p>',
   199 				$html = sprintf(
       
   200 					'<p><strong>' . __( 'There is a new version of %1$s available. <a href="%2$s" %3$s>View version %4$s details</a>.' ) . '</strong></p>',
   182 					$theme_name,
   201 					$theme_name,
   183 					esc_url( $details_url ),
   202 					esc_url( $details_url ),
   184 					sprintf( 'class="thickbox open-plugin-details-modal" aria-label="%s"',
   203 					sprintf(
       
   204 						'class="thickbox open-plugin-details-modal" aria-label="%s"',
   185 						/* translators: 1: theme name, 2: version number */
   205 						/* translators: 1: theme name, 2: version number */
   186 						esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme_name, $update['new_version'] ) )
   206 						esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme_name, $update['new_version'] ) )
   187 					),
   207 					),
   188 					$update['new_version']
   208 					$update['new_version']
   189 				);
   209 				);
   190 			} elseif ( empty( $update['package'] ) ) {
   210 			} elseif ( empty( $update['package'] ) ) {
   191 				/* translators: 1: theme name, 2: theme details URL, 3: additional link attributes, 4: version number */
   211 				/* translators: 1: theme name, 2: theme details URL, 3: additional link attributes, 4: version number */
   192 				$html = sprintf( '<p><strong>' . __( 'There is a new version of %1$s available. <a href="%2$s" %3$s>View version %4$s details</a>. <em>Automatic update is unavailable for this theme.</em>' ) . '</strong></p>',
   212 				$html = sprintf(
       
   213 					'<p><strong>' . __( 'There is a new version of %1$s available. <a href="%2$s" %3$s>View version %4$s details</a>. <em>Automatic update is unavailable for this theme.</em>' ) . '</strong></p>',
   193 					$theme_name,
   214 					$theme_name,
   194 					esc_url( $details_url ),
   215 					esc_url( $details_url ),
   195 					sprintf( 'class="thickbox open-plugin-details-modal" aria-label="%s"',
   216 					sprintf(
       
   217 						'class="thickbox open-plugin-details-modal" aria-label="%s"',
   196 						/* translators: 1: theme name, 2: version number */
   218 						/* translators: 1: theme name, 2: version number */
   197 						esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme_name, $update['new_version'] ) )
   219 						esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme_name, $update['new_version'] ) )
   198 					),
   220 					),
   199 					$update['new_version']
   221 					$update['new_version']
   200 				);
   222 				);
   201 			} else {
   223 			} else {
   202 				/* translators: 1: theme name, 2: theme details URL, 3: additional link attributes, 4: version number, 5: update URL, 6: additional link attributes */
   224 				/* translators: 1: theme name, 2: theme details URL, 3: additional link attributes, 4: version number, 5: update URL, 6: additional link attributes */
   203 				$html = sprintf( '<p><strong>' . __( 'There is a new version of %1$s available. <a href="%2$s" %3$s>View version %4$s details</a> or <a href="%5$s" %6$s>update now</a>.' ) . '</strong></p>',
   225 				$html = sprintf(
       
   226 					'<p><strong>' . __( 'There is a new version of %1$s available. <a href="%2$s" %3$s>View version %4$s details</a> or <a href="%5$s" %6$s>update now</a>.' ) . '</strong></p>',
   204 					$theme_name,
   227 					$theme_name,
   205 					esc_url( $details_url ),
   228 					esc_url( $details_url ),
   206 					sprintf( 'class="thickbox open-plugin-details-modal" aria-label="%s"',
   229 					sprintf(
       
   230 						'class="thickbox open-plugin-details-modal" aria-label="%s"',
   207 						/* translators: 1: theme name, 2: version number */
   231 						/* translators: 1: theme name, 2: version number */
   208 						esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme_name, $update['new_version'] ) )
   232 						esc_attr( sprintf( __( 'View %1$s version %2$s details' ), $theme_name, $update['new_version'] ) )
   209 					),
   233 					),
   210 					$update['new_version'],
   234 					$update['new_version'],
   211 					$update_url,
   235 					$update_url,
   212 					sprintf( 'aria-label="%s" id="update-theme" data-slug="%s"',
   236 					sprintf(
       
   237 						'aria-label="%s" id="update-theme" data-slug="%s"',
   213 						/* translators: %s: theme name */
   238 						/* translators: %s: theme name */
   214 						esc_attr( sprintf( __( 'Update %s now' ), $theme_name ) ),
   239 						esc_attr( sprintf( __( 'Update %s now' ), $theme_name ) ),
   215 						$stylesheet
   240 						$stylesheet
   216 					)
   241 					)
   217 				);
   242 				);
   221 
   246 
   222 	return $html;
   247 	return $html;
   223 }
   248 }
   224 
   249 
   225 /**
   250 /**
   226  * Retrieve list of WordPress theme features (aka theme tags)
   251  * Retrieve list of WordPress theme features (aka theme tags).
   227  *
   252  *
   228  * @since 3.1.0
   253  * @since 3.1.0
   229  *
   254  *
   230  * @param bool $api Optional. Whether try to fetch tags from the WordPress.org API. Defaults to true.
   255  * @param bool $api Optional. Whether try to fetch tags from the WordPress.org API. Defaults to true.
   231  * @return array Array of features keyed by category with translations keyed by slug.
   256  * @return array Array of features keyed by category with translations keyed by slug.
   260 			'post-formats'          => __( 'Post Formats' ),
   285 			'post-formats'          => __( 'Post Formats' ),
   261 			'sticky-post'           => __( 'Sticky Post' ),
   286 			'sticky-post'           => __( 'Sticky Post' ),
   262 			'theme-options'         => __( 'Theme Options' ),
   287 			'theme-options'         => __( 'Theme Options' ),
   263 		),
   288 		),
   264 
   289 
   265 		__( 'Layout' ) => array(
   290 		__( 'Layout' )   => array(
   266 			'grid-layout'   => __( 'Grid Layout' ),
   291 			'grid-layout'   => __( 'Grid Layout' ),
   267 			'one-column'    => __( 'One Column' ),
   292 			'one-column'    => __( 'One Column' ),
   268 			'two-columns'   => __( 'Two Columns' ),
   293 			'two-columns'   => __( 'Two Columns' ),
   269 			'three-columns' => __( 'Three Columns' ),
   294 			'three-columns' => __( 'Three Columns' ),
   270 			'four-columns'  => __( 'Four Columns' ),
   295 			'four-columns'  => __( 'Four Columns' ),
   271 			'left-sidebar'  => __( 'Left Sidebar' ),
   296 			'left-sidebar'  => __( 'Left Sidebar' ),
   272 			'right-sidebar' => __( 'Right Sidebar' ),
   297 			'right-sidebar' => __( 'Right Sidebar' ),
   273 		)
   298 		),
   274 
   299 
   275 	);
   300 	);
   276 
   301 
   277 	if ( ! $api || ! current_user_can( 'install_themes' ) )
   302 	if ( ! $api || ! current_user_can( 'install_themes' ) ) {
   278 		return $features;
   303 		return $features;
   279 
   304 	}
   280 	if ( !$feature_list = get_site_transient( 'wporg_theme_feature_list' ) )
   305 
       
   306 	if ( ! $feature_list = get_site_transient( 'wporg_theme_feature_list' ) ) {
   281 		set_site_transient( 'wporg_theme_feature_list', array(), 3 * HOUR_IN_SECONDS );
   307 		set_site_transient( 'wporg_theme_feature_list', array(), 3 * HOUR_IN_SECONDS );
   282 
   308 	}
   283 	if ( !$feature_list ) {
   309 
       
   310 	if ( ! $feature_list ) {
   284 		$feature_list = themes_api( 'feature_list', array() );
   311 		$feature_list = themes_api( 'feature_list', array() );
   285 		if ( is_wp_error( $feature_list ) )
   312 		if ( is_wp_error( $feature_list ) ) {
   286 			return $features;
   313 			return $features;
   287 	}
   314 		}
   288 
   315 	}
   289 	if ( !$feature_list )
   316 
       
   317 	if ( ! $feature_list ) {
   290 		return $features;
   318 		return $features;
       
   319 	}
   291 
   320 
   292 	set_site_transient( 'wporg_theme_feature_list', $feature_list, 3 * HOUR_IN_SECONDS );
   321 	set_site_transient( 'wporg_theme_feature_list', $feature_list, 3 * HOUR_IN_SECONDS );
   293 
   322 
   294 	$category_translations = array(
   323 	$category_translations = array(
   295 		'Layout'   => __( 'Layout' ),
   324 		'Layout'   => __( 'Layout' ),
   298 	);
   327 	);
   299 
   328 
   300 	// Loop over the wporg canonical list and apply translations
   329 	// Loop over the wporg canonical list and apply translations
   301 	$wporg_features = array();
   330 	$wporg_features = array();
   302 	foreach ( (array) $feature_list as $feature_category => $feature_items ) {
   331 	foreach ( (array) $feature_list as $feature_category => $feature_items ) {
   303 		if ( isset($category_translations[$feature_category]) )
   332 		if ( isset( $category_translations[ $feature_category ] ) ) {
   304 			$feature_category = $category_translations[$feature_category];
   333 			$feature_category = $category_translations[ $feature_category ];
   305 		$wporg_features[$feature_category] = array();
   334 		}
       
   335 		$wporg_features[ $feature_category ] = array();
   306 
   336 
   307 		foreach ( $feature_items as $feature ) {
   337 		foreach ( $feature_items as $feature ) {
   308 			if ( isset($features[$feature_category][$feature]) )
   338 			if ( isset( $features[ $feature_category ][ $feature ] ) ) {
   309 				$wporg_features[$feature_category][$feature] = $features[$feature_category][$feature];
   339 				$wporg_features[ $feature_category ][ $feature ] = $features[ $feature_category ][ $feature ];
   310 			else
   340 			} else {
   311 				$wporg_features[$feature_category][$feature] = $feature;
   341 				$wporg_features[ $feature_category ][ $feature ] = $feature;
       
   342 			}
   312 		}
   343 		}
   313 	}
   344 	}
   314 
   345 
   315 	return $wporg_features;
   346 	return $wporg_features;
   316 }
   347 }
   395  * @return object|array|WP_Error Response object or array on success, WP_Error on failure. See the
   426  * @return object|array|WP_Error Response object or array on success, WP_Error on failure. See the
   396  *         {@link https://developer.wordpress.org/reference/functions/themes_api/ function reference article}
   427  *         {@link https://developer.wordpress.org/reference/functions/themes_api/ function reference article}
   397  *         for more information on the make-up of possible return objects depending on the value of `$action`.
   428  *         for more information on the make-up of possible return objects depending on the value of `$action`.
   398  */
   429  */
   399 function themes_api( $action, $args = array() ) {
   430 function themes_api( $action, $args = array() ) {
       
   431 	// include an unmodified $wp_version
       
   432 	include( ABSPATH . WPINC . '/version.php' );
   400 
   433 
   401 	if ( is_array( $args ) ) {
   434 	if ( is_array( $args ) ) {
   402 		$args = (object) $args;
   435 		$args = (object) $args;
   403 	}
   436 	}
   404 
   437 
   405 	if ( ! isset( $args->per_page ) ) {
   438 	if ( 'query_themes' == $action ) {
   406 		$args->per_page = 24;
   439 		if ( ! isset( $args->per_page ) ) {
       
   440 			$args->per_page = 24;
       
   441 		}
   407 	}
   442 	}
   408 
   443 
   409 	if ( ! isset( $args->locale ) ) {
   444 	if ( ! isset( $args->locale ) ) {
   410 		$args->locale = get_user_locale();
   445 		$args->locale = get_user_locale();
       
   446 	}
       
   447 
       
   448 	if ( ! isset( $args->wp_version ) ) {
       
   449 		$args->wp_version = substr( $wp_version, 0, 3 ); // X.y
   411 	}
   450 	}
   412 
   451 
   413 	/**
   452 	/**
   414 	 * Filters arguments used to query for installer pages from the WordPress.org Themes API.
   453 	 * Filters arguments used to query for installer pages from the WordPress.org Themes API.
   415 	 *
   454 	 *
   439 	 * @param object             $args     Arguments used to query for installer pages from the Themes API.
   478 	 * @param object             $args     Arguments used to query for installer pages from the Themes API.
   440 	 */
   479 	 */
   441 	$res = apply_filters( 'themes_api', false, $action, $args );
   480 	$res = apply_filters( 'themes_api', false, $action, $args );
   442 
   481 
   443 	if ( ! $res ) {
   482 	if ( ! $res ) {
   444 		// include an unmodified $wp_version
   483 		$url = 'http://api.wordpress.org/themes/info/1.2/';
   445 		include( ABSPATH . WPINC . '/version.php' );
   484 		$url = add_query_arg(
   446 
   485 			array(
   447 		$url = $http_url = 'http://api.wordpress.org/themes/info/1.0/';
   486 				'action'  => $action,
   448 		if ( $ssl = wp_http_supports( array( 'ssl' ) ) )
   487 				'request' => $args,
       
   488 			),
       
   489 			$url
       
   490 		);
       
   491 
       
   492 		$http_url = $url;
       
   493 		if ( $ssl = wp_http_supports( array( 'ssl' ) ) ) {
   449 			$url = set_url_scheme( $url, 'https' );
   494 			$url = set_url_scheme( $url, 'https' );
       
   495 		}
   450 
   496 
   451 		$http_args = array(
   497 		$http_args = array(
   452 			'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url( '/' ),
   498 			'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url( '/' ),
   453 			'body' => array(
       
   454 				'action' => $action,
       
   455 				'request' => serialize( $args )
       
   456 			)
       
   457 		);
   499 		);
   458 		$request = wp_remote_post( $url, $http_args );
   500 		$request   = wp_remote_get( $url, $http_args );
   459 
   501 
   460 		if ( $ssl && is_wp_error( $request ) ) {
   502 		if ( $ssl && is_wp_error( $request ) ) {
   461 			if ( ! wp_doing_ajax() ) {
   503 			if ( ! wp_doing_ajax() ) {
   462 				trigger_error(
   504 				trigger_error(
   463 					sprintf(
   505 					sprintf(
   466 						__( 'https://wordpress.org/support/' )
   508 						__( 'https://wordpress.org/support/' )
   467 					) . ' ' . __( '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)' ),
   509 					) . ' ' . __( '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)' ),
   468 					headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE
   510 					headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE
   469 				);
   511 				);
   470 			}
   512 			}
   471 			$request = wp_remote_post( $http_url, $http_args );
   513 			$request = wp_remote_get( $http_url, $http_args );
   472 		}
   514 		}
   473 
   515 
   474 		if ( is_wp_error($request) ) {
   516 		if ( is_wp_error( $request ) ) {
   475 			$res = new WP_Error( 'themes_api_failed',
   517 			$res = new WP_Error(
       
   518 				'themes_api_failed',
   476 				sprintf(
   519 				sprintf(
   477 					/* translators: %s: support forums URL */
   520 					/* translators: %s: support forums URL */
   478 					__( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.' ),
   521 					__( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.' ),
   479 					__( 'https://wordpress.org/support/' )
   522 					__( 'https://wordpress.org/support/' )
   480 				),
   523 				),
   481 				$request->get_error_message()
   524 				$request->get_error_message()
   482 			);
   525 			);
   483 		} else {
   526 		} else {
   484 			$res = maybe_unserialize( wp_remote_retrieve_body( $request ) );
   527 			$res = json_decode( wp_remote_retrieve_body( $request ), true );
   485 			if ( ! is_object( $res ) && ! is_array( $res ) ) {
   528 			if ( is_array( $res ) ) {
   486 				$res = new WP_Error( 'themes_api_failed',
   529 				// Object casting is required in order to match the info/1.0 format.
       
   530 				$res = (object) $res;
       
   531 			} elseif ( null === $res ) {
       
   532 				$res = new WP_Error(
       
   533 					'themes_api_failed',
   487 					sprintf(
   534 					sprintf(
   488 						/* translators: %s: support forums URL */
   535 						/* translators: %s: support forums URL */
   489 						__( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.' ),
   536 						__( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="%s">support forums</a>.' ),
   490 						__( 'https://wordpress.org/support/' )
   537 						__( 'https://wordpress.org/support/' )
   491 					),
   538 					),
   492 					wp_remote_retrieve_body( $request )
   539 					wp_remote_retrieve_body( $request )
   493 				);
   540 				);
   494 			}
   541 			}
       
   542 
       
   543 			if ( isset( $res->error ) ) {
       
   544 				$res = new WP_Error( 'themes_api_failed', $res->error );
       
   545 			}
       
   546 		}
       
   547 
       
   548 		// Back-compat for info/1.2 API, upgrade the theme objects in query_themes to objects.
       
   549 		if ( 'query_themes' == $action ) {
       
   550 			foreach ( $res->themes as $i => $theme ) {
       
   551 				$res->themes[ $i ] = (object) $theme;
       
   552 			}
       
   553 		}
       
   554 		// Back-compat for info/1.2 API, downgrade the feature_list result back to an array.
       
   555 		if ( 'feature_list' == $action ) {
       
   556 			$res = (array) $res;
   495 		}
   557 		}
   496 	}
   558 	}
   497 
   559 
   498 	/**
   560 	/**
   499 	 * Filters the returned WordPress.org Themes API response.
   561 	 * Filters the returned WordPress.org Themes API response.
   511 /**
   573 /**
   512  * Prepare themes for JavaScript.
   574  * Prepare themes for JavaScript.
   513  *
   575  *
   514  * @since 3.8.0
   576  * @since 3.8.0
   515  *
   577  *
   516  * @param array $themes Optional. Array of WP_Theme objects to prepare.
   578  * @param WP_Theme[] $themes Optional. Array of theme objects to prepare.
   517  *                      Defaults to all allowed themes.
   579  *                           Defaults to all allowed themes.
   518  *
   580  *
   519  * @return array An associative array of theme data, sorted by name.
   581  * @return array An associative array of theme data, sorted by name.
   520  */
   582  */
   521 function wp_prepare_themes_for_js( $themes = null ) {
   583 function wp_prepare_themes_for_js( $themes = null ) {
   522 	$current_theme = get_stylesheet();
   584 	$current_theme = get_stylesheet();
   527 	 * Passing a non-empty array will result in wp_prepare_themes_for_js() returning
   589 	 * Passing a non-empty array will result in wp_prepare_themes_for_js() returning
   528 	 * early with that value instead.
   590 	 * early with that value instead.
   529 	 *
   591 	 *
   530 	 * @since 4.2.0
   592 	 * @since 4.2.0
   531 	 *
   593 	 *
   532 	 * @param array      $prepared_themes An associative array of theme data. Default empty array.
   594 	 * @param array           $prepared_themes An associative array of theme data. Default empty array.
   533 	 * @param null|array $themes          An array of WP_Theme objects to prepare, if any.
   595 	 * @param WP_Theme[]|null $themes          An array of theme objects to prepare, if any.
   534 	 * @param string     $current_theme   The current theme slug.
   596 	 * @param string          $current_theme   The current theme slug.
   535 	 */
   597 	 */
   536 	$prepared_themes = (array) apply_filters( 'pre_prepare_themes_for_js', array(), $themes, $current_theme );
   598 	$prepared_themes = (array) apply_filters( 'pre_prepare_themes_for_js', array(), $themes, $current_theme );
   537 
   599 
   538 	if ( ! empty( $prepared_themes ) ) {
   600 	if ( ! empty( $prepared_themes ) ) {
   539 		return $prepared_themes;
   601 		return $prepared_themes;
   560 	WP_Theme::sort_by_name( $themes );
   622 	WP_Theme::sort_by_name( $themes );
   561 
   623 
   562 	$parents = array();
   624 	$parents = array();
   563 
   625 
   564 	foreach ( $themes as $theme ) {
   626 	foreach ( $themes as $theme ) {
   565 		$slug = $theme->get_stylesheet();
   627 		$slug         = $theme->get_stylesheet();
   566 		$encoded_slug = urlencode( $slug );
   628 		$encoded_slug = urlencode( $slug );
   567 
   629 
   568 		$parent = false;
   630 		$parent = false;
   569 		if ( $theme->parent() ) {
   631 		if ( $theme->parent() ) {
   570 			$parent = $theme->parent();
   632 			$parent           = $theme->parent();
   571 			$parents[ $slug ] = $parent->get_stylesheet();
   633 			$parents[ $slug ] = $parent->get_stylesheet();
   572 			$parent = $parent->display( 'Name' );
   634 			$parent           = $parent->display( 'Name' );
   573 		}
   635 		}
   574 
   636 
   575 		$customize_action = null;
   637 		$customize_action = null;
   576 		if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) {
   638 		if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) {
   577 			$customize_action = esc_url( add_query_arg(
   639 			$customize_action = esc_url(
   578 				array(
   640 				add_query_arg(
   579 					'return' => urlencode( esc_url_raw( remove_query_arg( wp_removable_query_args(), wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) ),
   641 					array(
   580 				),
   642 						'return' => urlencode( esc_url_raw( remove_query_arg( wp_removable_query_args(), wp_unslash( $_SERVER['REQUEST_URI'] ) ) ) ),
   581 				wp_customize_url( $slug )
   643 					),
   582 			) );
   644 					wp_customize_url( $slug )
       
   645 				)
       
   646 			);
   583 		}
   647 		}
   584 
   648 
   585 		$prepared_themes[ $slug ] = array(
   649 		$prepared_themes[ $slug ] = array(
   586 			'id'           => $slug,
   650 			'id'           => $slug,
   587 			'name'         => $theme->display( 'Name' ),
   651 			'name'         => $theme->display( 'Name' ),
   592 			'version'      => $theme->display( 'Version' ),
   656 			'version'      => $theme->display( 'Version' ),
   593 			'tags'         => $theme->display( 'Tags' ),
   657 			'tags'         => $theme->display( 'Tags' ),
   594 			'parent'       => $parent,
   658 			'parent'       => $parent,
   595 			'active'       => $slug === $current_theme,
   659 			'active'       => $slug === $current_theme,
   596 			'hasUpdate'    => isset( $updates[ $slug ] ),
   660 			'hasUpdate'    => isset( $updates[ $slug ] ),
   597 			'hasPackage'   => isset( $updates[ $slug ] ) && ! empty( $updates[ $slug ][ 'package' ] ),
   661 			'hasPackage'   => isset( $updates[ $slug ] ) && ! empty( $updates[ $slug ]['package'] ),
   598 			'update'       => get_theme_update_available( $theme ),
   662 			'update'       => get_theme_update_available( $theme ),
   599 			'actions'      => array(
   663 			'actions'      => array(
   600 				'activate' => current_user_can( 'switch_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=activate&amp;stylesheet=' . $encoded_slug ), 'switch-theme_' . $slug ) : null,
   664 				'activate'  => current_user_can( 'switch_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=activate&amp;stylesheet=' . $encoded_slug ), 'switch-theme_' . $slug ) : null,
   601 				'customize' => $customize_action,
   665 				'customize' => $customize_action,
   602 				'delete'   => current_user_can( 'delete_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=delete&amp;stylesheet=' . $encoded_slug ), 'delete-theme_' . $slug ) : null,
   666 				'delete'    => current_user_can( 'delete_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=delete&amp;stylesheet=' . $encoded_slug ), 'delete-theme_' . $slug ) : null,
   603 			),
   667 			),
   604 		);
   668 		);
   605 	}
   669 	}
   606 
   670 
   607 	// Remove 'delete' action if theme has an active child
   671 	// Remove 'delete' action if theme has an active child
   655 					<h3 class="theme-author"><?php printf( __( 'By %s' ), '{{{ data.authorAndUri }}}' ); ?></h3>
   719 					<h3 class="theme-author"><?php printf( __( 'By %s' ), '{{{ data.authorAndUri }}}' ); ?></h3>
   656 
   720 
   657 					<# if ( data.stars && 0 != data.num_ratings ) { #>
   721 					<# if ( data.stars && 0 != data.num_ratings ) { #>
   658 						<div class="theme-rating">
   722 						<div class="theme-rating">
   659 							{{{ data.stars }}}
   723 							{{{ data.stars }}}
   660 							<span class="num-ratings">
   724 							<a class="num-ratings" target="_blank" href="{{ data.reviews_url }}">
   661 								<?php
   725 								<?php
   662 								/* translators: %s: number of ratings */
   726 								printf(
   663 								echo sprintf( __( '(%s ratings)' ), '{{ data.num_ratings }}' );
   727 									'%1$s <span class="screen-reader-text">%2$s</span>',
       
   728 									/* translators: %s: number of ratings */
       
   729 									sprintf( __( '(%s ratings)' ), '{{ data.num_ratings }}' ),
       
   730 									/* translators: accessibility text */
       
   731 									__( '(opens in a new tab)' )
       
   732 								);
   664 								?>
   733 								?>
   665 							</span>
   734 							</a>
   666 						</div>
   735 						</div>
   667 					<# } #>
   736 					<# } #>
   668 
   737 
   669 					<# if ( data.hasUpdate ) { #>
   738 					<# if ( data.hasUpdate ) { #>
   670 						<div class="notice notice-warning notice-alt notice-large" data-slug="{{ data.id }}">
   739 						<div class="notice notice-warning notice-alt notice-large" data-slug="{{ data.id }}">
   702 			</div>
   771 			</div>
   703 		</div>
   772 		</div>
   704 	</script>
   773 	</script>
   705 	<?php
   774 	<?php
   706 }
   775 }
       
   776 
       
   777 /**
       
   778  * Determines whether a theme is technically active but was paused while
       
   779  * loading.
       
   780  *
       
   781  * For more information on this and similar theme functions, check out
       
   782  * the {@link https://developer.wordpress.org/themes/basics/conditional-tags/
       
   783  * Conditional Tags} article in the Theme Developer Handbook.
       
   784  *
       
   785  * @since 5.2.0
       
   786  *
       
   787  * @param string $theme Path to the theme directory relative to the themes directory.
       
   788  * @return bool True, if in the list of paused themes. False, not in the list.
       
   789  */
       
   790 function is_theme_paused( $theme ) {
       
   791 	if ( ! isset( $GLOBALS['_paused_themes'] ) ) {
       
   792 		return false;
       
   793 	}
       
   794 
       
   795 	if ( get_stylesheet() !== $theme && get_template() !== $theme ) {
       
   796 		return false;
       
   797 	}
       
   798 
       
   799 	return array_key_exists( $theme, $GLOBALS['_paused_themes'] );
       
   800 }
       
   801 
       
   802 /**
       
   803  * Gets the error that was recorded for a paused theme.
       
   804  *
       
   805  * @since 5.2.0
       
   806  *
       
   807  * @param string $theme Path to the theme directory relative to the themes
       
   808  *                      directory.
       
   809  * @return array|false Array of error information as it was returned by
       
   810  *                     `error_get_last()`, or false if none was recorded.
       
   811  */
       
   812 function wp_get_theme_error( $theme ) {
       
   813 	if ( ! isset( $GLOBALS['_paused_themes'] ) ) {
       
   814 		return false;
       
   815 	}
       
   816 
       
   817 	if ( ! array_key_exists( $theme, $GLOBALS['_paused_themes'] ) ) {
       
   818 		return false;
       
   819 	}
       
   820 
       
   821 	return $GLOBALS['_paused_themes'][ $theme ];
       
   822 }
       
   823 
       
   824 /**
       
   825  * Tries to resume a single theme.
       
   826  *
       
   827  * If a redirect was provided and a functions.php file was found, we first ensure that
       
   828  * functions.php file does not throw fatal errors anymore.
       
   829  *
       
   830  * The way it works is by setting the redirection to the error before trying to
       
   831  * include the file. If the theme fails, then the redirection will not be overwritten
       
   832  * with the success message and the theme will not be resumed.
       
   833  *
       
   834  * @since 5.2.0
       
   835  *
       
   836  * @param string $theme    Single theme to resume.
       
   837  * @param string $redirect Optional. URL to redirect to. Default empty string.
       
   838  * @return bool|WP_Error True on success, false if `$theme` was not paused,
       
   839  *                       `WP_Error` on failure.
       
   840  */
       
   841 function resume_theme( $theme, $redirect = '' ) {
       
   842 	list( $extension ) = explode( '/', $theme );
       
   843 
       
   844 	/*
       
   845 	 * We'll override this later if the theme could be resumed without
       
   846 	 * creating a fatal error.
       
   847 	 */
       
   848 	if ( ! empty( $redirect ) ) {
       
   849 		$functions_path = '';
       
   850 		if ( strpos( STYLESHEETPATH, $extension ) ) {
       
   851 			$functions_path = STYLESHEETPATH . '/functions.php';
       
   852 		} elseif ( strpos( TEMPLATEPATH, $extension ) ) {
       
   853 			$functions_path = TEMPLATEPATH . '/functions.php';
       
   854 		}
       
   855 
       
   856 		if ( ! empty( $functions_path ) ) {
       
   857 			wp_redirect(
       
   858 				add_query_arg(
       
   859 					'_error_nonce',
       
   860 					wp_create_nonce( 'theme-resume-error_' . $theme ),
       
   861 					$redirect
       
   862 				)
       
   863 			);
       
   864 
       
   865 			// Load the theme's functions.php to test whether it throws a fatal error.
       
   866 			ob_start();
       
   867 			if ( ! defined( 'WP_SANDBOX_SCRAPING' ) ) {
       
   868 				define( 'WP_SANDBOX_SCRAPING', true );
       
   869 			}
       
   870 			include $functions_path;
       
   871 			ob_clean();
       
   872 		}
       
   873 	}
       
   874 
       
   875 	$result = wp_paused_themes()->delete( $extension );
       
   876 
       
   877 	if ( ! $result ) {
       
   878 		return new WP_Error(
       
   879 			'could_not_resume_theme',
       
   880 			__( 'Could not resume the theme.' )
       
   881 		);
       
   882 	}
       
   883 
       
   884 	return true;
       
   885 }
       
   886 
       
   887 /**
       
   888  * Renders an admin notice in case some themes have been paused due to errors.
       
   889  *
       
   890  * @since 5.2.0
       
   891  */
       
   892 function paused_themes_notice() {
       
   893 	if ( 'themes.php' === $GLOBALS['pagenow'] ) {
       
   894 		return;
       
   895 	}
       
   896 
       
   897 	if ( ! current_user_can( 'resume_themes' ) ) {
       
   898 		return;
       
   899 	}
       
   900 
       
   901 	if ( ! isset( $GLOBALS['_paused_themes'] ) || empty( $GLOBALS['_paused_themes'] ) ) {
       
   902 		return;
       
   903 	}
       
   904 
       
   905 	printf(
       
   906 		'<div class="notice notice-error"><p><strong>%s</strong><br>%s</p><p><a href="%s">%s</a></p></div>',
       
   907 		__( 'One or more themes failed to load properly.' ),
       
   908 		__( 'You can find more details and make changes on the Themes screen.' ),
       
   909 		esc_url( admin_url( 'themes.php' ) ),
       
   910 		__( 'Go to the Themes screen' )
       
   911 	);
       
   912 }