diff --git a/src/wp-admin/css/forms.css b/src/wp-admin/css/forms.css index 133713fbfd..19ff006d4d 100644 --- a/src/wp-admin/css/forms.css +++ b/src/wp-admin/css/forms.css @@ -848,6 +848,27 @@ table.form-table td .updated p { margin-bottom: 5px; } +/*------------------------------------------------------------------------------ + Credentials check dialog for Install and Updates +------------------------------------------------------------------------------*/ + +.request-filesystem-credentials-dialog { + display: none; +} + +.request-filesystem-credentials-dialog .notification-dialog { + top: 15%; +} + +.request-filesystem-credentials-dialog-content { + margin: 25px; +} + +.request-filesystem-credentials-dialog-content input[type="text"], +.request-filesystem-credentials-dialog-content input[type="password"] { + width: 85%; +} + /* =Media Queries -------------------------------------------------------------- */ diff --git a/src/wp-admin/includes/ajax-actions.php b/src/wp-admin/includes/ajax-actions.php index 81ba091519..f61c305caa 100644 --- a/src/wp-admin/includes/ajax-actions.php +++ b/src/wp-admin/includes/ajax-actions.php @@ -2913,6 +2913,10 @@ function wp_ajax_install_plugin() { if ( is_wp_error( $result ) ) { $status['error'] = $result->get_error_message(); wp_send_json_error( $status ); + } else if ( is_null( $result ) ) { + $status['errorCode'] = 'unable_to_connect_to_filesystem'; + $status['error'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' ); + wp_send_json_error( $status ); } $plugin_status = install_plugin_install_status( $api ); @@ -2933,10 +2937,16 @@ function wp_ajax_update_plugin() { $plugin = urldecode( $_POST['plugin'] ); $status = array( - 'update' => 'plugin', - 'plugin' => $plugin, - 'slug' => sanitize_key( $_POST['slug'] ), + 'update' => 'plugin', + 'plugin' => $plugin, + 'slug' => sanitize_key( $_POST['slug'] ), + 'oldVersion' => '', + 'newVersion' => '', ); + $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin ); + if ( $plugin_data['Version'] ) { + $status['oldVersion'] = sprintf( __( 'Version %s' ), $plugin_data['Version'] ); + } if ( ! current_user_can( 'update_plugins' ) ) { $status['error'] = __( 'You do not have sufficient permissions to update plugins on this site.' ); @@ -2956,15 +2966,31 @@ function wp_ajax_update_plugin() { $result = $upgrader->bulk_upgrade( array( $plugin ) ); if ( is_array( $result ) ) { - $result = $result[ $plugin ]; - } - - if ( is_wp_error( $result ) ) { + $plugin_update_data = current( $result ); + /* + * If the `update_plugins` site transient is empty (e.g. when you update + * two plugins in quick succession before the transient repopulates), + * this may be the return. + * + * Preferably something can be done to ensure `update_plugins` isn't empty. + * For now, surface some sort of error here. + */ + if ( $plugin_update_data === true ) { + wp_send_json_error( $status ); + } + $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin ); + if ( $plugin_data['Version'] ) { + $status['newVersion'] = sprintf( __( 'Version %s' ), $plugin_data['Version'] ); + } + wp_send_json_success( $status ); + } else if ( is_wp_error( $result ) ) { $status['error'] = $result->get_error_message(); wp_send_json_error( $status ); + } else if ( is_bool( $result ) && ! $result ) { + $status['errorCode'] = 'unable_to_connect_to_filesystem'; + $status['error'] = __( 'Unable to connect to the filesystem. Please confirm your credentials.' ); + wp_send_json_error( $status ); } - - wp_send_json_success( $status ); } /** diff --git a/src/wp-admin/includes/file.php b/src/wp-admin/includes/file.php index e398e9702c..9f3e6bab24 100644 --- a/src/wp-admin/includes/file.php +++ b/src/wp-admin/includes/file.php @@ -1192,3 +1192,29 @@ submit_button( __( 'Proceed' ), 'button', 'upgrade' ); +
+
+
+
+ +
+
+
+ ' + message + '
' ); + }; /** * Send an Ajax request to the server to install a plugin. @@ -216,14 +281,19 @@ window.wp = window.wp || {}; wp.updates.updateLock = true; var data = { - '_ajax_nonce': wp.updates.ajaxNonce, - 'slug': slug + _ajax_nonce: wp.updates.ajaxNonce, + slug: slug, + username: wp.updates.filesystemCredentials.ftp.username, + password: wp.updates.filesystemCredentials.ftp.password, + hostname: wp.updates.filesystemCredentials.ftp.hostname, + connection_type: wp.updates.filesystemCredentials.ftp.connectionType, + public_key: wp.updates.filesystemCredentials.ssh.publicKey, + private_key: wp.updates.filesystemCredentials.ssh.privateKey }; wp.ajax.post( 'install-plugin', data ) .done( wp.updates.installSuccess ) - .fail( wp.updates.installError ) - .always( wp.updates.updateAlways ); + .fail( wp.updates.installError ); }; /** @@ -239,6 +309,14 @@ window.wp = window.wp || {}; $message.removeClass( 'updating-message' ).addClass( 'updated-message button-disabled' ); $message.text( wp.updates.l10n.installed ); wp.a11y.speak( wp.updates.l10n.installedMsg ); + wp.updates.updateDoneSuccessfully = true; + + /* + * The lock can be released since the update was successful, + * and any other updates can commence. + */ + wp.updates.updateLock = false; + wp.updates.queueChecker(); }; /** @@ -250,11 +328,36 @@ window.wp = window.wp || {}; */ wp.updates.installError = function( response ) { var $message = $( '.plugin-card-' + response.slug ).find( '.install-now' ); + wp.updates.updateDoneSuccessfully = false; + if ( response.errorCode && response.errorCode == 'unable_to_connect_to_filesystem' ) { + wp.updates.credentialError( response, 'install-plugin' ); + return; + } $message.removeClass( 'updating-message' ); $message.text( wp.updates.l10n.installNow ); + + wp.updates.updateLock = false; }; + /** + * Events that need to happen when there is a credential error + * + * @since 4.2.0 + */ + wp.updates.credentialError = function( response, type ) { + wp.updates.updateQueue.push( { + 'type': type, + 'data': { + // Not cool that we're depending on response for this data. + // This would feel more whole in a view all tied together. + plugin: response.plugin, + slug: response.slug + } + } ); + wp.updates.showErrorInCredentialsForm( response.error ); + wp.updates.requestFilesystemCredentials(); + }; /** * If an install/update job has been placed in the queue, queueChecker pulls it out and runs it. @@ -282,9 +385,55 @@ window.wp = window.wp || {}; } }; + + /** + * Request the users filesystem credentials if we don't have them already + * + * @since 4.2.0 + */ + wp.updates.requestFilesystemCredentials = function() { + if ( wp.updates.updateDoneSuccessfully === false ) { + wp.updates.updateLock = true; + $( 'body' ).addClass( 'modal-open' ); + $( '#request-filesystem-credentials-dialog' ).show(); + } + }; + $( document ).ready( function() { + /* + * Check whether a user needs to submit filesystem credentials based on whether + * the form was output on the page server-side. + * + * @see {wp_print_request_filesystem_credentials_modal() in PHP} + */ + wp.updates.shouldRequestFilesystemCredentials = ( $( '#request-filesystem-credentials-dialog' ).length <= 0 ) ? false : true; + + // File system credentials form submit noop-er / handler. + $( '#request-filesystem-credentials-dialog form' ).on( 'submit', function() { + // Persist the credentials input by the user for the duration of the page load. + wp.updates.filesystemCredentials.ftp.hostname = $('#hostname').val(); + wp.updates.filesystemCredentials.ftp.username = $('#username').val(); + wp.updates.filesystemCredentials.ftp.password = $('#password').val(); + wp.updates.filesystemCredentials.ftp.connectionType = $('input[name="connection_type"]:checked').val(); + wp.updates.filesystemCredentials.ssh.publicKey = $('#public_key').val(); + wp.updates.filesystemCredentials.ssh.privateKey = $('#private_key').val(); + + $( '#request-filesystem-credentials-dialog' ).hide(); + $( 'body' ).removeClass( 'modal-open' ); + + // Unlock and invoke the queue. + wp.updates.updateLock = false; + wp.updates.queueChecker(); + + return false; + }); + + // Click handler for plugin updates in List Table view. $( '.plugin-update-tr .update-link' ).on( 'click', function( e ) { e.preventDefault(); + if ( wp.updates.shouldRequestFilesystemCredentials && ! wp.updates.updateLock ) { + wp.updates.requestFilesystemCredentials(); + } var $row = $( e.target ).parents( '.plugin-update-tr' ); wp.updates.updatePlugin( $row.data( 'plugin' ), $row.data( 'slug' ) ); } ); @@ -315,6 +464,9 @@ window.wp = window.wp || {}; $( '.plugin-card .install-now' ).on( 'click', function( e ) { e.preventDefault(); + if ( wp.updates.shouldRequestFilesystemCredentials && ! wp.updates.updateLock ) { + wp.updates.requestFilesystemCredentials(); + } var $button = $( e.target ); if ( $button.hasClass( 'button-disabled' ) ) { return; diff --git a/src/wp-admin/plugin-install.php b/src/wp-admin/plugin-install.php index 03085c7813..653113a902 100644 --- a/src/wp-admin/plugin-install.php +++ b/src/wp-admin/plugin-install.php @@ -128,7 +128,10 @@ if ( $tab !== 'upload' ) { */ do_action( "install_plugins_$tab", $paged ); ?>
-