wp/wp-admin/includes/plugin-install.php
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 18 be944660c56a
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
    71  *         @type bool $description       Whether to return the plugin full description. Default false.
    71  *         @type bool $description       Whether to return the plugin full description. Default false.
    72  *         @type bool $sections          Whether to return the plugin readme sections: description, installation,
    72  *         @type bool $sections          Whether to return the plugin readme sections: description, installation,
    73  *                                       FAQ, screenshots, other notes, and changelog. Default false.
    73  *                                       FAQ, screenshots, other notes, and changelog. Default false.
    74  *         @type bool $tested            Whether to return the 'Compatible up to' value. Default true.
    74  *         @type bool $tested            Whether to return the 'Compatible up to' value. Default true.
    75  *         @type bool $requires          Whether to return the required WordPress version. Default true.
    75  *         @type bool $requires          Whether to return the required WordPress version. Default true.
       
    76  *         @type bool $requires_php      Whether to return the required PHP version. Default true.
    76  *         @type bool $rating            Whether to return the rating in percent and total number of ratings.
    77  *         @type bool $rating            Whether to return the rating in percent and total number of ratings.
    77  *                                       Default true.
    78  *                                       Default true.
    78  *         @type bool $ratings           Whether to return the number of rating for each star (1-5). Default true.
    79  *         @type bool $ratings           Whether to return the number of rating for each star (1-5). Default true.
    79  *         @type bool $downloaded        Whether to return the download count. Default true.
    80  *         @type bool $downloaded        Whether to return the download count. Default true.
    80  *         @type bool $downloadlink      Whether to return the download link for the package. Default true.
    81  *         @type bool $downloadlink      Whether to return the download link for the package. Default true.
    97  * @return object|array|WP_Error Response object or array on success, WP_Error on failure. See the
    98  * @return object|array|WP_Error Response object or array on success, WP_Error on failure. See the
    98  *         {@link https://developer.wordpress.org/reference/functions/plugins_api/ function reference article}
    99  *         {@link https://developer.wordpress.org/reference/functions/plugins_api/ function reference article}
    99  *         for more information on the make-up of possible return values depending on the value of `$action`.
   100  *         for more information on the make-up of possible return values depending on the value of `$action`.
   100  */
   101  */
   101 function plugins_api( $action, $args = array() ) {
   102 function plugins_api( $action, $args = array() ) {
   102 	// include an unmodified $wp_version
   103 	// Include an unmodified $wp_version.
   103 	include( ABSPATH . WPINC . '/version.php' );
   104 	require ABSPATH . WPINC . '/version.php';
   104 
   105 
   105 	if ( is_array( $args ) ) {
   106 	if ( is_array( $args ) ) {
   106 		$args = (object) $args;
   107 		$args = (object) $args;
   107 	}
   108 	}
   108 
   109 
   109 	if ( 'query_plugins' == $action ) {
   110 	if ( 'query_plugins' === $action ) {
   110 		if ( ! isset( $args->per_page ) ) {
   111 		if ( ! isset( $args->per_page ) ) {
   111 			$args->per_page = 24;
   112 			$args->per_page = 24;
   112 		}
   113 		}
   113 	}
   114 	}
   114 
   115 
   115 	if ( ! isset( $args->locale ) ) {
   116 	if ( ! isset( $args->locale ) ) {
   116 		$args->locale = get_user_locale();
   117 		$args->locale = get_user_locale();
   117 	}
   118 	}
   118 
   119 
   119 	if ( ! isset( $args->wp_version ) ) {
   120 	if ( ! isset( $args->wp_version ) ) {
   120 		$args->wp_version = substr( $wp_version, 0, 3 ); // X.y
   121 		$args->wp_version = substr( $wp_version, 0, 3 ); // x.y
   121 	}
   122 	}
   122 
   123 
   123 	/**
   124 	/**
   124 	 * Filters the WordPress.org Plugin Installation API arguments.
   125 	 * Filters the WordPress.org Plugin Installation API arguments.
   125 	 *
   126 	 *
   158 			),
   159 			),
   159 			$url
   160 			$url
   160 		);
   161 		);
   161 
   162 
   162 		$http_url = $url;
   163 		$http_url = $url;
   163 		if ( $ssl = wp_http_supports( array( 'ssl' ) ) ) {
   164 		$ssl      = wp_http_supports( array( 'ssl' ) );
       
   165 		if ( $ssl ) {
   164 			$url = set_url_scheme( $url, 'https' );
   166 			$url = set_url_scheme( $url, 'https' );
   165 		}
   167 		}
   166 
   168 
   167 		$http_args = array(
   169 		$http_args = array(
   168 			'timeout'    => 15,
   170 			'timeout'    => 15,
   169 			'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url( '/' ),
   171 			'user-agent' => 'WordPress/' . $wp_version . '; ' . home_url( '/' ),
   170 		);
   172 		);
   171 		$request   = wp_remote_get( $url, $http_args );
   173 		$request   = wp_remote_get( $url, $http_args );
   172 
   174 
   173 		if ( $ssl && is_wp_error( $request ) ) {
   175 		if ( $ssl && is_wp_error( $request ) ) {
   174 			trigger_error(
   176 			if ( ! wp_is_json_request() ) {
   175 				sprintf(
   177 				trigger_error(
   176 					/* translators: %s: support forums URL */
   178 					sprintf(
   177 					__( '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>.' ),
   179 						/* translators: %s: Support forums URL. */
   178 					__( 'https://wordpress.org/support/' )
   180 						__( '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>.' ),
   179 				) . ' ' . __( '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)' ),
   181 						__( 'https://wordpress.org/support/forums/' )
   180 				headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE
   182 					) . ' ' . __( '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)' ),
   181 			);
   183 					headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE
       
   184 				);
       
   185 			}
       
   186 
   182 			$request = wp_remote_get( $http_url, $http_args );
   187 			$request = wp_remote_get( $http_url, $http_args );
   183 		}
   188 		}
   184 
   189 
   185 		if ( is_wp_error( $request ) ) {
   190 		if ( is_wp_error( $request ) ) {
   186 			$res = new WP_Error(
   191 			$res = new WP_Error(
   187 				'plugins_api_failed',
   192 				'plugins_api_failed',
   188 				sprintf(
   193 				sprintf(
   189 					/* translators: %s: support forums URL */
   194 					/* translators: %s: Support forums URL. */
   190 					__( '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>.' ),
   195 					__( '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>.' ),
   191 					__( 'https://wordpress.org/support/' )
   196 					__( 'https://wordpress.org/support/forums/' )
   192 				),
   197 				),
   193 				$request->get_error_message()
   198 				$request->get_error_message()
   194 			);
   199 			);
   195 		} else {
   200 		} else {
   196 			$res = json_decode( wp_remote_retrieve_body( $request ), true );
   201 			$res = json_decode( wp_remote_retrieve_body( $request ), true );
   199 				$res = (object) $res;
   204 				$res = (object) $res;
   200 			} elseif ( null === $res ) {
   205 			} elseif ( null === $res ) {
   201 				$res = new WP_Error(
   206 				$res = new WP_Error(
   202 					'plugins_api_failed',
   207 					'plugins_api_failed',
   203 					sprintf(
   208 					sprintf(
   204 						/* translators: %s: support forums URL */
   209 						/* translators: %s: Support forums URL. */
   205 						__( '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>.' ),
   210 						__( '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>.' ),
   206 						__( 'https://wordpress.org/support/' )
   211 						__( 'https://wordpress.org/support/forums/' )
   207 					),
   212 					),
   208 					wp_remote_retrieve_body( $request )
   213 					wp_remote_retrieve_body( $request )
   209 				);
   214 				);
   210 			}
   215 			}
   211 
   216 
   236  *
   241  *
   237  * @param array $args
   242  * @param array $args
   238  * @return array
   243  * @return array
   239  */
   244  */
   240 function install_popular_tags( $args = array() ) {
   245 function install_popular_tags( $args = array() ) {
   241 	$key = md5( serialize( $args ) );
   246 	$key  = md5( serialize( $args ) );
   242 	if ( false !== ( $tags = get_site_transient( 'poptags_' . $key ) ) ) {
   247 	$tags = get_site_transient( 'poptags_' . $key );
       
   248 	if ( false !== $tags ) {
   243 		return $tags;
   249 		return $tags;
   244 	}
   250 	}
   245 
   251 
   246 	$tags = plugins_api( 'hot_tags', $args );
   252 	$tags = plugins_api( 'hot_tags', $args );
   247 
   253 
   257 /**
   263 /**
   258  * @since 2.7.0
   264  * @since 2.7.0
   259  */
   265  */
   260 function install_dashboard() {
   266 function install_dashboard() {
   261 	?>
   267 	?>
   262 	<p><?php printf( __( 'Plugins extend and expand the functionality of WordPress. You may automatically install plugins from the <a href="%1$s">WordPress Plugin Directory</a> or upload a plugin in .zip format by clicking the button at the top of this page.' ), __( 'https://wordpress.org/plugins/' ) ); ?></p>
   268 	<p>
       
   269 		<?php
       
   270 		printf(
       
   271 			/* translators: %s: https://wordpress.org/plugins/ */
       
   272 			__( 'Plugins extend and expand the functionality of WordPress. You may automatically install plugins from the <a href="%s">WordPress Plugin Directory</a> or upload a plugin in .zip format by clicking the button at the top of this page.' ),
       
   273 			__( 'https://wordpress.org/plugins/' )
       
   274 		);
       
   275 		?>
       
   276 	</p>
   263 
   277 
   264 	<?php display_plugins_table(); ?>
   278 	<?php display_plugins_table(); ?>
   265 
   279 
   266 	<div class="plugins-popular-tags-wrapper">
   280 	<div class="plugins-popular-tags-wrapper">
   267 	<h2><?php _e( 'Popular tags' ); ?></h2>
   281 	<h2><?php _e( 'Popular tags' ); ?></h2>
   272 
   286 
   273 	echo '<p class="popular-tags">';
   287 	echo '<p class="popular-tags">';
   274 	if ( is_wp_error( $api_tags ) ) {
   288 	if ( is_wp_error( $api_tags ) ) {
   275 		echo $api_tags->get_error_message();
   289 		echo $api_tags->get_error_message();
   276 	} else {
   290 	} else {
   277 		//Set up the tags in a way which can be interpreted by wp_generate_tag_cloud()
   291 		// Set up the tags in a way which can be interpreted by wp_generate_tag_cloud().
   278 		$tags = array();
   292 		$tags = array();
   279 		foreach ( (array) $api_tags as $tag ) {
   293 		foreach ( (array) $api_tags as $tag ) {
   280 			$url                  = self_admin_url( 'plugin-install.php?tab=search&type=tag&s=' . urlencode( $tag['name'] ) );
   294 			$url                  = self_admin_url( 'plugin-install.php?tab=search&type=tag&s=' . urlencode( $tag['name'] ) );
   281 			$data                 = array(
   295 			$data                 = array(
   282 				'link'  => esc_url( $url ),
   296 				'link'  => esc_url( $url ),
   288 			$tags[ $tag['name'] ] = (object) $data;
   302 			$tags[ $tag['name'] ] = (object) $data;
   289 		}
   303 		}
   290 		echo wp_generate_tag_cloud(
   304 		echo wp_generate_tag_cloud(
   291 			$tags,
   305 			$tags,
   292 			array(
   306 			array(
       
   307 				/* translators: %s: Number of plugins. */
   293 				'single_text'   => __( '%s plugin' ),
   308 				'single_text'   => __( '%s plugin' ),
       
   309 				/* translators: %s: Number of plugins. */
   294 				'multiple_text' => __( '%s plugins' ),
   310 				'multiple_text' => __( '%s plugins' ),
   295 			)
   311 			)
   296 		);
   312 		);
   297 	}
   313 	}
   298 	echo '</p><br class="clear" /></div>';
   314 	echo '</p><br class="clear" /></div>';
   316 		<select name="type" id="typeselector">
   332 		<select name="type" id="typeselector">
   317 			<option value="term"<?php selected( 'term', $type ); ?>><?php _e( 'Keyword' ); ?></option>
   333 			<option value="term"<?php selected( 'term', $type ); ?>><?php _e( 'Keyword' ); ?></option>
   318 			<option value="author"<?php selected( 'author', $type ); ?>><?php _e( 'Author' ); ?></option>
   334 			<option value="author"<?php selected( 'author', $type ); ?>><?php _e( 'Author' ); ?></option>
   319 			<option value="tag"<?php selected( 'tag', $type ); ?>><?php _ex( 'Tag', 'Plugin Installer' ); ?></option>
   335 			<option value="tag"<?php selected( 'tag', $type ); ?>><?php _ex( 'Tag', 'Plugin Installer' ); ?></option>
   320 		</select>
   336 		</select>
   321 		<label><span class="screen-reader-text"><?php _e( 'Search Plugins' ); ?></span>
   337 		<label class="screen-reader-text" for="search-plugins"><?php _e( 'Search Plugins' ); ?></label>
   322 			<input type="search" name="s" value="<?php echo esc_attr( $term ); ?>" class="wp-filter-search" placeholder="<?php esc_attr_e( 'Search plugins...' ); ?>" />
   338 		<input type="search" name="s" id="search-plugins" value="<?php echo esc_attr( $term ); ?>" class="wp-filter-search" placeholder="<?php esc_attr_e( 'Search plugins...' ); ?>" />
   323 		</label>
       
   324 		<?php submit_button( __( 'Search Plugins' ), 'hide-if-js', false, false, array( 'id' => 'search-submit' ) ); ?>
   339 		<?php submit_button( __( 'Search Plugins' ), 'hide-if-js', false, false, array( 'id' => 'search-submit' ) ); ?>
   325 	</form>
   340 	</form>
   326 	<?php
   341 	<?php
   327 }
   342 }
   328 
   343 
   332  * @since 2.8.0
   347  * @since 2.8.0
   333  */
   348  */
   334 function install_plugins_upload() {
   349 function install_plugins_upload() {
   335 	?>
   350 	?>
   336 <div class="upload-plugin">
   351 <div class="upload-plugin">
   337 	<p class="install-help"><?php _e( 'If you have a plugin in a .zip format, you may install it by uploading it here.' ); ?></p>
   352 	<p class="install-help"><?php _e( 'If you have a plugin in a .zip format, you may install or update it by uploading it here.' ); ?></p>
   338 	<form method="post" enctype="multipart/form-data" class="wp-upload-form" action="<?php echo self_admin_url( 'update.php?action=upload-plugin' ); ?>">
   353 	<form method="post" enctype="multipart/form-data" class="wp-upload-form" action="<?php echo self_admin_url( 'update.php?action=upload-plugin' ); ?>">
   339 		<?php wp_nonce_field( 'plugin-upload' ); ?>
   354 		<?php wp_nonce_field( 'plugin-upload' ); ?>
   340 		<label class="screen-reader-text" for="pluginzip"><?php _e( 'Plugin zip file' ); ?></label>
   355 		<label class="screen-reader-text" for="pluginzip"><?php _e( 'Plugin zip file' ); ?></label>
   341 		<input type="file" id="pluginzip" name="pluginzip" />
   356 		<input type="file" id="pluginzip" name="pluginzip" accept=".zip" />
   342 		<?php submit_button( __( 'Install Now' ), '', 'install-plugin-submit', false ); ?>
   357 		<?php submit_button( __( 'Install Now' ), '', 'install-plugin-submit', false ); ?>
   343 	</form>
   358 	</form>
   344 </div>
   359 </div>
   345 	<?php
   360 	<?php
   346 }
   361 }
   352  */
   367  */
   353 function install_plugins_favorites_form() {
   368 function install_plugins_favorites_form() {
   354 	$user   = get_user_option( 'wporg_favorites' );
   369 	$user   = get_user_option( 'wporg_favorites' );
   355 	$action = 'save_wporg_username_' . get_current_user_id();
   370 	$action = 'save_wporg_username_' . get_current_user_id();
   356 	?>
   371 	?>
   357 	<p class="install-help"><?php _e( 'If you have marked plugins as favorites on WordPress.org, you can browse them here.' ); ?></p>
   372 	<p><?php _e( 'If you have marked plugins as favorites on WordPress.org, you can browse them here.' ); ?></p>
   358 	<form method="get">
   373 	<form method="get">
   359 		<input type="hidden" name="tab" value="favorites" />
   374 		<input type="hidden" name="tab" value="favorites" />
   360 		<p>
   375 		<p>
   361 			<label for="user"><?php _e( 'Your WordPress.org username:' ); ?></label>
   376 			<label for="user"><?php _e( 'Your WordPress.org username:' ); ?></label>
   362 			<input type="search" id="user" name="user" value="<?php echo esc_attr( $user ); ?>" />
   377 			<input type="search" id="user" name="user" value="<?php echo esc_attr( $user ); ?>" />
   386 		case 'install_plugins_recommended':
   401 		case 'install_plugins_recommended':
   387 			echo '<p>' . __( 'These suggestions are based on the plugins you and other users have installed.' ) . '</p>';
   402 			echo '<p>' . __( 'These suggestions are based on the plugins you and other users have installed.' ) . '</p>';
   388 			break;
   403 			break;
   389 		case 'install_plugins_beta':
   404 		case 'install_plugins_beta':
   390 			printf(
   405 			printf(
       
   406 				/* translators: %s: URL to "Features as Plugins" page. */
   391 				'<p>' . __( 'You are using a development version of WordPress. These feature plugins are also under development. <a href="%s">Learn more</a>.' ) . '</p>',
   407 				'<p>' . __( 'You are using a development version of WordPress. These feature plugins are also under development. <a href="%s">Learn more</a>.' ) . '</p>',
   392 				'https://make.wordpress.org/core/handbook/about/release-cycle/features-as-plugins/'
   408 				'https://make.wordpress.org/core/handbook/about/release-cycle/features-as-plugins/'
   393 			);
   409 			);
   394 			break;
   410 			break;
   395 	}
   411 	}
   404 /**
   420 /**
   405  * Determine the status we can perform on a plugin.
   421  * Determine the status we can perform on a plugin.
   406  *
   422  *
   407  * @since 3.0.0
   423  * @since 3.0.0
   408  *
   424  *
   409  * @param  array|object $api  Data about the plugin retrieved from the API.
   425  * @param array|object $api  Data about the plugin retrieved from the API.
   410  * @param  bool         $loop Optional. Disable further loops. Default false.
   426  * @param bool         $loop Optional. Disable further loops. Default false.
   411  * @return array {
   427  * @return array {
   412  *     Plugin installation status data.
   428  *     Plugin installation status data.
   413  *
   429  *
   414  *     @type string $status  Status of a plugin. Could be one of 'install', 'update_available', 'latest_installed' or 'newer_installed'.
   430  *     @type string $status  Status of a plugin. Could be one of 'install', 'update_available', 'latest_installed' or 'newer_installed'.
   415  *     @type string $url     Plugin installation URL.
   431  *     @type string $url     Plugin installation URL.
   421 	// This function is called recursively, $loop prevents further loops.
   437 	// This function is called recursively, $loop prevents further loops.
   422 	if ( is_array( $api ) ) {
   438 	if ( is_array( $api ) ) {
   423 		$api = (object) $api;
   439 		$api = (object) $api;
   424 	}
   440 	}
   425 
   441 
   426 	// Default to a "new" plugin
   442 	// Default to a "new" plugin.
   427 	$status      = 'install';
   443 	$status      = 'install';
   428 	$url         = false;
   444 	$url         = false;
   429 	$update_file = false;
   445 	$update_file = false;
   430 	$version     = '';
   446 	$version     = '';
   431 
   447 
   446 				break;
   462 				break;
   447 			}
   463 			}
   448 		}
   464 		}
   449 	}
   465 	}
   450 
   466 
   451 	if ( 'install' == $status ) {
   467 	if ( 'install' === $status ) {
   452 		if ( is_dir( WP_PLUGIN_DIR . '/' . $api->slug ) ) {
   468 		if ( is_dir( WP_PLUGIN_DIR . '/' . $api->slug ) ) {
   453 			$installed_plugin = get_plugins( '/' . $api->slug );
   469 			$installed_plugin = get_plugins( '/' . $api->slug );
   454 			if ( empty( $installed_plugin ) ) {
   470 			if ( empty( $installed_plugin ) ) {
   455 				if ( current_user_can( 'install_plugins' ) ) {
   471 				if ( current_user_can( 'install_plugins' ) ) {
   456 					$url = wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=' . $api->slug ), 'install-plugin_' . $api->slug );
   472 					$url = wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=' . $api->slug ), 'install-plugin_' . $api->slug );
   457 				}
   473 				}
   458 			} else {
   474 			} else {
   459 				$key         = array_keys( $installed_plugin );
   475 				$key = array_keys( $installed_plugin );
   460 				$key         = reset( $key ); //Use the first plugin regardless of the name, Could have issues for multiple-plugins in one directory if they share different version numbers
   476 				// Use the first plugin regardless of the name.
       
   477 				// Could have issues for multiple plugins in one directory if they share different version numbers.
       
   478 				$key = reset( $key );
       
   479 
   461 				$update_file = $api->slug . '/' . $key;
   480 				$update_file = $api->slug . '/' . $key;
   462 				if ( version_compare( $api->version, $installed_plugin[ $key ]['Version'], '=' ) ) {
   481 				if ( version_compare( $api->version, $installed_plugin[ $key ]['Version'], '=' ) ) {
   463 					$status = 'latest_installed';
   482 					$status = 'latest_installed';
   464 				} elseif ( version_compare( $api->version, $installed_plugin[ $key ]['Version'], '<' ) ) {
   483 				} elseif ( version_compare( $api->version, $installed_plugin[ $key ]['Version'], '<' ) ) {
   465 					$status  = 'newer_installed';
   484 					$status  = 'newer_installed';
   466 					$version = $installed_plugin[ $key ]['Version'];
   485 					$version = $installed_plugin[ $key ]['Version'];
   467 				} else {
   486 				} else {
   468 					//If the above update check failed, Then that probably means that the update checker has out-of-date information, force a refresh
   487 					// If the above update check failed, then that probably means that the update checker has out-of-date information, force a refresh.
   469 					if ( ! $loop ) {
   488 					if ( ! $loop ) {
   470 						delete_site_transient( 'update_plugins' );
   489 						delete_site_transient( 'update_plugins' );
   471 						wp_update_plugins();
   490 						wp_update_plugins();
   472 						return install_plugin_install_status( $api, true );
   491 						return install_plugin_install_status( $api, true );
   473 					}
   492 					}
   474 				}
   493 				}
   475 			}
   494 			}
   476 		} else {
   495 		} else {
   477 			// "install" & no directory with that slug
   496 			// "install" & no directory with that slug.
   478 			if ( current_user_can( 'install_plugins' ) ) {
   497 			if ( current_user_can( 'install_plugins' ) ) {
   479 				$url = wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=' . $api->slug ), 'install-plugin_' . $api->slug );
   498 				$url = wp_nonce_url( self_admin_url( 'update.php?action=install-plugin&plugin=' . $api->slug ), 'install-plugin_' . $api->slug );
   480 			}
   499 			}
   481 		}
   500 		}
   482 	}
   501 	}
   554 		'changelog'    => _x( 'Changelog', 'Plugin installer section title' ),
   573 		'changelog'    => _x( 'Changelog', 'Plugin installer section title' ),
   555 		'reviews'      => _x( 'Reviews', 'Plugin installer section title' ),
   574 		'reviews'      => _x( 'Reviews', 'Plugin installer section title' ),
   556 		'other_notes'  => _x( 'Other Notes', 'Plugin installer section title' ),
   575 		'other_notes'  => _x( 'Other Notes', 'Plugin installer section title' ),
   557 	);
   576 	);
   558 
   577 
   559 	// Sanitize HTML
   578 	// Sanitize HTML.
   560 	foreach ( (array) $api->sections as $section_name => $content ) {
   579 	foreach ( (array) $api->sections as $section_name => $content ) {
   561 		$api->sections[ $section_name ] = wp_kses( $content, $plugins_allowedtags );
   580 		$api->sections[ $section_name ] = wp_kses( $content, $plugins_allowedtags );
   562 	}
   581 	}
   563 
   582 
   564 	foreach ( array( 'version', 'author', 'requires', 'tested', 'homepage', 'downloaded', 'slug' ) as $key ) {
   583 	foreach ( array( 'version', 'author', 'requires', 'tested', 'homepage', 'downloaded', 'slug' ) as $key ) {
   567 		}
   586 		}
   568 	}
   587 	}
   569 
   588 
   570 	$_tab = esc_attr( $tab );
   589 	$_tab = esc_attr( $tab );
   571 
   590 
   572 	$section = isset( $_REQUEST['section'] ) ? wp_unslash( $_REQUEST['section'] ) : 'description'; // Default to the Description tab, Do not translate, API returns English.
   591 	// Default to the Description tab, Do not translate, API returns English.
       
   592 	$section = isset( $_REQUEST['section'] ) ? wp_unslash( $_REQUEST['section'] ) : 'description';
   573 	if ( empty( $section ) || ! isset( $api->sections[ $section ] ) ) {
   593 	if ( empty( $section ) || ! isset( $api->sections[ $section ] ) ) {
   574 		$section_titles = array_keys( (array) $api->sections );
   594 		$section_titles = array_keys( (array) $api->sections );
   575 		$section        = reset( $section_titles );
   595 		$section        = reset( $section_titles );
   576 	}
   596 	}
   577 
   597 
   635 			<?php } if ( ! empty( $api->author ) ) { ?>
   655 			<?php } if ( ! empty( $api->author ) ) { ?>
   636 				<li><strong><?php _e( 'Author:' ); ?></strong> <?php echo links_add_target( $api->author, '_blank' ); ?></li>
   656 				<li><strong><?php _e( 'Author:' ); ?></strong> <?php echo links_add_target( $api->author, '_blank' ); ?></li>
   637 			<?php } if ( ! empty( $api->last_updated ) ) { ?>
   657 			<?php } if ( ! empty( $api->last_updated ) ) { ?>
   638 				<li><strong><?php _e( 'Last Updated:' ); ?></strong>
   658 				<li><strong><?php _e( 'Last Updated:' ); ?></strong>
   639 					<?php
   659 					<?php
   640 					/* translators: %s: Time since the last update */
   660 					/* translators: %s: Human-readable time difference. */
   641 					printf( __( '%s ago' ), human_time_diff( strtotime( $api->last_updated ) ) );
   661 					printf( __( '%s ago' ), human_time_diff( strtotime( $api->last_updated ) ) );
   642 					?>
   662 					?>
   643 				</li>
   663 				</li>
   644 			<?php } if ( ! empty( $api->requires ) ) { ?>
   664 			<?php } if ( ! empty( $api->requires ) ) { ?>
   645 				<li>
   665 				<li>
   646 					<strong><?php _e( 'Requires WordPress Version:' ); ?></strong>
   666 					<strong><?php _e( 'Requires WordPress Version:' ); ?></strong>
   647 					<?php
   667 					<?php
   648 					/* translators: %s: version number */
   668 					/* translators: %s: Version number. */
   649 					printf( __( '%s or higher' ), $api->requires );
   669 					printf( __( '%s or higher' ), $api->requires );
   650 					?>
   670 					?>
   651 				</li>
   671 				</li>
   652 			<?php } if ( ! empty( $api->tested ) ) { ?>
   672 			<?php } if ( ! empty( $api->tested ) ) { ?>
   653 				<li><strong><?php _e( 'Compatible up to:' ); ?></strong> <?php echo $api->tested; ?></li>
   673 				<li><strong><?php _e( 'Compatible up to:' ); ?></strong> <?php echo $api->tested; ?></li>
   654 			<?php } if ( ! empty( $api->requires_php ) ) { ?>
   674 			<?php } if ( ! empty( $api->requires_php ) ) { ?>
   655 				<li>
   675 				<li>
   656 					<strong><?php _e( 'Requires PHP Version:' ); ?></strong>
   676 					<strong><?php _e( 'Requires PHP Version:' ); ?></strong>
   657 					<?php
   677 					<?php
   658 					/* translators: %s: version number */
   678 					/* translators: %s: Version number. */
   659 					printf( __( '%s or higher' ), $api->requires_php );
   679 					printf( __( '%s or higher' ), $api->requires_php );
   660 					?>
   680 					?>
   661 				</li>
   681 				</li>
   662 			<?php } if ( isset( $api->active_installs ) ) { ?>
   682 			<?php } if ( isset( $api->active_installs ) ) { ?>
   663 				<li><strong><?php _e( 'Active Installations:' ); ?></strong>
   683 				<li><strong><?php _e( 'Active Installations:' ); ?></strong>
   664 				<?php
   684 				<?php
   665 				if ( $api->active_installs >= 1000000 ) {
   685 				if ( $api->active_installs >= 1000000 ) {
   666 					$active_installs_millions = floor( $api->active_installs / 1000000 );
   686 					$active_installs_millions = floor( $api->active_installs / 1000000 );
   667 					printf(
   687 					printf(
       
   688 						/* translators: %s: Number of millions. */
   668 						_nx( '%s+ Million', '%s+ Million', $active_installs_millions, 'Active plugin installations' ),
   689 						_nx( '%s+ Million', '%s+ Million', $active_installs_millions, 'Active plugin installations' ),
   669 						number_format_i18n( $active_installs_millions )
   690 						number_format_i18n( $active_installs_millions )
   670 					);
   691 					);
   671 				} elseif ( 0 == $api->active_installs ) {
   692 				} elseif ( 0 == $api->active_installs ) {
   672 					_ex( 'Less Than 10', 'Active plugin installations' );
   693 					_ex( 'Less Than 10', 'Active plugin installations' );
   692 					'type'   => 'percent',
   713 					'type'   => 'percent',
   693 					'number' => $api->num_ratings,
   714 					'number' => $api->num_ratings,
   694 				)
   715 				)
   695 			);
   716 			);
   696 			?>
   717 			?>
   697 			<p aria-hidden="true" class="fyi-description"><?php printf( _n( '(based on %s rating)', '(based on %s ratings)', $api->num_ratings ), number_format_i18n( $api->num_ratings ) ); ?></p>
   718 			<p aria-hidden="true" class="fyi-description">
       
   719 				<?php
       
   720 				printf(
       
   721 					/* translators: %s: Number of ratings. */
       
   722 					_n( '(based on %s rating)', '(based on %s ratings)', $api->num_ratings ),
       
   723 					number_format_i18n( $api->num_ratings )
       
   724 				);
       
   725 				?>
       
   726 			</p>
   698 			<?php
   727 			<?php
   699 		}
   728 		}
   700 
   729 
   701 		if ( ! empty( $api->ratings ) && array_sum( (array) $api->ratings ) > 0 ) {
   730 		if ( ! empty( $api->ratings ) && array_sum( (array) $api->ratings ) > 0 ) {
   702 			?>
   731 			?>
   703 			<h3><?php _e( 'Reviews' ); ?></h3>
   732 			<h3><?php _e( 'Reviews' ); ?></h3>
   704 			<p class="fyi-description"><?php _e( 'Read all reviews on WordPress.org or write your own!' ); ?></p>
   733 			<p class="fyi-description"><?php _e( 'Read all reviews on WordPress.org or write your own!' ); ?></p>
   705 			<?php
   734 			<?php
   706 			foreach ( $api->ratings as $key => $ratecount ) {
   735 			foreach ( $api->ratings as $key => $ratecount ) {
   707 				// Avoid div-by-zero.
   736 				// Avoid div-by-zero.
   708 				$_rating = $api->num_ratings ? ( $ratecount / $api->num_ratings ) : 0;
   737 				$_rating    = $api->num_ratings ? ( $ratecount / $api->num_ratings ) : 0;
   709 				/* translators: 1: number of stars (used to determine singular/plural), 2: number of reviews */
       
   710 				$aria_label = esc_attr(
   738 				$aria_label = esc_attr(
   711 					sprintf(
   739 					sprintf(
   712 						_n( 'Reviews with %1$d star: %2$s. Opens in a new tab.', 'Reviews with %1$d stars: %2$s. Opens in a new tab.', $key ),
   740 						/* translators: 1: Number of stars (used to determine singular/plural), 2: Number of reviews. */
       
   741 						_n(
       
   742 							'Reviews with %1$d star: %2$s. Opens in a new tab.',
       
   743 							'Reviews with %1$d stars: %2$s. Opens in a new tab.',
       
   744 							$key
       
   745 						),
   713 						$key,
   746 						$key,
   714 						number_format_i18n( $ratecount )
   747 						number_format_i18n( $ratecount )
   715 					)
   748 					)
   716 				);
   749 				);
   717 				?>
   750 				?>
   718 				<div class="counter-container">
   751 				<div class="counter-container">
   719 						<span class="counter-label">
   752 						<span class="counter-label">
   720 							<a href="https://wordpress.org/support/plugin/<?php echo $api->slug; ?>/reviews/?filter=<?php echo $key; ?>"
   753 							<?php
   721 								target="_blank" aria-label="<?php echo $aria_label; ?>"><?php printf( _n( '%d star', '%d stars', $key ), $key ); ?></a>
   754 							printf(
       
   755 								'<a href="%s" target="_blank" aria-label="%s">%s</a>',
       
   756 								"https://wordpress.org/support/plugin/{$api->slug}/reviews/?filter={$key}",
       
   757 								$aria_label,
       
   758 								/* translators: %s: Number of stars. */
       
   759 								sprintf( _n( '%d star', '%d stars', $key ), $key )
       
   760 							);
       
   761 							?>
   722 						</span>
   762 						</span>
   723 						<span class="counter-back">
   763 						<span class="counter-back">
   724 							<span class="counter-bar" style="width: <?php echo 92 * $_rating; ?>px;"></span>
   764 							<span class="counter-bar" style="width: <?php echo 92 * $_rating; ?>px;"></span>
   725 						</span>
   765 						</span>
   726 					<span class="counter-count" aria-hidden="true"><?php echo number_format_i18n( $ratecount ); ?></span>
   766 					<span class="counter-count" aria-hidden="true"><?php echo number_format_i18n( $ratecount ); ?></span>
   764 	if ( ! $compatible_php ) {
   804 	if ( ! $compatible_php ) {
   765 		echo '<div class="notice notice-error notice-alt"><p>';
   805 		echo '<div class="notice notice-error notice-alt"><p>';
   766 		_e( '<strong>Error:</strong> This plugin <strong>requires a newer version of PHP</strong>.' );
   806 		_e( '<strong>Error:</strong> This plugin <strong>requires a newer version of PHP</strong>.' );
   767 		if ( current_user_can( 'update_php' ) ) {
   807 		if ( current_user_can( 'update_php' ) ) {
   768 			printf(
   808 			printf(
   769 				/* translators: %s: "Update PHP" page URL */
   809 				/* translators: %s: URL to Update PHP page. */
   770 				' ' . __( '<a href="%s" target="_blank">Click here to learn more about updating PHP</a>.' ),
   810 				' ' . __( '<a href="%s" target="_blank">Click here to learn more about updating PHP</a>.' ),
   771 				esc_url( wp_get_update_php_url() )
   811 				esc_url( wp_get_update_php_url() )
   772 			);
   812 			);
   773 
   813 
   774 			wp_update_php_annotation( '</p><p><em>', '</em>' );
   814 			wp_update_php_annotation( '</p><p><em>', '</em>' );
   785 	} elseif ( ! $compatible_wp ) {
   825 	} elseif ( ! $compatible_wp ) {
   786 		echo '<div class="notice notice-error notice-alt"><p>';
   826 		echo '<div class="notice notice-error notice-alt"><p>';
   787 		_e( '<strong>Error:</strong> This plugin <strong>requires a newer version of WordPress</strong>.' );
   827 		_e( '<strong>Error:</strong> This plugin <strong>requires a newer version of WordPress</strong>.' );
   788 		if ( current_user_can( 'update_core' ) ) {
   828 		if ( current_user_can( 'update_core' ) ) {
   789 			printf(
   829 			printf(
   790 				/* translators: %s: "Update WordPress" screen URL */
   830 				/* translators: %s: URL to WordPress Updates screen. */
   791 				' ' . __( '<a href="%s" target="_parent">Click here to update WordPress</a>.' ),
   831 				' ' . __( '<a href="%s" target="_parent">Click here to update WordPress</a>.' ),
   792 				self_admin_url( 'update-core.php' )
   832 				self_admin_url( 'update-core.php' )
   793 			);
   833 			);
   794 		}
   834 		}
   795 		echo '</p></div>';
   835 		echo '</p></div>';
   837 						);
   877 						);
   838 					}
   878 					}
   839 				}
   879 				}
   840 				break;
   880 				break;
   841 			case 'newer_installed':
   881 			case 'newer_installed':
   842 				/* translators: %s: Plugin version */
   882 				/* translators: %s: Plugin version. */
   843 				echo '<a class="button button-primary right disabled">' . sprintf( __( 'Newer Version (%s) Installed' ), $status['version'] ) . '</a>';
   883 				echo '<a class="button button-primary right disabled">' . sprintf( __( 'Newer Version (%s) Installed' ), $status['version'] ) . '</a>';
   844 				break;
   884 				break;
   845 			case 'latest_installed':
   885 			case 'latest_installed':
   846 				echo '<a class="button button-primary right disabled">' . __( 'Latest Version Installed' ) . '</a>';
   886 				echo '<a class="button button-primary right disabled">' . __( 'Latest Version Installed' ) . '</a>';
   847 				break;
   887 				break;