Skip to content

Commit

Permalink
Scheduled Updates: Introduce Views Link (#36880)
Browse files Browse the repository at this point in the history
* Scheduled Updates: Introduce Views Link

* Remove unneeded translator comment.

* Version bumps

* Fix phan

* Update phan baseline

* Remove plugins with scheduled updates from the list of plugins without autoupdates.

Props @ouikhuan.

* Fix test dependency that's uncovered by the new admin test file.

See p1713280022484079/1713277354.019009-slack-C01A60HCGUA
  • Loading branch information
obenland authored Apr 16, 2024
1 parent 6438a55 commit 83ac1e4
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 130 deletions.
3 changes: 1 addition & 2 deletions projects/packages/scheduled-updates/.phan/baseline.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: minor
Type: added

Scheduled Updates: Added a views link to filter plugins that are part of a scheduled update.
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
<?php
/**
* Scheduled Updates Admin.
*
* @package automattic/scheduled-updates
*/

namespace Automattic\Jetpack;

/**
* Class Scheduled_Updates_Admin.
*
* Contains all the wp-admin-related functionality for scheduled updates.
*/
class Scheduled_Updates_Admin {

/**
* Add context for scheduled updates in the Plugins list table.
*
* @param array $plugins An array of plugins.
* @return array
*/
public static function add_scheduled_updates_context( $plugins ) {
if ( ! function_exists( 'wp_get_scheduled_events' ) ) {
require_once __DIR__ . '/pluggable.php';
}

$events = wp_get_scheduled_events( Scheduled_Updates::PLUGIN_CRON_HOOK );

if ( ! empty( $events ) ) {
if ( ! empty( $_REQUEST['plugin_status'] ) && 'scheduled-updates' === $_REQUEST['plugin_status'] ) { // phpcs:ignore WordPress.Security.NonceVerification
$GLOBALS['status'] = 'scheduled-updates'; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
}

/*
* Get the unique list of plugins that are part of scheduled updates.
*
* Example:
* $scheduled = array(
* 'rest-api-console/rest-api-console.php' => 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(
'<a href="%1$s" class="%2$s">%3$s <span class="count">(%4$s)</span></a>',
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 = '<p style="margin: 0 0 8px">' . implode( '<br>', $text ) . '</p>';
$html .= sprintf(
'<a href="%1$s">%2$s</a>',
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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' );

Expand Down Expand Up @@ -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 = '<p style="margin: 0 0 8px">' . implode( '<br>', $text ) . '</p>';
$html .= sprintf(
'<a href="%1$s">%2$s</a>',
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.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php
/**
* Test class for Scheduled_Updates.
*
* @package automattic/scheduled-updates
*/

namespace Automattic\Jetpack;

/**
* Test class for Scheduled_Updates_Admin.
*
* @coversDefaultClass Scheduled_Updates_Admin
*/
class Scheduled_Updates_Admin_Test extends \WorDBless\BaseTestCase {
/**
* 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_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' ) ) ),
),
);
}
}
Loading

0 comments on commit 83ac1e4

Please sign in to comment.