From 152fba56cc1e3427efa775f251f9b5596282db8b Mon Sep 17 00:00:00 2001 From: Dominik Schilling Date: Thu, 4 Aug 2016 22:17:44 +0000 Subject: [PATCH] Upgrade/Install: Make some install/update failures more verbose. An upgrader class is used in conjunction with an upgrader skin class. A skin class handles the logging for an upgrade and informs a user about the progress and failures. The current Ajax install/update handlers are using the `Automatic_Upgrader_Skin` class because during an Ajax request no output is intended. The difference between Ajax updates and automatic updates is that you will see the full log (usually by email) while Ajax updates focus only on success or failure. For that `Automatic_Upgrader_Skin` has one disadvantage: It doesn't provide a way to retrieve failure messages which were passed through `WP_Upgrader_Skin::error()` by the upgrader. To solve this issue a new skin `WP_Ajax_Upgrader_Skin` has been introduced. The skin extends `Automatic_Upgrader_Skin` and overrides the `error()` and `feedback()` methods to intercept all errors, which can be a `WP_Error` object or a string. This updates all four Ajax handler for installing/updating themes/plugins to use the new skin. They now also check the skin for any intercepted errors and pass them on to the user. Props flixos90, obenland, ocean90. Props DrewAPicture, pento for review. Fixes #37531. git-svn-id: https://develop.svn.wordpress.org/trunk@38199 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-admin/includes/ajax-actions.php | 64 ++++++--- .../includes/class-wp-ajax-upgrader-skin.php | 132 ++++++++++++++++++ .../includes/class-wp-upgrader-skins.php | 3 + src/wp-admin/includes/class-wp-upgrader.php | 3 + src/wp-admin/js/updates.js | 4 +- 5 files changed, 183 insertions(+), 23 deletions(-) create mode 100644 src/wp-admin/includes/class-wp-ajax-upgrader-skin.php diff --git a/src/wp-admin/includes/ajax-actions.php b/src/wp-admin/includes/ajax-actions.php index 5e86d792dd..dc965d63cc 100644 --- a/src/wp-admin/includes/ajax-actions.php +++ b/src/wp-admin/includes/ajax-actions.php @@ -3345,16 +3345,25 @@ function wp_ajax_install_theme() { wp_send_json_error( $status ); } - $upgrader = new Theme_Upgrader( new Automatic_Upgrader_Skin() ); + $skin = new WP_Ajax_Upgrader_Skin(); + $upgrader = new Theme_Upgrader( $skin ); $result = $upgrader->install( $api->download_link ); if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { - $status['debug'] = $upgrader->skin->get_upgrade_messages(); + $status['debug'] = $skin->get_upgrade_messages(); } if ( is_wp_error( $result ) ) { + $status['errorCode'] = $result->get_error_code(); $status['errorMessage'] = $result->get_error_message(); wp_send_json_error( $status ); + } elseif ( is_wp_error( $skin->result ) ) { + $status['errorCode'] = $skin->result->get_error_code(); + $status['errorMessage'] = $skin->result->get_error_message(); + wp_send_json_error( $status ); + } elseif ( $skin->get_errors()->get_error_code() ) { + $status['errorMessage'] = $skin->get_error_messages(); + wp_send_json_error( $status ); } elseif ( is_null( $result ) ) { global $wp_filesystem; @@ -3437,14 +3446,22 @@ function wp_ajax_update_theme() { wp_update_themes(); } - $upgrader = new Theme_Upgrader( new Automatic_Upgrader_Skin() ); + $skin = new WP_Ajax_Upgrader_Skin(); + $upgrader = new Theme_Upgrader( $skin ); $result = $upgrader->bulk_upgrade( array( $stylesheet ) ); if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { - $status['debug'] = $upgrader->skin->get_upgrade_messages(); + $status['debug'] = $skin->get_upgrade_messages(); } - if ( is_array( $result ) && ! empty( $result[ $stylesheet ] ) ) { + if ( is_wp_error( $skin->result ) ) { + $status['errorCode'] = $skin->result->get_error_code(); + $status['errorMessage'] = $skin->result->get_error_message(); + wp_send_json_error( $status ); + } elseif ( $skin->get_errors()->get_error_code() ) { + $status['errorMessage'] = $skin->get_error_messages(); + wp_send_json_error( $status ); + } elseif ( is_array( $result ) && ! empty( $result[ $stylesheet ] ) ) { // Theme is already at the latest version. if ( true === $result[ $stylesheet ] ) { @@ -3458,10 +3475,6 @@ function wp_ajax_update_theme() { } wp_send_json_success( $status ); - } elseif ( is_wp_error( $upgrader->skin->result ) ) { - $status['errorCode'] = $upgrader->skin->result->get_error_code(); - $status['errorMessage'] = $upgrader->skin->result->get_error_message(); - wp_send_json_error( $status ); } elseif ( false === $result ) { global $wp_filesystem; @@ -3594,16 +3607,25 @@ function wp_ajax_install_plugin() { $status['pluginName'] = $api->name; - $upgrader = new Plugin_Upgrader( new Automatic_Upgrader_Skin() ); + $skin = new WP_Ajax_Upgrader_Skin(); + $upgrader = new Plugin_Upgrader( $skin ); $result = $upgrader->install( $api->download_link ); if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { - $status['debug'] = $upgrader->skin->get_upgrade_messages(); + $status['debug'] = $skin->get_upgrade_messages(); } if ( is_wp_error( $result ) ) { + $status['errorCode'] = $result->get_error_code(); $status['errorMessage'] = $result->get_error_message(); wp_send_json_error( $status ); + } elseif ( is_wp_error( $skin->result ) ) { + $status['errorCode'] = $skin->result->get_error_code(); + $status['errorMessage'] = $skin->result->get_error_message(); + wp_send_json_error( $status ); + } elseif ( $skin->get_errors()->get_error_code() ) { + $status['errorMessage'] = $skin->get_error_messages(); + wp_send_json_error( $status ); } elseif ( is_null( $result ) ) { global $wp_filesystem; @@ -3680,19 +3702,22 @@ function wp_ajax_update_plugin() { wp_update_plugins(); - $skin = new Automatic_Upgrader_Skin(); + $skin = new WP_Ajax_Upgrader_Skin(); $upgrader = new Plugin_Upgrader( $skin ); $result = $upgrader->bulk_upgrade( array( $plugin ) ); if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) { - $status['debug'] = $upgrader->skin->get_upgrade_messages(); + $status['debug'] = $skin->get_upgrade_messages(); } - if ( is_array( $result ) && empty( $result[ $plugin ] ) && is_wp_error( $skin->result ) ) { - $result = $skin->result; - } - - if ( is_array( $result ) && ! empty( $result[ $plugin ] ) ) { + if ( is_wp_error( $skin->result ) ) { + $status['errorCode'] = $skin->result->get_error_code(); + $status['errorMessage'] = $skin->result->get_error_message(); + wp_send_json_error( $status ); + } elseif ( $skin->get_errors()->get_error_code() ) { + $status['errorMessage'] = $skin->get_error_messages(); + wp_send_json_error( $status ); + } elseif ( is_array( $result ) && ! empty( $result[ $plugin ] ) ) { $plugin_update_data = current( $result ); /* @@ -3716,9 +3741,6 @@ function wp_ajax_update_plugin() { $status['newVersion'] = sprintf( __( 'Version %s' ), $plugin_data['Version'] ); } wp_send_json_success( $status ); - } elseif ( is_wp_error( $result ) ) { - $status['errorMessage'] = $result->get_error_message(); - wp_send_json_error( $status ); } elseif ( false === $result ) { global $wp_filesystem; diff --git a/src/wp-admin/includes/class-wp-ajax-upgrader-skin.php b/src/wp-admin/includes/class-wp-ajax-upgrader-skin.php new file mode 100644 index 0000000000..8698e20445 --- /dev/null +++ b/src/wp-admin/includes/class-wp-ajax-upgrader-skin.php @@ -0,0 +1,132 @@ +errors = new WP_Error(); + } + + /** + * Retrieves the list of errors. + * + * @since 4.6.0 + * @access public + * + * @return WP_Error Errors during an upgrade. + */ + public function get_errors() { + return $this->errors; + } + + /** + * Retrieves a string for error messages. + * + * @since 4.6.0 + * @access public + * + * @return string Error messages during an upgrade. + */ + public function get_error_messages() { + $messages = array(); + + foreach ( $this->errors->get_error_codes() as $error_code ) { + if ( $this->errors->get_error_data( $error_code ) && is_string( $this->errors->get_error_data( $error_code ) ) ) { + $messages[] = $this->errors->get_error_message( $error_code ) . ' ' . esc_html( strip_tags( $this->errors->get_error_data( $error_code ) ) ); + } else { + $messages[] = $this->errors->get_error_message( $error_code ); + } + } + + return implode( ', ', $messages ); + } + + /** + * Stores a log entry for an error. + * + * @since 4.6.0 + * @access public + * + * @param string|WP_Error $errors Errors. + */ + public function error( $errors ) { + if ( is_string( $errors ) ) { + $string = $errors; + if ( ! empty( $this->upgrader->strings[ $string ] ) ) { + $string = $this->upgrader->strings[ $string ]; + } + + if ( false !== strpos( $string, '%' ) ) { + $args = func_get_args(); + $args = array_splice( $args, 1 ); + if ( ! empty( $args ) ) { + $string = vsprintf( $string, $args ); + } + } + + // Count existing errors to generate an unique error code. + $errors_count = count( $errors->get_error_codes() ); + $this->errors->add( 'unknown_upgrade_error_' . $errors_count + 1 , $string ); + } elseif ( is_wp_error( $errors ) ) { + foreach ( $errors->get_error_codes() as $error_code ) { + $this->errors->add( $error_code, $errors->get_error_message( $error_code ), $errors->get_error_data( $error_code ) ); + } + } + + $args = func_get_args(); + call_user_func_array( array( $this, 'parent::error' ), $args ); + } + + /** + * Stores a log entry. + * + * @since 4.6.0 + * @access public + * + * @param string|array|WP_Error $data Log entry data. + */ + public function feedback( $data ) { + if ( is_wp_error( $data ) ) { + foreach ( $data->get_error_codes() as $error_code ) { + $this->errors->add( $error_code, $data->get_error_message( $error_code ), $data->get_error_data( $error_code ) ); + } + } + + $args = func_get_args(); + call_user_func_array( array( $this, 'parent::feedback' ), $args ); + } +} diff --git a/src/wp-admin/includes/class-wp-upgrader-skins.php b/src/wp-admin/includes/class-wp-upgrader-skins.php index 6b8fbdb735..8b2789da0c 100644 --- a/src/wp-admin/includes/class-wp-upgrader-skins.php +++ b/src/wp-admin/includes/class-wp-upgrader-skins.php @@ -36,3 +36,6 @@ require_once ABSPATH . 'wp-admin/includes/class-language-pack-upgrader-skin.php' /** Automatic_Upgrader_Skin class */ require_once ABSPATH . 'wp-admin/includes/class-automatic-upgrader-skin.php'; + +/** WP_Ajax_Upgrader_Skin class */ +require_once ABSPATH . 'wp-admin/includes/class-wp-ajax-upgrader-skin.php'; diff --git a/src/wp-admin/includes/class-wp-upgrader.php b/src/wp-admin/includes/class-wp-upgrader.php index d5e10c4c64..2276152cc4 100644 --- a/src/wp-admin/includes/class-wp-upgrader.php +++ b/src/wp-admin/includes/class-wp-upgrader.php @@ -39,6 +39,9 @@ require_once ABSPATH . 'wp-admin/includes/class-language-pack-upgrader-skin.php' /** Automatic_Upgrader_Skin class */ require_once ABSPATH . 'wp-admin/includes/class-automatic-upgrader-skin.php'; +/** WP_Ajax_Upgrader_Skin class */ +require_once ABSPATH . 'wp-admin/includes/class-wp-ajax-upgrader-skin.php'; + /** * Core class used for upgrading/installing a local set of files via * the Filesystem Abstraction classes from a Zip file. diff --git a/src/wp-admin/js/updates.js b/src/wp-admin/js/updates.js index d4274fef6a..5df8ded165 100644 --- a/src/wp-admin/js/updates.js +++ b/src/wp-admin/js/updates.js @@ -244,7 +244,7 @@ * @param {string=} response.errorCode Optional. Error code for an error that occurred. */ wp.updates.ajaxAlways = function( response ) { - if ( ! response.errorCode && 'unable_to_connect_to_filesystem' !== response.errorCode ) { + if ( ! response.errorCode || 'unable_to_connect_to_filesystem' !== response.errorCode ) { wp.updates.ajaxLocked = false; wp.updates.queueChecker(); } @@ -1533,7 +1533,7 @@ * @returns {boolean} Whether there is an error that needs to be handled or not. */ wp.updates.maybeHandleCredentialError = function( response, action ) { - if ( response.errorCode && 'unable_to_connect_to_filesystem' === response.errorCode ) { + if ( wp.updates.shouldRequestFilesystemCredentials && response.errorCode && 'unable_to_connect_to_filesystem' === response.errorCode ) { wp.updates.credentialError( response, action ); return true; }