summaryrefslogtreecommitdiffstats
path: root/wp-admin/includes/plugin.php
diff options
context:
space:
mode:
Diffstat (limited to 'wp-admin/includes/plugin.php')
-rw-r--r--wp-admin/includes/plugin.php146
1 files changed, 111 insertions, 35 deletions
diff --git a/wp-admin/includes/plugin.php b/wp-admin/includes/plugin.php
index f55bbd8..bcae273 100644
--- a/wp-admin/includes/plugin.php
+++ b/wp-admin/includes/plugin.php
@@ -45,6 +45,7 @@
* @since 1.5.0
* @since 5.3.0 Added support for `Requires at least` and `Requires PHP` headers.
* @since 5.8.0 Added support for `Update URI` header.
+ * @since 6.5.0 Added support for `Requires Plugins` header.
*
* @param string $plugin_file Absolute path to the main plugin file.
* @param bool $markup Optional. If the returned data should have HTML markup applied.
@@ -53,39 +54,41 @@
* @return array {
* Plugin data. Values will be empty if not supplied by the plugin.
*
- * @type string $Name Name of the plugin. Should be unique.
- * @type string $PluginURI Plugin URI.
- * @type string $Version Plugin version.
- * @type string $Description Plugin description.
- * @type string $Author Plugin author's name.
- * @type string $AuthorURI Plugin author's website address (if set).
- * @type string $TextDomain Plugin textdomain.
- * @type string $DomainPath Plugin's relative directory path to .mo files.
- * @type bool $Network Whether the plugin can only be activated network-wide.
- * @type string $RequiresWP Minimum required version of WordPress.
- * @type string $RequiresPHP Minimum required version of PHP.
- * @type string $UpdateURI ID of the plugin for update purposes, should be a URI.
- * @type string $Title Title of the plugin and link to the plugin's site (if set).
- * @type string $AuthorName Plugin author's name.
+ * @type string $Name Name of the plugin. Should be unique.
+ * @type string $PluginURI Plugin URI.
+ * @type string $Version Plugin version.
+ * @type string $Description Plugin description.
+ * @type string $Author Plugin author's name.
+ * @type string $AuthorURI Plugin author's website address (if set).
+ * @type string $TextDomain Plugin textdomain.
+ * @type string $DomainPath Plugin's relative directory path to .mo files.
+ * @type bool $Network Whether the plugin can only be activated network-wide.
+ * @type string $RequiresWP Minimum required version of WordPress.
+ * @type string $RequiresPHP Minimum required version of PHP.
+ * @type string $UpdateURI ID of the plugin for update purposes, should be a URI.
+ * @type string $RequiresPlugins Comma separated list of dot org plugin slugs.
+ * @type string $Title Title of the plugin and link to the plugin's site (if set).
+ * @type string $AuthorName Plugin author's name.
* }
*/
function get_plugin_data( $plugin_file, $markup = true, $translate = true ) {
$default_headers = array(
- 'Name' => 'Plugin Name',
- 'PluginURI' => 'Plugin URI',
- 'Version' => 'Version',
- 'Description' => 'Description',
- 'Author' => 'Author',
- 'AuthorURI' => 'Author URI',
- 'TextDomain' => 'Text Domain',
- 'DomainPath' => 'Domain Path',
- 'Network' => 'Network',
- 'RequiresWP' => 'Requires at least',
- 'RequiresPHP' => 'Requires PHP',
- 'UpdateURI' => 'Update URI',
+ 'Name' => 'Plugin Name',
+ 'PluginURI' => 'Plugin URI',
+ 'Version' => 'Version',
+ 'Description' => 'Description',
+ 'Author' => 'Author',
+ 'AuthorURI' => 'Author URI',
+ 'TextDomain' => 'Text Domain',
+ 'DomainPath' => 'Domain Path',
+ 'Network' => 'Network',
+ 'RequiresWP' => 'Requires at least',
+ 'RequiresPHP' => 'Requires PHP',
+ 'UpdateURI' => 'Update URI',
+ 'RequiresPlugins' => 'Requires Plugins',
// Site Wide Only is deprecated in favor of Network.
- '_sitewide' => 'Site Wide Only',
+ '_sitewide' => 'Site Wide Only',
);
$plugin_data = get_file_data( $plugin_file, $default_headers, 'plugin' );
@@ -478,14 +481,23 @@ function get_dropins() {
}
/**
- * Returns drop-ins that WordPress uses.
+ * Returns drop-in plugins that WordPress uses.
*
* Includes Multisite drop-ins only when is_multisite()
*
* @since 3.0.0
- * @return array[] Key is file name. The value is an array, with the first value the
- * purpose of the drop-in and the second value the name of the constant that must be
- * true for the drop-in to be used, or true if no constant is required.
+ *
+ * @return array[] {
+ * Key is file name. The value is an array of data about the drop-in.
+ *
+ * @type array ...$0 {
+ * Data about the drop-in.
+ *
+ * @type string $0 The purpose of the drop-in.
+ * @type string|true $1 Name of the constant that must be true for the drop-in
+ * to be used, or true if no constant is required.
+ * }
+ * }
*/
function _get_dropins() {
$dropins = array(
@@ -1009,6 +1021,7 @@ function delete_plugins( $plugins, $deprecated = '' ) {
foreach ( $translations as $translation => $data ) {
$wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.po' );
$wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.mo' );
+ $wp_filesystem->delete( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '.l10n.php' );
$json_translation_files = glob( WP_LANG_DIR . '/plugins/' . $plugin_slug . '-' . $translation . '-*.json' );
if ( $json_translation_files ) {
@@ -1113,13 +1126,14 @@ function validate_plugin( $plugin ) {
/**
* Validates the plugin requirements for WordPress version and PHP version.
*
- * Uses the information from `Requires at least` and `Requires PHP` headers
+ * Uses the information from `Requires at least`, `Requires PHP` and `Requires Plugins` headers
* defined in the plugin's main PHP file.
*
* @since 5.2.0
* @since 5.3.0 Added support for reading the headers from the plugin's
* main PHP file, with `readme.txt` as a fallback.
* @since 5.8.0 Removed support for using `readme.txt` as a fallback.
+ * @since 6.5.0 Added support for the 'Requires Plugins' header.
*
* @param string $plugin Path to the plugin file relative to the plugins directory.
* @return true|WP_Error True if requirements are met, WP_Error on failure.
@@ -1128,8 +1142,9 @@ function validate_plugin_requirements( $plugin ) {
$plugin_headers = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
$requirements = array(
- 'requires' => ! empty( $plugin_headers['RequiresWP'] ) ? $plugin_headers['RequiresWP'] : '',
- 'requires_php' => ! empty( $plugin_headers['RequiresPHP'] ) ? $plugin_headers['RequiresPHP'] : '',
+ 'requires' => ! empty( $plugin_headers['RequiresWP'] ) ? $plugin_headers['RequiresWP'] : '',
+ 'requires_php' => ! empty( $plugin_headers['RequiresPHP'] ) ? $plugin_headers['RequiresPHP'] : '',
+ 'requires_plugins' => ! empty( $plugin_headers['RequiresPlugins'] ) ? $plugin_headers['RequiresPlugins'] : '',
);
$compatible_wp = is_wp_version_compatible( $requirements['requires'] );
@@ -1184,6 +1199,62 @@ function validate_plugin_requirements( $plugin ) {
);
}
+ WP_Plugin_Dependencies::initialize();
+
+ if ( WP_Plugin_Dependencies::has_unmet_dependencies( $plugin ) ) {
+ $dependency_names = WP_Plugin_Dependencies::get_dependency_names( $plugin );
+ $unmet_dependencies = array();
+ $unmet_dependency_names = array();
+
+ foreach ( $dependency_names as $dependency => $dependency_name ) {
+ $dependency_file = WP_Plugin_Dependencies::get_dependency_filepath( $dependency );
+
+ if ( false === $dependency_file ) {
+ $unmet_dependencies['not_installed'][ $dependency ] = $dependency_name;
+ $unmet_dependency_names[] = $dependency_name;
+ } elseif ( is_plugin_inactive( $dependency_file ) ) {
+ $unmet_dependencies['inactive'][ $dependency ] = $dependency_name;
+ $unmet_dependency_names[] = $dependency_name;
+ }
+ }
+
+ $error_message = sprintf(
+ /* translators: 1: Plugin name, 2: Number of plugins, 3: A comma-separated list of plugin names. */
+ _n(
+ '<strong>Error:</strong> %1$s requires %2$d plugin to be installed and activated: %3$s.',
+ '<strong>Error:</strong> %1$s requires %2$d plugins to be installed and activated: %3$s.',
+ count( $unmet_dependency_names )
+ ),
+ $plugin_headers['Name'],
+ count( $unmet_dependency_names ),
+ implode( wp_get_list_item_separator(), $unmet_dependency_names )
+ );
+
+ if ( is_multisite() ) {
+ if ( current_user_can( 'manage_network_plugins' ) ) {
+ $error_message .= ' ' . sprintf(
+ /* translators: %s: Link to the plugins page. */
+ __( '<a href="%s">Manage plugins</a>.' ),
+ esc_url( network_admin_url( 'plugins.php' ) )
+ );
+ } else {
+ $error_message .= ' ' . __( 'Please contact your network administrator.' );
+ }
+ } else {
+ $error_message .= ' ' . sprintf(
+ /* translators: %s: Link to the plugins page. */
+ __( '<a href="%s">Manage plugins</a>.' ),
+ esc_url( admin_url( 'plugins.php' ) )
+ );
+ }
+
+ return new WP_Error(
+ 'plugin_missing_dependencies',
+ "<p>{$error_message}</p>",
+ $unmet_dependencies
+ );
+ }
+
return true;
}
@@ -2386,6 +2457,8 @@ function wp_add_privacy_policy_content( $plugin_name, $policy_text ) {
*
* @since 5.2.0
*
+ * @global WP_Paused_Extensions_Storage $_paused_plugins
+ *
* @param string $plugin Path to the plugin file relative to the plugins directory.
* @return bool True, if in the list of paused plugins. False, if not in the list.
*/
@@ -2408,6 +2481,8 @@ function is_plugin_paused( $plugin ) {
*
* @since 5.2.0
*
+ * @global WP_Paused_Extensions_Storage $_paused_plugins
+ *
* @param string $plugin Path to the plugin file relative to the plugins directory.
* @return array|false Array of error information as returned by `error_get_last()`,
* or false if none was recorded.
@@ -2482,7 +2557,8 @@ function resume_plugin( $plugin, $redirect = '' ) {
*
* @since 5.2.0
*
- * @global string $pagenow The filename of the current screen.
+ * @global string $pagenow The filename of the current screen.
+ * @global WP_Paused_Extensions_Storage $_paused_plugins
*/
function paused_plugins_notice() {
if ( 'plugins.php' === $GLOBALS['pagenow'] ) {