diff --git a/css/wp-autoupdates.css b/css/wp-autoupdates.css index 1edc286..bc7a94a 100644 --- a/css/wp-autoupdates.css +++ b/css/wp-autoupdates.css @@ -1,7 +1,3 @@ -.theme-overlay .theme-autoupdate a { - text-decoration: none; -} - #autoupdates_column { min-width: 200px; } @@ -17,4 +13,18 @@ .plugin-autoupdate-enabled, .theme-autoupdate-enabled { color: #006505; +} + +.plugins .plugin-title .plugin-autoupdate-enabled .dashicons { + float: none; + padding: 0; + width: auto; + height: auto; +} +.plugins .plugin-title .plugin-autoupdate-enabled .dashicons:before { + padding: 0; + background: transparent; + box-shadow: none; + font-size: 20px; + color: #006505; } \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100755 index 0000000..abca8f5 --- /dev/null +++ b/readme.md @@ -0,0 +1,68 @@ +# WordPress Autoupdates πŸ€– + +A feature plugin to integrate Plugins & Themes automatic updates in WordPress Core. + +## About πŸ”Ž + +The purpose of this repository is to prepare a future Plugins & Themes automatic updates feature. + +To test/contribute, just install this plugin and activate it on your WordPress installation. + +## Context ⏳ + +In 2018, Matt Mullenweg posted 9 projects for Core to focus on in 2019. We didn’t ship as many as hoped, but we made a lot of progress. Plugins and Themes Automatic Updates were one of those 9 projects. This project is now milestoned to WordPress 5.5 and this feature plugin is here to help move towards this achievement. + +- [See also: Update on the 9 projects for 2019](https://make.wordpress.org/core/2019/12/06/update-9-projects-for-2019/): +- [Related Trac ticket for plugins autoupdates](https://core.trac.wordpress.org/ticket/48850) +- [Related Trac ticket for themes autoupdates](https://core.trac.wordpress.org/ticket/48850) + +## Features / to-do list πŸ›  + +- βœ… Open a Trac ticket to handle Core merge for plugins +- βœ… Open a Trac ticket to handle Core merge for themes +- βœ… Handle plugin autoupdates +- πŸ”² Handle themes autoupdates +- βœ… Handle plugin autoupdates in a multisite context +- πŸ”² Handle themes autoupdates in a multisite context +- πŸ”² Validate design for plugins screen +- πŸ”² Validate design for themes screen +- πŸ”² Validate design for update-core screen +- πŸ”² Email notifications for plugins +- πŸ”² Email notifications for themes +- πŸ”² Documentation +- πŸ”² Submit Feature Plugin on WordPress.org repository +- πŸ”² Publish the feature plugin proposal +- πŸ”² Get the plugin featured as beta plugin on WordPress.org +- πŸ”² Open a dedicated Slack channel on Make WordPress +- πŸ”² Copy review +- πŸ”² Accessibility audit +- πŸ”² Security audit +- πŸ”² Coding standards audit +- πŸ”² Inline Docs audit + +## Contributors πŸ‘₯ + +- audrasjb +- whodunitagency + +## Documentation πŸ“š + +TODO. + +## Screenshots πŸ–Ό + +### Plugins Admin screen - screenshot + +![Plugins Admin screen - screenshot](https://jeanbaptisteaudras.com/images/wp-autoupdates-togglesingleplugin-01.gif) + +### Plugins Admin screen - Toggle update single plugin - screenshot + +![Plugins Admin screen - Toggle update single plugin - animated screenshot](https://jeanbaptisteaudras.com/images/wp-autoupdates-plugins-01.png) + +### Plugins Admin screen - Buk Edit - animated screenshot + +![Plugins Admin screen - Buk Edit - animated screenshot](https://jeanbaptisteaudras.com/images/wp-autoupdates-bulkeditplugins-01.gif) + +### Update Core Admin Screen + +![Update Core Admin screen - screenshot](wp-autoupdates-updatecore-01.png) \ No newline at end of file diff --git a/wp-autoupdates-themes-draft.php b/wp-autoupdates-themes-draft.php new file mode 100755 index 0000000..a26723c --- /dev/null +++ b/wp-autoupdates-themes-draft.php @@ -0,0 +1,147 @@ +'; + $autoupdate_text .= sprintf( + /* translators: Time until the next update. */ + __( 'Automatic update scheduled in %s', 'wp-autoupdates' ), + $time_to_next_update + ); + $autoupdate_text .= ' '; + $script .= 'jQuery(".check-column input[value=\'' . $theme . '\']").closest("tr").find(".plugin-title > p").append(\'' . $autoupdate_text . '\');'; + } + } + $script .= '});'; + wp_add_inline_script( 'jquery', $script ); + } + if ( 'themes.php' === $hook ) { + if ( 'autoupdate' === esc_url( $_GET['action'] ) ) { + check_admin_referer( 'autoupdate-theme_' . $_GET['stylesheet'] ); + $theme = wp_get_theme( $_GET['stylesheet'] ); + $slug = $theme->get_stylesheet(); + if ( ! current_user_can( 'update_themes' ) ) { + wp_die( + '

' . __( 'You need a higher level of permission.' ) . '

' . + '

' . __( 'Sorry, you are not allowed to enable automatic update for themes.' ) . '

', + 403 + ); + } + $autoupdated_themes = get_option( 'wp_autoupdated_themes', array() ); + if ( ! in_array( $slug, $autoupdated_themes, true ) ) { + $autoupdate_action = 'enable'; + $autoupdated_themes[] = $slug; + } else { + $autoupdate_action = 'disable'; + $autoupdated_themes = array_diff( $autoupdated_themes, array( $slug ) ); + } + update_option( 'wp_autoupdated_themes', $autoupdated_themes ); + wp_redirect( admin_url( 'themes.php?autoupdated=' . $autoupdate_action ) ); + exit; + } + if ( wp_is_themes_auto_update_enabled() ) { + $script = 'jQuery( document ).ready(function() {'; + $wp_auto_update_themes = get_site_option( 'wp_auto_update_themes', array() ); + foreach ( $wp_auto_update_themes as $theme ) { + $aria_label_enabled = esc_attr__( 'Disable automatic updates', 'plugin', 'wp-autoupdates' ); + $autoupdate_enabled_text = '

'; + $autoupdate_enabled_text .= ' ' . __( 'Automatic updates enabled', 'wp-autoupdates' ); + $autoupdate_enabled_text = '
'; + if ( current_user_can( 'update_themes', $theme ) ) { + $autoupdate_enabled_text .= sprintf( + '%s', + wp_nonce_url( 'plugins.php?action=autoupdate&plugin=' . urlencode( $theme ) . '&paged=' . $page, 'autoupdate-plugin_' . $theme ), + $aria_label, + __( 'Disable', 'wp-autoupdates' ) + ); + } + $autoupdate_enabled_text .= '

'; + $script .= 'jQuery(".check-column input[value=\'' . $theme . '\']").closest("tr").find(".plugin-title > p").append(\'' . $autoupdate_enabled_text . '\');'; + } + $script .= '});'; + wp_add_inline_script( 'jquery', $script ); + } + } +} +add_action( 'admin_enqueue_scripts', 'wp_auto_updates_styles_scripts' ); + + +/** + * Checks whether themes manual autoupdate is enabled. + */ +function wp_is_themes_auto_update_enabled() { + $enabled = ! defined( 'WP_DISABLE_THEMES_AUTO_UPDATE' ) || ! WP_DISABLE_THEMES_AUTO_UPDATE; + + /** + * Filters whether themes manual autoupdate is enabled. + * + * @param bool $enabled True if themes auto udpate is enabled, false otherwise. + */ + return apply_filters( 'wp_themes_auto_update_enabled', $enabled ); +} + +/** + * Autoupdate selected themes. + */ +function wp_auto_update_theme( $update, $item ) { + $wp_auto_update_themes = get_site_option( 'wp_auto_update_themes', array() ); + if ( in_array( $item->theme, $wp_auto_update_themes, true ) && wp_is_themes_auto_update_enabled() ) { + return true; + } else { + return $update; + } +} +add_filter( 'auto_update_theme', 'wp_auto_update_theme', 10, 2 ); + +/** + * Filter the themes prepared for JavaScript, for themes.php. + */ +function wp_filter_themes_for_js( $prepared_themes ) { + $autoupdated_themes = get_option( 'wp_autoupdated_themes', array() ); + foreach( $autoupdated_themes as $theme ) { + $slug = $theme['id']; + $encoded_slug = urlencode( $slug ); + if ( in_array( $slug, $autoupdated_themes, true ) ) { + $theme['autoupdated'] = true; + $theme['actions']['autoupdate'] = current_user_can( 'update_themes' ) ? wp_nonce_url( admin_url( 'themes.php?action=autoupdate&stylesheet=' . $encoded_slug ), 'autoupdate-theme_' . $slug ) : null; + } + } + return $prepared_themes; +} +add_filter( 'wp_prepare_themes_for_js', 'wp_filter_themes_for_js' ); + +/** + * Auto-update notices + */ +function wp_autoupdates_themes_notices() { + // Themes screen + if ( isset( $_GET['autoupdated'] ) ) { + $autoupdated_themes = get_option( 'wp_autoupdated_themes', array() ); + $autoupdate_notice = __( 'The selected theme won’t update automatically anymore.' ); + if ( 'enable' === $_GET['autoupdated'] ) { + echo '

'; + _e( 'The selected theme will now update automatically.', 'wp-autoupdates' ); + echo '

'; + } else { + echo '

'; + _e( 'The selected theme won’t automatically update anymore.', 'wp-autoupdates' ); + echo '

'; + } + } +} +add_action( 'admin_notices', 'wp_autoupdates_themes_notices' ); \ No newline at end of file diff --git a/wp-autoupdates.php b/wp-autoupdates.php index 71ec040..58d3e1a 100755 --- a/wp-autoupdates.php +++ b/wp-autoupdates.php @@ -19,20 +19,22 @@ /** * Enqueue styles and scripts */ -function wp_auto_updates_styles_scripts( $hook ) { - if ( ! in_array( $hook, array( 'plugins.php', 'themes.php', 'update-core.php' ) ) ) { +function wp_autoupdates_enqueues( $hook ) { + if ( ! in_array( $hook, array( 'plugins.php', 'update-core.php' ) ) ) { return; } wp_register_style( 'wp-autoupdates', plugin_dir_url( __FILE__ ) . 'css/wp-autoupdates.css', array() ); wp_enqueue_style( 'wp-autoupdates' ); + + // Update core screen JS hack (due to lack of filters) if ( 'update-core.php' === $hook ) { $script = 'jQuery( document ).ready(function() {'; - if ( wp_is_plugins_auto_update_enabled() ) { + if ( wp_autoupdates_is_plugins_auto_update_enabled() ) { $wp_auto_update_plugins = get_site_option( 'wp_auto_update_plugins', array() ); foreach ( $wp_auto_update_plugins as $plugin ) { $next_update_time = wp_next_scheduled( 'wp_version_check' ); $time_to_next_update = human_time_diff( intval( $next_update_time ) ); - $autoupdate_text = ' | '; + $autoupdate_text = ' '; $autoupdate_text .= sprintf( /* translators: Time until the next update. */ __( 'Automatic update scheduled in %s', 'wp-autoupdates' ), @@ -42,32 +44,17 @@ function wp_auto_updates_styles_scripts( $hook ) { $script .= 'jQuery(".check-column input[value=\'' . $plugin . '\']").closest("tr").find(".plugin-title > p").append(\'' . $autoupdate_text . '\');'; } } - if ( wp_is_themes_auto_update_enabled() ) { - $wp_auto_update_themes = get_site_option( 'wp_auto_update_themes', array() ); - foreach ( $wp_auto_update_themes as $theme ) { - $next_update_time = wp_next_scheduled( 'wp_version_check' ); - $time_to_next_update = human_time_diff( intval( $next_update_time ) ); - $autoupdate_text = ' | '; - $autoupdate_text .= sprintf( - /* translators: Time until the next update. */ - __( 'Automatic update scheduled in %s', 'wp-autoupdates' ), - $time_to_next_update - ); - $autoupdate_text .= ' '; - $script .= 'jQuery(".check-column input[value=\'' . $theme . '\']").closest("tr").find(".plugin-title > p").append(\'' . $autoupdate_text . '\');'; - } - } $script .= '});'; wp_add_inline_script( 'jquery', $script ); } } -add_action( 'admin_enqueue_scripts', 'wp_auto_updates_styles_scripts' ); +add_action( 'admin_enqueue_scripts', 'wp_autoupdates_enqueues' ); /** * Checks whether plugins manual autoupdate is enabled. */ -function wp_is_plugins_auto_update_enabled() { +function wp_autoupdates_is_plugins_auto_update_enabled() { $enabled = ! defined( 'WP_DISABLE_PLUGINS_AUTO_UPDATE' ) || ! WP_DISABLE_PLUGINS_AUTO_UPDATE; /** @@ -78,66 +65,39 @@ function wp_is_plugins_auto_update_enabled() { return apply_filters( 'wp_plugins_auto_update_enabled', $enabled ); } -/** - * Checks whether themes manual autoupdate is enabled. - */ -function wp_is_themes_auto_update_enabled() { - $enabled = ! defined( 'WP_DISABLE_THEMES_AUTO_UPDATE' ) || ! WP_DISABLE_THEMES_AUTO_UPDATE; - - /** - * Filters whether themes manual autoupdate is enabled. - * - * @param bool $enabled True if themes auto udpate is enabled, false otherwise. - */ - return apply_filters( 'wp_themes_auto_update_enabled', $enabled ); -} - /** * Autoupdate selected plugins. */ -function wp_auto_update_plugin( $update, $item ) { +function wp_autoupdates_selected_plugins( $update, $item ) { $wp_auto_update_plugins = get_site_option( 'wp_auto_update_plugins', array() ); - if ( in_array( $item->plugin, $wp_auto_update_plugins, true ) && wp_is_plugins_auto_update_enabled() ) { + if ( in_array( $item->plugin, $wp_auto_update_plugins, true ) && wp_autoupdates_is_plugins_auto_update_enabled() ) { return true; } else { return $update; } } -add_filter( 'auto_update_plugin', 'wp_auto_update_plugin', 10, 2 ); - -/** - * Autoupdate selected themes. - */ -function wp_auto_update_theme( $update, $item ) { - $wp_auto_update_themes = get_site_option( 'wp_auto_update_themes', array() ); - if ( in_array( $item->theme, $wp_auto_update_themes, true ) && wp_is_themes_auto_update_enabled() ) { - return true; - } else { - return $update; - } -} -add_filter( 'auto_update_theme', 'wp_auto_update_theme', 10, 2 ); +add_filter( 'auto_update_plugin', 'wp_autoupdates_selected_plugins', 10, 2 ); /** * Add autoupdate column to plugins screen. */ -function wp_add_plugins_autoupdates_column( $columns ) { +function wp_autoupdates_add_plugins_autoupdates_column( $columns ) { $columns['autoupdates_column'] = 'Automatic updates'; return $columns; } -add_filter( 'manage_plugins_columns', 'wp_add_plugins_autoupdates_column' ); +add_filter( 'manage_plugins_columns', 'wp_autoupdates_add_plugins_autoupdates_column' ); /** * Render autoupdate column’s content. */ -function wp_add_plugins_autoupdates_column_content( $column_name, $plugin_file, $plugin_data ) { - if ( ! current_user_can( 'update_plugins' ) || ! wp_is_plugins_auto_update_enabled() ) { +function wp_autoupdates_add_plugins_autoupdates_column_content( $column_name, $plugin_file, $plugin_data ) { + if ( ! current_user_can( 'update_plugins' ) || ! wp_autoupdates_is_plugins_auto_update_enabled() ) { return; } if ( is_multisite() && ! is_network_admin() ) { return; } - if ( wp_is_plugins_auto_update_enabled() ) { + if ( wp_autoupdates_is_plugins_auto_update_enabled() ) { $wp_auto_update_plugins = get_site_option( 'wp_auto_update_plugins', array() ); if ( in_array( $plugin_file, $wp_auto_update_plugins, true ) ) { $aria_label = esc_attr( @@ -180,17 +140,17 @@ function wp_add_plugins_autoupdates_column_content( $column_name, $plugin_file, } } } -add_action( 'manage_plugins_custom_column' , 'wp_add_plugins_autoupdates_column_content', 10, 3 ); +add_action( 'manage_plugins_custom_column' , 'wp_autoupdates_add_plugins_autoupdates_column_content', 10, 3 ); /** * Add plugins autoupdates bulk actions */ -function wp_plugins_bulk_actions( $actions ) { +function wp_autoupdates_plugins_bulk_actions( $actions ) { $actions['enable-autoupdate-selected'] = __( 'Enable auto-updates', 'wp-autoupdates' ); $actions['disable-autoupdate-selected'] = __( 'Disable auto-updates', 'wp-autoupdates' ); return $actions; } -add_action( 'bulk_actions-plugins', 'wp_plugins_bulk_actions' ); +add_action( 'bulk_actions-plugins', 'wp_autoupdates_plugins_bulk_actions' ); /** @@ -202,7 +162,7 @@ function wp_autoupdates_enabler() { return; } if ( 'autoupdate' === esc_html( $_GET['action'] ) ) { - if ( ! current_user_can( 'update_plugins' ) || ! wp_is_plugins_auto_update_enabled() ) { + if ( ! current_user_can( 'update_plugins' ) || ! wp_autoupdates_is_plugins_auto_update_enabled() ) { wp_die( __( 'Sorry, you are not allowed to enable plugins automatic updates.', 'wp-autoupdates' ) ); } @@ -218,7 +178,6 @@ function wp_autoupdates_enabler() { } check_admin_referer( 'autoupdate-plugin_' . $plugin ); - $wp_auto_update_plugins = get_site_option( 'wp_auto_update_plugins', array() ); if ( in_array( $plugin, $wp_auto_update_plugins, true ) ) { @@ -228,12 +187,9 @@ function wp_autoupdates_enabler() { $wp_auto_update_plugins[] = $plugin; $action_type = 'enable-autoupdate=true'; } - update_site_option( 'wp_auto_update_plugins', $wp_auto_update_plugins ); - wp_redirect( self_admin_url( "plugins.php?$action_type&plugin_status=$status&paged=$page&s=$s" ) ); exit; - } } add_action( 'admin_init', 'wp_autoupdates_enabler' ); @@ -241,9 +197,9 @@ function wp_autoupdates_enabler() { /** * Handle plugins autoupdates bulk actions */ -function wp_plugins_bulk_actions_handle( $redirect_to, $doaction, $items ) { +function wp_autoupdates_plugins_bulk_actions_handle( $redirect_to, $doaction, $items ) { if ( 'enable-autoupdate-selected' === $doaction ) { - if ( ! current_user_can( 'update_plugins' ) || ! wp_is_plugins_auto_update_enabled() ) { + if ( ! current_user_can( 'update_plugins' ) || ! wp_autoupdates_is_plugins_auto_update_enabled() ) { wp_die( __( 'Sorry, you are not allowed to enable plugins automatic updates.', 'wp-autoupdates' ) ); } @@ -272,7 +228,7 @@ function wp_plugins_bulk_actions_handle( $redirect_to, $doaction, $items ) { } if ( 'disable-autoupdate-selected' === $doaction ) { - if ( ! current_user_can( 'update_plugins' ) || ! wp_is_plugins_auto_update_enabled() ) { + if ( ! current_user_can( 'update_plugins' ) || ! wp_autoupdates_is_plugins_auto_update_enabled() ) { wp_die( __( 'Sorry, you are not allowed to enable plugins automatic updates.', 'wp-autoupdates' ) ); } @@ -301,22 +257,22 @@ function wp_plugins_bulk_actions_handle( $redirect_to, $doaction, $items ) { } } -add_action( 'handle_bulk_actions-plugins', 'wp_plugins_bulk_actions_handle', 10, 3 ); +add_action( 'handle_bulk_actions-plugins', 'wp_autoupdates_plugins_bulk_actions_handle', 10, 3 ); /** * Auto-update notices */ -function wp_autoupdate_notices() { - if ( isset( esc_html( $_GET['enable-autoupdate'] ) ) ) { +function wp_autoupdates_notices() { + // Plugins screen + if ( isset( $_GET['enable-autoupdate'] ) ) { echo '

'; _e( 'The selected plugins will now update automatically.', 'wp-autoupdates' ); echo '

'; } - - if ( isset( esc_html( $_GET['disable-autoupdate'] ) ) ) { + if ( isset( $_GET['disable-autoupdate'] ) ) { echo '

'; _e( 'The selected plugins won’t automatically update anymore.', 'wp-autoupdates' ); echo '

'; } } -add_action( 'admin_notices', 'wp_autoupdate_notices' ); \ No newline at end of file +add_action( 'admin_notices', 'wp_autoupdates_notices' ); \ No newline at end of file