Request FTP and SSH credentials when needed during shiny updates

This is a restoration of [31749] which was reverted in [31755].

It includes a number of enhancements from the original version.  Namely:
 * Not doing a credential check in src/wp-includes/script-loader.php
 * Add new function `wp_print_request_filesystem_credentials_modal`
 * update the version number in the list table when a plugin is updated

UI still needs further work, but this basic version should enable more testing

Props ericlewis, jorbin
See #31528



git-svn-id: https://develop.svn.wordpress.org/trunk@31811 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Aaron Jorbin 2015-03-18 03:17:59 +00:00
parent 533aefdf13
commit 08ac58e160
6 changed files with 254 additions and 24 deletions

View File

@ -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
-------------------------------------------------------------- */

View File

@ -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 );
}
/**

View File

@ -1192,3 +1192,29 @@ submit_button( __( 'Proceed' ), 'button', 'upgrade' );
<?php
return false;
}
/**
* Print the credentials modal when needed
*
* @since 4.2.0
*/
function wp_print_request_filesystem_credentials_modal() {
$filesystem_method = get_filesystem_method();
ob_start();
$filesystem_credentials_are_stored = request_filesystem_credentials( self_admin_url() );
ob_end_clean();
$request_filesystem_credentials = ( $filesystem_method != 'direct' && ! $filesystem_credentials_are_stored );
if ( ! $request_filesystem_credentials ) {
return;
}
?>
<div id="request-filesystem-credentials-dialog" class="notification-dialog-wrap request-filesystem-credentials-dialog">
<div class="notification-dialog-background"></div>
<div class="notification-dialog">
<div class="request-filesystem-credentials-dialog-content">
<?php request_filesystem_credentials( site_url() ); ?>
<div>
</div>
</div>
<?php
}

View File

@ -21,6 +21,35 @@ window.wp = window.wp || {};
*/
wp.updates.l10n = window._wpUpdatesSettings.l10n;
/**
* Whether filesystem credentials need to be requested from the user.
*
* @since 4.2.0
*
* @var bool
*/
wp.updates.shouldRequestFilesystemCredentials = null;
/**
* Filesystem credentials to be packaged along with the request.
*
* @since 4.2.0
*
* @var object
*/
wp.updates.filesystemCredentials = {
ftp: {
host: null,
username: null,
password: null,
connectionType: null
},
ssh: {
publicKey: null,
privateKey: null
}
};
/**
* Flag if we're waiting for an install/update to complete.
*
@ -30,6 +59,15 @@ window.wp = window.wp || {};
*/
wp.updates.updateLock = false;
/**
* * Flag if we've done an install or update successfully.
*
* @since 4.2.0
*
* @var bool
*/
wp.updates.updateDoneSuccessfully = false;
/**
* If the user tries to install/update a plugin while an install/update is
* already happening, it can be placed in this queue to perform later.
@ -123,15 +161,20 @@ window.wp = window.wp || {};
wp.updates.updateLock = true;
var data = {
'_ajax_nonce': wp.updates.ajaxNonce,
'plugin': plugin,
'slug': slug
_ajax_nonce: wp.updates.ajaxNonce,
plugin: plugin,
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( 'update-plugin', data )
.done( wp.updates.updateSuccess )
.fail( wp.updates.updateError )
.always( wp.updates.updateAlways );
.fail( wp.updates.updateError );
};
/**
@ -147,6 +190,9 @@ window.wp = window.wp || {};
$message = $( '#' + response.slug ).next().find( '.update-message' );
$( '#' + response.slug ).addClass( 'updated' ).removeClass( 'update' );
$( '#' + response.slug + '-update' ).addClass( 'updated' ).removeClass( 'update' );
// Update the version number in the row.
var newText = $( '#' + response.slug ).find('.plugin-version-author-uri').html().replace( response.oldVersion, response.newVersion );
$( '#' + response.slug ).find('.plugin-version-author-uri').html( newText );
} else if ( 'plugin-install' === pagenow ) {
$message = $( '.plugin-card-' + response.slug ).find( '.update-now' );
$message.addClass( 'button-disabled' );
@ -157,6 +203,15 @@ window.wp = window.wp || {};
wp.a11y.speak( wp.updates.l10n.updatedMsg );
wp.updates.decrementCount( 'plugin' );
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();
};
/**
@ -168,6 +223,11 @@ window.wp = window.wp || {};
*/
wp.updates.updateError = function( response ) {
var $message;
wp.updates.updateDoneSuccessfully = false;
if ( response.errorCode && response.errorCode == 'unable_to_connect_to_filesystem' ) {
wp.updates.credentialError( response, 'update-plugin' );
return;
}
if ( 'plugins' === pagenow || 'plugins-network' === pagenow ) {
$message = $( '#' + response.slug ).next().find( '.update-message' );
} else if ( 'plugin-install' === pagenow ) {
@ -176,18 +236,23 @@ window.wp = window.wp || {};
$message.removeClass( 'updating-message' );
$message.text( wp.updates.l10n.updateFailed );
wp.a11y.speak( wp.updates.l10n.updateFailed );
};
/**
* After an update attempt has completed, check the queue.
* Show an error message in the request for credentials form.
*
* @param {string} message
* @since 4.2.0
*/
wp.updates.updateAlways = function() {
wp.updates.updateLock = false;
wp.updates.queueChecker();
};
wp.updates.showErrorInCredentialsForm = function( message ) {
var $notificationDialog = $( '.notification-dialog' );
// Remove any existing error
$notificationDialog.find( '.error' ).remove();
$notificationDialog.find( 'h3' ).after( '<div class="error">' + message + '</div>' );
};
/**
* 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;

View File

@ -128,7 +128,10 @@ if ( $tab !== 'upload' ) {
*/
do_action( "install_plugins_$tab", $paged ); ?>
</div>
<?php
<?php
wp_print_request_filesystem_credentials_modal();
/**
* WordPress Administration Template Footer.
*/

View File

@ -476,4 +476,6 @@ do_action( 'pre_current_active_plugins', $plugins['all'] );
</div>
<?php
wp_print_request_filesystem_credentials_modal();
include(ABSPATH . 'wp-admin/admin-footer.php');