diff --git a/projects/packages/scheduled-updates/.phan/baseline.php b/projects/packages/scheduled-updates/.phan/baseline.php index 74832f049ae01..7bedf7098cdde 100644 --- a/projects/packages/scheduled-updates/.phan/baseline.php +++ b/projects/packages/scheduled-updates/.phan/baseline.php @@ -17,11 +17,10 @@ // PhanUndeclaredClassMethod : 2 occurrences // PhanNoopNew : 1 occurrence // PhanTypeMismatchArgumentProbablyReal : 1 occurrence - // PhanTypeMismatchDimFetch : 1 occurrence // Currently, file_suppressions and directory_suppressions are the only supported suppressions 'file_suppressions' => [ - 'src/class-scheduled-updates.php' => ['PhanRedundantCondition', 'PhanTypeMismatchArgumentProbablyReal', 'PhanTypeMismatchDimFetch', 'PhanUndeclaredClassMethod'], + 'src/class-scheduled-updates.php' => ['PhanRedundantCondition', 'PhanTypeMismatchArgumentProbablyReal', 'PhanUndeclaredClassMethod'], 'src/pluggable.php' => ['PhanTypeArraySuspiciousNullable'], 'src/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-update-schedules.php' => ['PhanPluginMixedKeyNoKey'], 'tests/php/class-scheduled-updates-logs-test.php' => ['PhanCompatibleAccessMethodOnTraitDefinition', 'PhanUndeclaredProperty'], diff --git a/projects/packages/scheduled-updates/changelog/add-plugin-list-filter b/projects/packages/scheduled-updates/changelog/add-plugin-list-filter new file mode 100644 index 0000000000000..dc1d1c2af92c9 --- /dev/null +++ b/projects/packages/scheduled-updates/changelog/add-plugin-list-filter @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Scheduled Updates: Added a views link to filter plugins that are part of a scheduled update. diff --git a/projects/packages/scheduled-updates/src/class-scheduled-updates-admin.php b/projects/packages/scheduled-updates/src/class-scheduled-updates-admin.php new file mode 100644 index 0000000000000..f879ca5369ea7 --- /dev/null +++ b/projects/packages/scheduled-updates/src/class-scheduled-updates-admin.php @@ -0,0 +1,164 @@ + 0, + * 'wordpress-importer/wordpress-importer.php' => 1, + * 'wp-last-login/wp-last-login.php' => 2, + * ); + */ + $scheduled = array_flip( + array_unique( + array_merge( + ...array_values( + wp_list_pluck( $events, 'args' ) + ) + ) + ) + ); + + // Removing from the auto-update-disabled list since they are scheduled. + $plugins['auto-update-disabled'] = array_diff_key( (array) $plugins['auto-update-disabled'], $scheduled ); + $plugins['scheduled-updates'] = array_intersect_key( (array) $plugins['all'], $scheduled ); + } + + return $plugins; + } + + /** + * Add a view for scheduled updates in the Plugins list table. + * + * @param array $views An array of available views. + * @return array + */ + public static function add_scheduled_updates_view( $views ) { + global $totals; + + if ( ! empty( $totals['scheduled-updates'] ) ) { + $views['scheduled-updates'] = sprintf( + '%3$s (%4$s)', + esc_url( add_query_arg( array( 'plugin_status' => 'scheduled-updates' ), 'plugins.php' ) ), + isset( $_REQUEST['plugin_status'] ) && 'scheduled-updates' === $_REQUEST['plugin_status'] ? 'current' : '', // phpcs:ignore WordPress.Security.NonceVerification + __( 'Scheduled Updates', 'jetpack-scheduled-updates' ), + number_format_i18n( $totals['scheduled-updates'] ) + ); + } + + return $views; + } + + /** + * Filters the HTML of the auto-updates setting for each plugin in the Plugins list table. + * + * @param string $html The HTML of the plugin's auto-update column content, + * including toggle auto-update action links and + * time to next update. + * @param string $plugin_file Path to the plugin file relative to the plugin directory. + */ + public static function show_scheduled_updates( $html, $plugin_file ) { + if ( ! function_exists( 'wp_get_scheduled_events' ) ) { + require_once __DIR__ . '/pluggable.php'; + } + + $events = wp_get_scheduled_events( Scheduled_Updates::PLUGIN_CRON_HOOK ); + + $schedules = array(); + foreach ( $events as $event ) { + if ( in_array( $plugin_file, $event->args, true ) ) { + $schedules[] = $event; + } + } + + // Plugin is not part of an update schedule. + if ( empty( $schedules ) ) { + return $html; + } + + $text = array_map( array( __CLASS__, 'get_scheduled_update_text' ), $schedules ); + + $html = '

' . implode( '
', $text ) . '

'; + $html .= sprintf( + '%2$s', + esc_url( 'https://wordpress.com/plugins/scheduled-updates/' . ( new Status() )->get_site_suffix() ), + esc_html__( 'Edit', 'jetpack-scheduled-updates' ) + ); + + return $html; + } + + /** + * Get the text for a scheduled update. + * + * @param object $schedule The scheduled update. + * @return string + */ + public static function get_scheduled_update_text( $schedule ) { + if ( DAY_IN_SECONDS === $schedule->interval ) { + $html = sprintf( + /* translators: %s is the time of day. Daily at 10 am. */ + esc_html__( 'Daily at %s.', 'jetpack-scheduled-updates' ), + wp_date( get_option( 'time_format' ), $schedule->timestamp ) + ); + } else { + // Not getting smart about passing in weekdays makes it easier to translate. + $weekdays = array( + /* translators: %s is the time of day. Mondays at 10 am. */ + 1 => __( 'Mondays at %s.', 'jetpack-scheduled-updates' ), + /* translators: %s is the time of day. Tuesdays at 10 am. */ + 2 => __( 'Tuesdays at %s.', 'jetpack-scheduled-updates' ), + /* translators: %s is the time of day. Wednesdays at 10 am. */ + 3 => __( 'Wednesdays at %s.', 'jetpack-scheduled-updates' ), + /* translators: %s is the time of day. Thursdays at 10 am. */ + 4 => __( 'Thursdays at %s.', 'jetpack-scheduled-updates' ), + /* translators: %s is the time of day. Fridays at 10 am. */ + 5 => __( 'Fridays at %s.', 'jetpack-scheduled-updates' ), + /* translators: %s is the time of day. Saturdays at 10 am. */ + 6 => __( 'Saturdays at %s.', 'jetpack-scheduled-updates' ), + /* translators: %s is the time of day. Sundays at 10 am. */ + 7 => __( 'Sundays at %s.', 'jetpack-scheduled-updates' ), + ); + + $html = sprintf( + $weekdays[ (int) wp_date( 'N', $schedule->timestamp ) ], + wp_date( get_option( 'time_format' ), $schedule->timestamp ) + ); + } + + return $html; + } +} diff --git a/projects/packages/scheduled-updates/src/class-scheduled-updates.php b/projects/packages/scheduled-updates/src/class-scheduled-updates.php index 4bdfce49e7ef8..cfa2c27a20a00 100644 --- a/projects/packages/scheduled-updates/src/class-scheduled-updates.php +++ b/projects/packages/scheduled-updates/src/class-scheduled-updates.php @@ -51,9 +51,12 @@ public static function init() { add_action( self::PLUGIN_CRON_HOOK, array( __CLASS__, 'run_scheduled_update' ), 10, 10 ); add_action( 'rest_api_init', array( __CLASS__, 'add_is_managed_extension_field' ) ); add_filter( 'auto_update_plugin', array( __CLASS__, 'allowlist_scheduled_plugins' ), 10, 2 ); - add_filter( 'plugin_auto_update_setting_html', array( __CLASS__, 'show_scheduled_updates' ), 10, 2 ); add_action( 'deleted_plugin', array( __CLASS__, 'deleted_plugin' ), 10, 2 ); + add_filter( 'plugins_list', array( Scheduled_Updates_Admin::class, 'add_scheduled_updates_context' ) ); + add_filter( 'views_plugins', array( Scheduled_Updates_Admin::class, 'add_scheduled_updates_view' ) ); + add_filter( 'plugin_auto_update_setting_html', array( Scheduled_Updates_Admin::class, 'show_scheduled_updates' ), 10, 2 ); + // Update cron sync option after options update. $callback = array( __CLASS__, 'update_option_cron' ); @@ -263,86 +266,6 @@ public static function allowlist_scheduled_plugins( $update, $item ) { return $update; } - /** - * Filters the HTML of the auto-updates setting for each plugin in the Plugins list table. - * - * @param string $html The HTML of the plugin's auto-update column content, - * including toggle auto-update action links and - * time to next update. - * @param string $plugin_file Path to the plugin file relative to the plugin directory. - */ - public static function show_scheduled_updates( $html, $plugin_file ) { - if ( ! function_exists( 'wp_get_scheduled_events' ) ) { - require_once __DIR__ . '/pluggable.php'; - } - - $events = wp_get_scheduled_events( self::PLUGIN_CRON_HOOK ); - - $schedules = array(); - foreach ( $events as $event ) { - if ( in_array( $plugin_file, $event->args, true ) ) { - $schedules[] = $event; - } - } - - // Plugin is not part of an update schedule. - if ( empty( $schedules ) ) { - return $html; - } - - $text = array_map( array( __CLASS__, 'get_scheduled_update_text' ), $schedules ); - - $html = '

' . implode( '
', $text ) . '

'; - $html .= sprintf( - '%2$s', - esc_url( 'https://wordpress.com/plugins/scheduled-updates/' . ( new Status() )->get_site_suffix() ), - esc_html__( 'Edit', 'jetpack-scheduled-updates' ) - ); - - return $html; - } - - /** - * Get the text for a scheduled update. - * - * @param object $schedule The scheduled update. - * @return string - */ - public static function get_scheduled_update_text( $schedule ) { - if ( DAY_IN_SECONDS === $schedule->interval ) { - $html = sprintf( - /* translators: %s is the time of day. Daily at 10 am. */ - esc_html__( 'Daily at %s.', 'jetpack-scheduled-updates' ), - wp_date( get_option( 'time_format' ), $schedule->timestamp ) - ); - } else { - // Not getting smart about passing in weekdays makes it easier to translate. - $weekdays = array( - /* translators: %s is the time of day. Mondays at 10 am. */ - 1 => __( 'Mondays at %s.', 'jetpack-scheduled-updates' ), - /* translators: %s is the time of day. Tuesdays at 10 am. */ - 2 => __( 'Tuesdays at %s.', 'jetpack-scheduled-updates' ), - /* translators: %s is the time of day. Wednesdays at 10 am. */ - 3 => __( 'Wednesdays at %s.', 'jetpack-scheduled-updates' ), - /* translators: %s is the time of day. Thursdays at 10 am. */ - 4 => __( 'Thursdays at %s.', 'jetpack-scheduled-updates' ), - /* translators: %s is the time of day. Fridays at 10 am. */ - 5 => __( 'Fridays at %s.', 'jetpack-scheduled-updates' ), - /* translators: %s is the time of day. Saturdays at 10 am. */ - 6 => __( 'Saturdays at %s.', 'jetpack-scheduled-updates' ), - /* translators: %s is the time of day. Sundays at 10 am. */ - 7 => __( 'Sundays at %s.', 'jetpack-scheduled-updates' ), - ); - - $html = sprintf( - $weekdays[ wp_date( 'N', $schedule->timestamp ) ], - wp_date( get_option( 'time_format' ), $schedule->timestamp ) - ); - } - - return $html; - } - /** * Registers the is_managed field for the plugin REST API. */ diff --git a/projects/packages/scheduled-updates/tests/php/class-scheduled-updates-admin-test.php b/projects/packages/scheduled-updates/tests/php/class-scheduled-updates-admin-test.php new file mode 100644 index 0000000000000..86c2051995217 --- /dev/null +++ b/projects/packages/scheduled-updates/tests/php/class-scheduled-updates-admin-test.php @@ -0,0 +1,59 @@ +assertSame( $expected, Scheduled_Updates_Admin::get_scheduled_update_text( $schedule ) ); + } + + /** + * Data provider for test_get_scheduled_update_text. + * + * @return array[] + */ + public function update_text_provider() { + return array( + array( + (object) array( + 'timestamp' => strtotime( 'next Monday 00:00' ), + 'interval' => WEEK_IN_SECONDS, + ), + sprintf( 'Mondays at %s.', gmdate( get_option( 'time_format' ), strtotime( 'next Monday 8:00' ) ) ), + ), + array( + (object) array( + 'timestamp' => strtotime( 'next Tuesday 00:00' ), + 'interval' => DAY_IN_SECONDS, + ), + sprintf( 'Daily at %s.', gmdate( get_option( 'time_format' ), strtotime( 'next Tuesday 8:00' ) ) ), + ), + array( + (object) array( + 'timestamp' => strtotime( 'next Sunday 00:00' ), + 'interval' => WEEK_IN_SECONDS, + ), + sprintf( 'Sundays at %s.', gmdate( get_option( 'time_format' ), strtotime( 'next Sunday 8:00' ) ) ), + ), + ); + } +} diff --git a/projects/packages/scheduled-updates/tests/php/class-scheduled-updates-test.php b/projects/packages/scheduled-updates/tests/php/class-scheduled-updates-test.php index 70f6a16e78801..6ebdead2d833a 100644 --- a/projects/packages/scheduled-updates/tests/php/class-scheduled-updates-test.php +++ b/projects/packages/scheduled-updates/tests/php/class-scheduled-updates-test.php @@ -556,19 +556,6 @@ public function test_delete_plugin_new_events_inherit_statuses() { } } - /** - * Test get_scheduled_update_text. - * - * @dataProvider update_text_provider - * @covers ::get_scheduled_update_text - * - * @param object $schedule The schedule object. - * @param string $expected The expected text. - */ - public function test_get_scheduled_update_text( $schedule, $expected ) { - $this->assertSame( $expected, Scheduled_Updates::get_scheduled_update_text( $schedule ) ); - } - /** * Test clear CRON cache. * @@ -645,37 +632,6 @@ public function test_clear_cron_cache() { $this->assertArrayHasKey( $id_2, $data ); } - /** - * Data provider for test_get_scheduled_update_text. - * - * @return array[] - */ - public function update_text_provider() { - return array( - array( - (object) array( - 'timestamp' => strtotime( 'next Monday 00:00' ), - 'interval' => WEEK_IN_SECONDS, - ), - sprintf( 'Mondays at %s.', gmdate( get_option( 'time_format' ), strtotime( 'next Monday 8:00' ) ) ), - ), - array( - (object) array( - 'timestamp' => strtotime( 'next Tuesday 00:00' ), - 'interval' => DAY_IN_SECONDS, - ), - sprintf( 'Daily at %s.', gmdate( get_option( 'time_format' ), strtotime( 'next Tuesday 8:00' ) ) ), - ), - array( - (object) array( - 'timestamp' => strtotime( 'next Sunday 00:00' ), - 'interval' => WEEK_IN_SECONDS, - ), - sprintf( 'Sundays at %s.', gmdate( get_option( 'time_format' ), strtotime( 'next Sunday 8:00' ) ) ), - ), - ); - } - /** * Create a list of plugins to be deleted. * diff --git a/projects/packages/scheduled-updates/tests/php/class-wpcom-rest-api-v2-endpoint-update-schedules-test.php b/projects/packages/scheduled-updates/tests/php/class-wpcom-rest-api-v2-endpoint-update-schedules-test.php index a179dde6b8fd3..3076511b03f19 100644 --- a/projects/packages/scheduled-updates/tests/php/class-wpcom-rest-api-v2-endpoint-update-schedules-test.php +++ b/projects/packages/scheduled-updates/tests/php/class-wpcom-rest-api-v2-endpoint-update-schedules-test.php @@ -59,6 +59,7 @@ public function set_up() { ); wp_set_current_user( 0 ); + Scheduled_Updates::init(); do_action( 'rest_api_init' ); } @@ -73,6 +74,7 @@ public function tear_down() { wp_clear_scheduled_hook( Scheduled_Updates::PLUGIN_CRON_HOOK ); delete_option( 'jetpack_scheduled_update_statuses' ); + delete_option( Scheduled_Updates::PLUGIN_CRON_HOOK ); } /** @@ -865,11 +867,11 @@ public function test_admin_user_capabilities() { } /** - * Test adding a log entry. + * Test adding a log entry for a non-existent schedule. * * @covers ::add_log */ - public function test_add_log() { + public function test_add_log_invalid_schedule() { wp_set_current_user( $this->admin_id ); $request = new WP_REST_Request( 'PUT', '/wpcom/v2/update-schedules/' . Scheduled_Updates::generate_schedule_id( array() ) . '/logs' ); @@ -882,7 +884,7 @@ public function test_add_log() { $result = rest_do_request( $request ); $this->assertSame( 404, $result->get_status() ); - $this->assertSame( false, get_option( Scheduled_Updates::PLUGIN_CRON_HOOK ) ); + $this->assertEmpty( get_option( Scheduled_Updates::PLUGIN_CRON_HOOK ) ); } /** diff --git a/projects/plugins/mu-wpcom-plugin/changelog/add-plugin-list-filter b/projects/plugins/mu-wpcom-plugin/changelog/add-plugin-list-filter new file mode 100644 index 0000000000000..9aa70e3ec1f75 --- /dev/null +++ b/projects/plugins/mu-wpcom-plugin/changelog/add-plugin-list-filter @@ -0,0 +1,5 @@ +Significance: patch +Type: changed +Comment: Updated composer.lock. + +