diff --git a/features/plugin-uninstall.feature b/features/plugin-uninstall.feature index bf67746f..8e389970 100644 --- a/features/plugin-uninstall.feature +++ b/features/plugin-uninstall.feature @@ -3,7 +3,7 @@ Feature: Uninstall a WordPress plugin Background: Given a WP install - Scenario: Uninstall an installed plugin + Scenario: Uninstall an installed plugin should uninstall, delete files When I run `wp plugin uninstall akismet` Then STDOUT should be: """ @@ -11,6 +11,39 @@ Feature: Uninstall a WordPress plugin Success: Uninstalled 1 of 1 plugins. """ And the return code should be 0 + And STDERR should be empty + And the wp-content/plugins/akismet directory should not exist + + Scenario: Uninstall an installed plugin but do not delete its files + When I run `wp plugin uninstall akismet --skip-delete` + Then STDOUT should be: + """ + Ran uninstall procedure for 'akismet' plugin without deleting. + Success: Uninstalled 1 of 1 plugins. + """ + And the return code should be 0 + And STDERR should be empty + And the wp-content/plugins/akismet directory should exist + + Scenario: Uninstall a plugin that is not in a folder and has custom name + When I run `wp plugin uninstall hello` + Then STDOUT should be: + """ + Uninstalled and deleted 'hello' plugin. + Success: Uninstalled 1 of 1 plugins. + """ + And the return code should be 0 + And STDERR should be empty + And the wp-content/plugins/hello.php file should not exist + + Scenario: Missing required inputs + When I try `wp plugin uninstall` + Then STDERR should be: + """ + Error: Please specify one or more plugins, or use --all. + """ + And the return code should be 1 + And STDOUT should be empty Scenario: Attempting to uninstall a plugin that's activated When I run `wp plugin activate akismet` @@ -25,6 +58,21 @@ Feature: Uninstall a WordPress plugin And STDOUT should be empty And the return code should be 1 + Scenario: Attempting to uninstall a plugin that's activated (using --deactivate) + When I run `wp plugin activate akismet` + Then STDOUT should not be empty + + When I try `wp plugin uninstall akismet --deactivate` + Then STDOUT should be: + """ + Deactivating 'akismet'... + Plugin 'akismet' deactivated. + Uninstalled and deleted 'akismet' plugin. + Success: Uninstalled 1 of 1 plugins. + """ + And STDERR should be empty + And the return code should be 0 + Scenario: Attempting to uninstall a plugin that doesn't exist When I try `wp plugin uninstall debug-bar` Then STDERR should be: @@ -43,12 +91,14 @@ Feature: Uninstall a WordPress plugin Success: Uninstalled 2 of 2 plugins. """ And the return code should be 0 + And STDERR should be empty When I run the previous command again Then STDOUT should be: """ Success: No plugins uninstalled. """ + And STDERR should be empty Scenario: Uninstall all installed plugins when one or more activated When I run `wp plugin activate --all` @@ -65,12 +115,14 @@ Feature: Uninstall a WordPress plugin Error: No plugins uninstalled. """ And the return code should be 1 + And STDOUT should be empty When I run `wp plugin uninstall --deactivate --all` Then STDOUT should contain: """ Success: Uninstalled 2 of 2 plugins. """ + And STDERR should be empty Scenario: Excluding a plugin from uninstallation when using --all switch When I try `wp plugin uninstall --all --exclude=akismet,hello` @@ -79,6 +131,7 @@ Feature: Uninstall a WordPress plugin Success: No plugins uninstalled. """ And the return code should be 0 + And STDERR should be empty Scenario: Excluding a missing plugin should not throw an error Given a WP install @@ -91,7 +144,7 @@ Feature: Uninstall a WordPress plugin And the return code should be 0 @require-wp-5.2 - Scenario: Uninstalling a plugin should remove its language pack too + Scenario: Uninstalling a plugin should remove its language pack Given a WP install And I run `wp plugin install wordpress-importer` And I run `wp core language install fr_FR` @@ -104,6 +157,7 @@ Feature: Uninstall a WordPress plugin """ And the wp-content/languages/plugins/wordpress-importer-fr_FR.mo file should exist And the wp-content/languages/plugins/wordpress-importer-fr_FR.po file should exist + And the wp-content/languages/plugins/wordpress-importer-fr_FR.l10n.php file should exist When I run `wp plugin uninstall wordpress-importer` Then STDOUT should contain: @@ -112,3 +166,29 @@ Feature: Uninstall a WordPress plugin """ And the wp-content/languages/plugins/wordpress-importer-fr_FR.mo file should not exist And the wp-content/languages/plugins/wordpress-importer-fr_FR.po file should not exist + And the wp-content/languages/plugins/wordpress-importer-fr_FR.l10n.php file should not exist + And STDERR should be empty + + @require-wp-5.2 + Scenario: Uninstalling a plugin should remove its update info + Given a WP install + And I run `wp plugin install wordpress-importer --version=0.6` + And I run `wp plugin status wordpress-importer` + + And I run `wp transient get --network update_plugins` + Then STDOUT should contain: + """ + wordpress-importer + """ + + When I run `wp plugin uninstall wordpress-importer` + Then STDOUT should contain: + """ + Success: + """ + + When I run `wp transient get --network update_plugins` + Then STDOUT should not contain: + """ + wordpress-importer + """ diff --git a/src/Plugin_Command.php b/src/Plugin_Command.php index ca8c346a..427d6067 100644 --- a/src/Plugin_Command.php +++ b/src/Plugin_Command.php @@ -1098,9 +1098,12 @@ public function uninstall( $args, $assoc_args = array() ) { return; } - $successes = 0; - $errors = 0; - $plugins = $this->fetcher->get_many( $args ); + $successes = 0; + $errors = 0; + $delete_errors = array(); + $deleted_plugin_files = array(); + + $plugins = $this->fetcher->get_many( $args ); if ( count( $plugins ) < count( $args ) ) { $errors = count( $args ) - count( $plugins ); } @@ -1140,6 +1143,7 @@ public function uninstall( $args, $assoc_args = array() ) { 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 ) { @@ -1148,13 +1152,35 @@ public function uninstall( $args, $assoc_args = array() ) { } } - if ( ! Utils\get_flag_value( $assoc_args, 'skip-delete' ) && $this->delete_plugin( $plugin ) ) { - WP_CLI::log( "Uninstalled and deleted '$plugin->name' plugin." ); + if ( ! Utils\get_flag_value( $assoc_args, 'skip-delete' ) ) { + if ( $this->delete_plugin( $plugin ) ) { + $deleted_plugin_files[] = $plugin->file; + WP_CLI::log( "Uninstalled and deleted '$plugin->name' plugin." ); + } else { + $delete_errors[] = $plugin->file; + WP_CLI::log( "Ran uninstall procedure for '$plugin->name' plugin. Deletion of plugin files failed" ); + ++$errors; + continue; + } } else { WP_CLI::log( "Ran uninstall procedure for '$plugin->name' plugin without deleting." ); } ++$successes; } + + // Remove deleted plugins from the plugin updates list. + $current = get_site_transient( $this->upgrade_transient ); + if ( $current ) { + // Don't remove the plugins that weren't deleted. + $deleted = array_diff( $deleted_plugin_files, $delete_errors ); + + foreach ( $deleted as $plugin_file ) { + unset( $current->response[ $plugin_file ] ); + } + + set_site_transient( $this->upgrade_transient, $current ); + } + if ( ! $this->chained_command ) { Utils\report_batch_operation_results( 'plugin', 'uninstall', count( $args ), $successes, $errors ); } @@ -1474,7 +1500,16 @@ private function get_details( $file ) { return $plugin_folder[ $plugin_file ]; } + /** + * Performs deletion of plugin files + * + * @param $plugin - Plugin fetcher object (name, file) + * @return bool - If plugin was deleted + */ private function delete_plugin( $plugin ) { + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound + do_action( 'delete_plugin', $plugin->file ); + $plugin_dir = dirname( $plugin->file ); if ( '.' === $plugin_dir ) { $plugin_dir = $plugin->file; @@ -1495,6 +1530,11 @@ private function delete_plugin( $plugin ) { $command = 'rm -rf '; } - return ! WP_CLI::launch( $command . escapeshellarg( $path ), false ); + $result = ! WP_CLI::launch( $command . escapeshellarg( $path ), false ); + + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound + do_action( 'deleted_plugin', $plugin->file, $result ); + + return $result; } }