Security: Add user interface to auto-update themes and plugins.

Building on core update mechanisms, this adds the ability to enable automatic updates for themes and plugins to the WordPress admin. 

Fixes: #50052.
Props: afercia, afragen, audrasjb, azaozz, bookdude13, davidperonne, desrosj, gmays, gmays, javiercasares, karmatosed, knutsp, mapk, mukesh27, netweb, nicolaskulka, nielsdeblaauw, paaljoachim, passoniate, pbiron, pedromendonca, whodunitagency, whyisjake, wpamitkumar, and xkon.


git-svn-id: https://develop.svn.wordpress.org/trunk@47835 602fd350-edb4-49c9-b593-d223f7449a82
This commit is contained in:
Jake Spurlock
2020-05-20 18:47:24 +00:00
parent cdd8b92ea6
commit eb79d7f1ee
17 changed files with 1142 additions and 45 deletions
+126 -17
View File
@@ -22,7 +22,21 @@ $plugin = isset( $_REQUEST['plugin'] ) ? wp_unslash( $_REQUEST['plugin'] ) : '';
$s = isset( $_REQUEST['s'] ) ? urlencode( wp_unslash( $_REQUEST['s'] ) ) : '';
// Clean up request URI from temporary args for screen options/paging uri's to work as expected.
$_SERVER['REQUEST_URI'] = remove_query_arg( array( 'error', 'deleted', 'activate', 'activate-multi', 'deactivate', 'deactivate-multi', '_error_nonce' ), $_SERVER['REQUEST_URI'] );
$query_args_to_remove = array(
'error',
'deleted',
'activate',
'activate-multi',
'deactivate',
'deactivate-multi',
'enabled-auto-update',
'disabled-auto-update',
'enabled-auto-update-multi',
'disabled-auto-update-multi',
'_error_nonce',
);
$_SERVER['REQUEST_URI'] = remove_query_arg( $query_args_to_remove, $_SERVER['REQUEST_URI'] );
wp_enqueue_script( 'updates' );
@@ -284,11 +298,14 @@ if ( $action ) {
if ( ! isset( $_REQUEST['verify-delete'] ) ) {
wp_enqueue_script( 'jquery' );
require_once ABSPATH . 'wp-admin/admin-header.php';
?>
<div class="wrap">
<div class="wrap">
<?php
$plugin_info = array();
$have_non_network_plugins = false;
$plugin_info = array();
$have_non_network_plugins = false;
foreach ( (array) $plugins as $plugin ) {
$plugin_slug = dirname( $plugin );
@@ -315,9 +332,11 @@ if ( $action ) {
}
}
}
$plugins_to_delete = count( $plugin_info );
$plugins_to_delete = count( $plugin_info );
?>
<?php if ( 1 == $plugins_to_delete ) : ?>
<?php if ( 1 === $plugins_to_delete ) : ?>
<h1><?php _e( 'Delete Plugin' ); ?></h1>
<?php if ( $have_non_network_plugins && is_network_admin() ) : ?>
<div class="error"><p><strong><?php _e( 'Caution:' ); ?></strong> <?php _e( 'This plugin may be active on other sites in the network.' ); ?></p></div>
@@ -332,7 +351,9 @@ if ( $action ) {
<?php endif; ?>
<ul class="ul-disc">
<?php
$data_to_delete = false;
foreach ( $plugin_info as $plugin ) {
if ( $plugin['is_uninstallable'] ) {
/* translators: 1: Plugin name, 2: Plugin author. */
@@ -343,36 +364,44 @@ if ( $action ) {
echo '<li>', sprintf( _x( '%1$s by %2$s', 'plugin' ), '<strong>' . $plugin['Name'] . '</strong>', '<em>' . $plugin['AuthorName'] ) . '</em>', '</li>';
}
}
?>
</ul>
<p>
<?php
if ( $data_to_delete ) {
_e( 'Are you sure you want to delete these files and data?' );
} else {
_e( 'Are you sure you want to delete these files?' );
}
?>
</p>
<form method="post" action="<?php echo esc_url( $_SERVER['REQUEST_URI'] ); ?>" style="display:inline;">
<input type="hidden" name="verify-delete" value="1" />
<input type="hidden" name="action" value="delete-selected" />
<?php
foreach ( (array) $plugins as $plugin ) {
echo '<input type="hidden" name="checked[]" value="' . esc_attr( $plugin ) . '" />';
}
?>
<?php wp_nonce_field( 'bulk-plugins' ); ?>
<?php submit_button( $data_to_delete ? __( 'Yes, delete these files and data' ) : __( 'Yes, delete these files' ), '', 'submit', false ); ?>
</form>
<?php
$referer = wp_get_referer();
?>
<form method="post" action="<?php echo $referer ? esc_url( $referer ) : ''; ?>" style="display:inline;">
<?php submit_button( __( 'No, return me to the plugin list' ), '', 'submit', false ); ?>
</form>
</div>
</div>
<?php
require_once ABSPATH . 'wp-admin/admin-footer.php';
exit;
} else {
@@ -385,15 +414,14 @@ if ( $action ) {
set_transient( 'plugins_delete_result_' . $user_ID, $delete_result );
wp_redirect( self_admin_url( "plugins.php?deleted=$plugins_to_delete&plugin_status=$status&paged=$page&s=$s" ) );
exit;
case 'clear-recent-list':
if ( ! is_network_admin() ) {
update_option( 'recently_activated', array() );
} else {
update_site_option( 'recently_activated', array() );
}
break;
break;
case 'resume':
if ( is_multisite() ) {
return;
@@ -413,7 +441,78 @@ if ( $action ) {
wp_redirect( self_admin_url( "plugins.php?resume=true&plugin_status=$status&paged=$page&s=$s" ) );
exit;
case 'enable-auto-update':
case 'disable-auto-update':
case 'enable-auto-update-selected':
case 'disable-auto-update-selected':
if ( ! current_user_can( 'update_plugins' ) || ! wp_is_auto_update_enabled_for_type( 'plugin' ) ) {
wp_die( __( 'Sorry, you are not allowed to manage plugins automatic updates.' ) );
}
if ( is_multisite() && ! is_network_admin() ) {
wp_die( __( 'Please connect to your network admin to manage plugins automatic updates.' ) );
}
$redirect = self_admin_url( "plugins.php?plugin_status={$status}&paged={$page}&s={$s}" );
if ( 'enable-auto-update' === $action || 'disable-auto-update' === $action ) {
if ( empty( $plugin ) ) {
wp_redirect( $redirect );
exit;
}
check_admin_referer( 'updates' );
} else {
if ( empty( $_POST['checked'] ) ) {
wp_redirect( $redirect );
exit;
}
check_admin_referer( 'bulk-plugins' );
}
$auto_updates = (array) get_site_option( 'auto_update_plugins', array() );
if ( 'enable-auto-update' === $action ) {
$auto_updates[] = $plugin;
$auto_updates = array_unique( $auto_updates );
$redirect = add_query_arg( array( 'enabled-auto-update' => 'true' ), $redirect );
} elseif ( 'disable-auto-update' === $action ) {
$auto_updates = array_diff( $auto_updates, array( $plugin ) );
$redirect = add_query_arg( array( 'disabled-auto-update' => 'true' ), $redirect );
} else {
$plugins = (array) wp_unslash( $_POST['checked'] );
if ( 'enable-auto-update-selected' === $action ) {
$new_auto_updates = array_merge( $auto_updates, $plugins );
$new_auto_updates = array_unique( $new_auto_updates );
$query_args = array( 'enabled-auto-update-multi' => 'true' );
} else {
$new_auto_updates = array_diff( $auto_updates, $plugins );
$query_args = array( 'disabled-auto-update-multi' => 'true' );
}
// Return early if all selected plugins already have auto-updates enabled or disabled.
// Must use non-strict comparison, so that array order is not treated as significant.
if ( $new_auto_updates == $auto_updates ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
wp_redirect( $redirect );
exit;
}
$auto_updates = $new_auto_updates;
$redirect = add_query_arg( $query_args, $redirect );
}
/** This filter is documented in wp-admin/includes/class-wp-plugins-list-table.php */
$all_items = apply_filters( 'all_plugins', get_plugins() );
// Remove plugins that don't exist or have been deleted since the option was last updated.
$auto_updates = array_intersect( $auto_updates, array_keys( $all_items ) );
update_site_option( 'auto_update_plugins', $auto_updates );
wp_redirect( $redirect );
exit;
default:
if ( isset( $_POST['checked'] ) ) {
check_admin_referer( 'bulk-plugins' );
@@ -498,9 +597,7 @@ if ( ! empty( $invalid ) ) {
echo '</p></div>';
}
}
?>
<?php
if ( isset( $_GET['error'] ) ) :
if ( isset( $_GET['main'] ) ) {
@@ -521,9 +618,11 @@ if ( isset( $_GET['error'] ) ) :
} else {
$errmsg = __( 'Plugin could not be activated because it triggered a <strong>fatal error</strong>.' );
}
?>
<div id="message" class="error"><p><?php echo $errmsg; ?></p>
<?php
if ( ! isset( $_GET['main'] ) && ! isset( $_GET['charsout'] ) && wp_verify_nonce( $_GET['_error_nonce'], 'plugin-activation-error_' . $plugin ) ) {
$iframe_url = add_query_arg(
array(
@@ -533,17 +632,19 @@ if ( isset( $_GET['error'] ) ) :
),
admin_url( 'plugins.php' )
);
?>
<iframe style="border:0" width="100%" height="70px" src="<?php echo esc_url( $iframe_url ); ?>"></iframe>
<iframe style="border:0" width="100%" height="70px" src="<?php echo esc_url( $iframe_url ); ?>"></iframe>
<?php
}
?>
</div>
<?php
elseif ( isset( $_GET['deleted'] ) ) :
$delete_result = get_transient( 'plugins_delete_result_' . $user_ID );
// Delete it once we're done.
delete_transient( 'plugins_delete_result_' . $user_ID );
$delete_result = get_transient( 'plugins_delete_result_' . $user_ID );
// Delete it once we're done.
delete_transient( 'plugins_delete_result_' . $user_ID );
if ( is_wp_error( $delete_result ) ) :
?>
@@ -562,7 +663,7 @@ elseif ( isset( $_GET['deleted'] ) ) :
<div id="message" class="updated notice is-dismissible">
<p>
<?php
if ( 1 == (int) $_GET['deleted'] ) {
if ( 1 === (int) $_GET['deleted'] ) {
_e( 'The selected plugin has been deleted.' );
} else {
_e( 'The selected plugins have been deleted.' );
@@ -570,7 +671,7 @@ elseif ( isset( $_GET['deleted'] ) ) :
?>
</p>
</div>
<?php endif; ?>
<?php endif; ?>
<?php elseif ( isset( $_GET['activate'] ) ) : ?>
<div id="message" class="updated notice is-dismissible"><p><?php _e( 'Plugin activated.' ); ?></p></div>
<?php elseif ( isset( $_GET['activate-multi'] ) ) : ?>
@@ -583,6 +684,14 @@ elseif ( isset( $_GET['deleted'] ) ) :
<div id="message" class="updated notice is-dismissible"><p><?php _e( 'All selected plugins are up to date.' ); ?></p></div>
<?php elseif ( isset( $_GET['resume'] ) ) : ?>
<div id="message" class="updated notice is-dismissible"><p><?php _e( 'Plugin resumed.' ); ?></p></div>
<?php elseif ( isset( $_GET['enabled-auto-update'] ) ) : ?>
<div id="message" class="updated notice is-dismissible"><p><?php _e( 'Plugin will be auto-updated.' ); ?></p></div>
<?php elseif ( isset( $_GET['disabled-auto-update'] ) ) : ?>
<div id="message" class="updated notice is-dismissible"><p><?php _e( 'Plugin will no longer be auto-updated.' ); ?></p></div>
<?php elseif ( isset( $_GET['enabled-auto-update-multi'] ) ) : ?>
<div id="message" class="updated notice is-dismissible"><p><?php _e( 'Selected plugins will be auto-updated.' ); ?></p></div>
<?php elseif ( isset( $_GET['disabled-auto-update-multi'] ) ) : ?>
<div id="message" class="updated notice is-dismissible"><p><?php _e( 'Selected plugins will no longer be auto-updated.' ); ?></p></div>
<?php endif; ?>
<div class="wrap">