diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..c961598 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,24 @@ +# This file is for unifying the coding style for different editors and IDEs +# editorconfig.org + +# WordPress Coding Standards +# https://make.wordpress.org/core/handbook/coding-standards/ + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = tab + +[*.yml] +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.txt] +end_of_line = crlf diff --git a/.gitattributes b/.gitattributes index 4e632c3..5e7d7a3 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,6 +2,7 @@ .gitattributes export-ignore .gitignore export-ignore +.editorconfig export-ignore composer.lock export-ignore .phpcs.xml export-ignore .phpcs.xml.dist export-ignore diff --git a/CHANGES.md b/CHANGES.md index 3661fb0..a60811c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ [unreleased] +#### 1.14.0 / 2023-07-19 +* update _More details_ link +* fixed strange error between slug from different sources in PD part 2 +* update JS to correctly display Plugin Card button, thanks @costdev + #### 1.13.0 / 2023-07-10 * update version check * simplify plugin card notice diff --git a/phpcs.xml.dist b/phpcs.xml.dist index 1751c36..eee6a4b 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -10,6 +10,7 @@ */node_modules/* */tests/* */test-plugins/* + *.js diff --git a/plugin.php b/plugin.php index 0955e66..945d101 100644 --- a/plugin.php +++ b/plugin.php @@ -13,7 +13,7 @@ * Plugin URI: https://wordpress.org/plugins/wp-plugin-dependencies * Description: Parses 'Requires Plugins' header, add plugin install dependencies tab, and information about dependencies. * Author: Andy Fragen, Colin Stewart, Paul Biron - * Version: 1.13.0 + * Version: 1.14.0 * License: MIT * Network: true * Requires at least: 6.0 @@ -79,9 +79,6 @@ static function( $class_name ) { } ); - // Switch to simple plugin card. - add_filter( 'pd_simple_card', '__return_true' ); - if ( ! WP_PLUGIN_DEPENDENCIES1_COMMITTED ) { require_once __DIR__ . '/wp-admin/includes/class-wp-plugin-dependencies.php'; diff --git a/readme.txt b/readme.txt index 2577160..a7b452e 100644 --- a/readme.txt +++ b/readme.txt @@ -7,7 +7,7 @@ Network: true Requires at least: 6.0 Requires PHP: 5.6 Tested up to: 6.3 -Stable tag: 1.13.0 +Stable tag: 1.14.0 ## Description @@ -48,6 +48,11 @@ PRs should be made against the `develop` branch. ## Changelog +#### 1.14.0 / 2023-07-19 +* update _More details_ link +* fixed strange error between slug from different sources in PD part 2 +* update JS to correctly display Plugin Card button, thanks @costdev + #### 1.13.0 / 2023-07-10 * update version check * simplify plugin card notice diff --git a/wp-admin/includes/class-wp-plugin-dependencies-2.php b/wp-admin/includes/class-wp-plugin-dependencies-2.php index ec74bde..8d0eab2 100644 --- a/wp-admin/includes/class-wp-plugin-dependencies-2.php +++ b/wp-admin/includes/class-wp-plugin-dependencies-2.php @@ -146,7 +146,11 @@ public function add_plugin_card_dependencies( $response, $action, $args ) { * @return array */ public function upgrader_package_options( $options ) { - $options['hook_extra']['slug'] = $this->args->slug; + if ( isset( $options['hook_extra']['temp_backup'] ) ) { + $options['hook_extra']['slug'] = $options['hook_extra']['temp_backup']['slug']; + } else { + $options['hook_extra']['slug'] = $this->args->slug; + } remove_filter( 'upgrader_package_options', array( $this, 'upgrader_package_options' ), 10 ); return $options; diff --git a/wp-admin/includes/class-wp-plugin-dependencies.php b/wp-admin/includes/class-wp-plugin-dependencies.php index 895cc89..7d0e1e4 100644 --- a/wp-admin/includes/class-wp-plugin-dependencies.php +++ b/wp-admin/includes/class-wp-plugin-dependencies.php @@ -96,8 +96,11 @@ public function start() { add_action( 'admin_init', array( $this, 'modify_plugin_row' ), 15 ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) ); + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) ); add_action( 'admin_notices', array( $this, 'admin_notices' ) ); add_action( 'network_admin_notices', array( $this, 'admin_notices' ) ); + + add_action( 'wp_ajax_check_plugin_dependencies', array( $this, 'check_plugin_dependencies' ) ); } $required_headers = $this->parse_plugin_headers(); @@ -127,6 +130,28 @@ public function enqueue_styles() { } } + /** + * Enqueues scripts for plugin dependencies on the "Add New" plugins screen. + * + * @global string $wp_version The WordPress version string. + * @global string $pagenow The filename of the current screen. + * + * @return void + */ + public function enqueue_scripts() { + global $wp_version, $pagenow; + + if ( 'plugin-install.php' === $pagenow ) { + wp_enqueue_script( + 'wp-plugin-dependencies-updates', + plugins_url( 'wp-admin/js/updates.js', 'wp-plugin-dependencies/plugin.php' ), + array( 'updates' ), + $wp_version, + true + ); + } + } + /** * Run get_plugins() and store result. * @@ -293,7 +318,7 @@ public function empty_plugins_api_result( $res, $action, $args ) { public function get_dot_org_data() { global $pagenow; - if ( ! in_array( $pagenow, array( 'plugin-install.php', 'plugins.php' ), true ) ) { + if ( ! wp_doing_ajax() && ! in_array( $pagenow, array( 'plugin-install.php', 'plugins.php' ), true ) ) { return; } @@ -603,10 +628,11 @@ public function plugin_install_description_uninstalled( $description, $plugin ) if ( isset( $plugin_data['name'] ) && ! empty( $plugin_data['version'] ) ) { $more_details_link[ $slug ] = sprintf( - '%3$s', + '%4$s', esc_url( $url ), /* translators: %s: Plugin name. */ esc_attr( sprintf( __( 'More information about %s' ), $plugin_data['name'] ) ), + esc_attr( $plugin_data['name'] ), __( 'More details' ) ); $more_details_link[ $slug ] = esc_attr( $plugin_data['name'] ) . ' ' . $more_details_link[ $slug ]; @@ -1037,6 +1063,69 @@ private function get_requires_plugins_names( $data ) { return isset( $names ) ? $names : ''; } + + /** + * Handles checking plugin dependencies after a plugin is installed via AJAX. + */ + public function check_plugin_dependencies() { + check_ajax_referer( 'updates' ); + + if ( empty( $_POST['slug'] ) ) { + wp_send_json_error( + array( + 'slug' => '', + 'errorCode' => 'no_plugin_specified', + 'errorMessage' => __( 'No plugin specified.' ), + ) + ); + } + + $slug = sanitize_key( wp_unslash( $_POST['slug'] ) ); + $status = array( 'slug' => $slug ); + + if ( ! isset( $this->plugin_dirnames[ $slug ] ) ) { + $status['errorCode'] = 'plugin_not_installed'; + $status['errorMessage'] = __( 'The plugin is not installed.' ); + wp_send_json_error( $status ); + } + + $plugin_file = $this->plugin_dirnames[ $slug ]; + + if ( ! isset( $this->requires_plugins[ $plugin_file ]['RequiresPlugins'] ) ) { + $status['message'] = __( 'The plugin has no required plugins.' ); + wp_send_json_success( $status ); + } + + $dependencies = explode( ',', $this->requires_plugins[ $plugin_file ]['RequiresPlugins'] ); + $inactive_dependencies = array(); + foreach ( $dependencies as $dependency ) { + if ( is_plugin_inactive( $this->plugin_dirnames[ $dependency ] ) ) { + $inactive_dependencies[] = $dependency; + } + } + + if ( ! empty( $inactive_dependencies ) ) { + $inactive_dependency_names = array_map( + function( $dependency ) { + return $this->plugin_data[ $dependency ]['name']; + }, + $inactive_dependencies + ); + + $status['errorCode'] = 'inactive_dependencies'; + $status['errorMessage'] = sprintf( + /* translators: %s: A list of inactive dependency plugin names. */ + __( 'The following plugins must be activated first: %s.' ), + implode( ', ', $inactive_dependency_names ) + ); + $status['errorData'] = array_combine( $inactive_dependencies, $inactive_dependency_names ); + + wp_send_json_error( $status ); + } + + $status['message'] = __( 'All required plugins are installed and activated.' ); + wp_send_json_success( $status ); + } } ( new WP_Plugin_Dependencies() )->start(); diff --git a/wp-admin/includes/plugin-install.php b/wp-admin/includes/plugin-install.php index 3a98fb3..4cd58bf 100644 --- a/wp-admin/includes/plugin-install.php +++ b/wp-admin/includes/plugin-install.php @@ -7,12 +7,14 @@ * * @package WordPress * @subpackage Administration - * @since 6.3.0 + * @since 6.4.0 */ /** * Gets the markup for the plugin install action button. * + * @since 6.4.0 + * * @param string $name Plugin name. * @param array|object $data { * An array or object of plugin data. Can be retrieved from the API. @@ -23,7 +25,6 @@ * } * @param bool $compatible_php The result of a PHP compatibility check. * @param bool $compatible_wp The result of a WP compatibility check. - * * @return string $button The markup for the dependency row button. */ function wp_get_plugin_action_button( $name, $data, $compatible_php, $compatible_wp ) { @@ -54,10 +55,6 @@ function wp_get_plugin_action_button( $name, $data, $compatible_php, $compatible $all_plugin_dependencies_installed = $installed_plugin_dependencies_count === $plugin_dependencies_count; $all_plugin_dependencies_active = $active_plugin_dependencies_count === $plugin_dependencies_count; - if ( apply_filters( 'pd_simple_card', false ) ) { - $plugin_dependency_met = true; - } - sprintf( '%s', esc_attr( $data->slug ), diff --git a/wp-admin/js/updates.js b/wp-admin/js/updates.js new file mode 100644 index 0000000..1101c33 --- /dev/null +++ b/wp-admin/js/updates.js @@ -0,0 +1,83 @@ +(function( $, wp ) { + var $document = $( document ), + __ = wp.i18n.__, + _x = wp.i18n._x, + sprintf = wp.i18n.sprintf; + + wp.updates.installPluginSuccess = function( response ) { + var $message = $( '.plugin-card-' + response.slug ).find( '.install-now' ); + + $message + .removeClass( 'updating-message' ) + .addClass( 'updated-message installed button-disabled' ) + .attr( + 'aria-label', + sprintf( + /* translators: %s: Plugin name and version. */ + _x( '%s installed!', 'plugin' ), + response.pluginName + ) + ) + .text( _x( 'Installed!', 'plugin' ) ); + + wp.a11y.speak( __( 'Installation completed successfully.' ) ); + + $document.trigger( 'wp-plugin-install-success', response ); + + if ( response.activateUrl ) { + setTimeout( function() { + wp.updates.ajax( + 'check_plugin_dependencies', + { + slug: response.slug, + success: function() { + // Transform the 'Install' button into an 'Activate' button. + $message.removeClass( 'install-now installed button-disabled updated-message' ) + .addClass( 'activate-now button-primary' ) + .attr( 'href', response.activateUrl ); + + if ( 'plugins-network' === pagenow ) { + $message + .attr( + 'aria-label', + sprintf( + /* translators: %s: Plugin name. */ + _x( 'Network Activate %s', 'plugin' ), + response.pluginName + ) + ) + .text( __( 'Network Activate' ) ); + } else { + $message + .attr( + 'aria-label', + sprintf( + /* translators: %s: Plugin name. */ + _x( 'Activate %s', 'plugin' ), + response.pluginName + ) + ) + .text( __( 'Activate' ) ); + } + }, + error: function( error ) { + $message + .removeClass( 'install-now installed updated-message' ) + .addClass( 'activate-now button-primary' ) + .attr( + 'aria-label', + sprintf( + /* translators: 1: Plugin name, 2. The reason the plugin cannot be activated. */ + _x( 'Cannot activate %1$s. %2$s', 'plugin' ), + response.pluginName, + error.errorMessage + ) + ) + .text( __( 'Cannot Activate' ) ); + } + } + ); + }, 1000 ); + } + }; +})( jQuery, window.wp, window._wpUpdatesSettings );