# jetpack/16.3/jetpack_vendor/automattic/jetpack-backup/src/rest/class-activity-log-bridge.php

Jetpack – WP Security, Backup, Speed, &amp; Growth, version 16.3. 146 lines.

- Page: https://pluginprobe.com/plugins/jetpack/16.3/code/jetpack_vendor/automattic/jetpack-backup/src/rest/class-activity-log-bridge.php
- Raw: https://pluginprobe.com/plugins/jetpack/16.3/raw/jetpack_vendor/automattic/jetpack-backup/src/rest/class-activity-log-bridge.php
- Modified: 2026-09-01T16:55:44+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/jetpack/16.3/code/jetpack_vendor/automattic/jetpack-backup/src/rest/class-activity-log-bridge.php#L10-L20`.

```php
<?php
/**
 * Activity log REST bridge — proxies /sites/{id}/activity/rewindable.
 *
 * @package automattic/jetpack-backup-plugin
 */

namespace Automattic\Jetpack\Backup\V0005\REST;

use Automattic\Jetpack\Connection\Client;
use WP_Error;
use WP_REST_Request;
use WP_REST_Server;

if ( ! defined( 'ABSPATH' ) ) {
	exit( 0 );
}

/**
 * Powers the modernized Overview's activity list. Forwards the raw
 * WPCOM rewindable-activity response; the React layer's
 * `normalize/activity-log.ts` converts entries to `ActivityItem` shape.
 */
class Activity_Log_Bridge {

	/**
	 * Register routes.
	 *
	 * @return void
	 */
	public static function register_routes() {
		// NOTE: not `/activity-log` — that namespace is owned by the
		// `automattic/jetpack-activity-log` package's REST controller,
		// which is loaded inside the Jetpack plugin. Sharing the route
		// would let `register_rest_route`'s endpoint-merge pick the
		// wrong handler at dispatch time (the two bridges proxy
		// different WPCOM endpoints with different response shapes).
		register_rest_route(
			'jetpack/v4',
			'/site/rewindable-activity',
			array(
				'methods'             => WP_REST_Server::READABLE,
				'callback'            => array( __CLASS__, 'get_activity_log' ),
				'permission_callback' => array( Rest_Controller::class, 'permission_check' ),
				// Bounds mirror what WPCOM's own `get_stream_args()`
				// validate_callback enforces on `/activity/rewindable`, and
				// the sibling `jetpack-activity-log` package's declaration.
				// Declaring them here turns an out-of-range value into a 400
				// with a useful message instead of a round-trip that WPCOM
				// rejects.
				'args'                => array(
					'number'     => array(
						'description' => __( 'Number of items to return per page.', 'jetpack-backup-pkg' ),
						'type'        => 'integer',
						'minimum'     => 1,
						'maximum'     => 1000,
					),
					'page'       => array(
						'description' => __( '1-indexed page number.', 'jetpack-backup-pkg' ),
						'type'        => 'integer',
						'minimum'     => 1,
					),
					// Direction only: WPCOM's `get_stream_args()` sorts on `ts_utc`
					// and takes no field, so there is no `orderby` to declare.
					'sort_order' => array(
						'description' => __( 'Sort direction.', 'jetpack-backup-pkg' ),
						'type'        => 'string',
						'enum'        => array( 'asc', 'desc' ),
					),
				),
			)
		);
	}

	/**
	 * Proxy the WPCOM rewindable activity log.
	 *
	 * Forwards `number` (per page) and `page` (1-indexed) so the dashboard
	 * paginates against the real server response — `totalItems` /
	 * `totalPages` come back in the WPCOM envelope and drive DataViews'
	 * pagination footer. Same param shape the `automattic/jetpack-activity-log`
	 * package uses against the parent `/sites/{id}/activity` endpoint.
	 *
	 * `sort_order` is forwarded too, so the list's Order control reorders
	 * the real result set rather than the ten rows already on screen.
	 * Omitted when absent: WPCOM defaults it to `desc`.
	 *
	 * @param WP_REST_Request $request The REST request.
	 * @return \WP_REST_Response|WP_Error
	 */
	public static function get_activity_log( WP_REST_Request $request ) {
		$blog_id = Rest_Controller::get_blog_id_or_error();
		if ( is_wp_error( $blog_id ) ) {
			return $blog_id;
		}

		$query = array_filter(
			array(
				'number'     => $request->get_param( 'number' ),
				'page'       => $request->get_param( 'page' ),
				'sort_order' => $request->get_param( 'sort_order' ),
				// Activity summaries are rendered by WPCOM. Without this they
				// come back in English whatever the reader's language is.
				'_locale'    => get_user_locale(),
			),
			static function ( $value ) {
				return null !== $value && '' !== $value;
			}
		);

		$endpoint = sprintf( '/sites/%d/activity/rewindable', $blog_id );
		if ( ! empty( $query ) ) {
			$endpoint .= '?' . http_build_query( $query );
		}

		$response = Client::wpcom_json_api_request_as_user(
			$endpoint,
			'v2',
			array(),
			null,
			'wpcom'
		);

		if ( is_wp_error( $response ) ) {
			return Rest_Controller::transport_error( $response, 'activity_log_fetch_failed' );
		}

		// Cast because `wp_remote_retrieve_response_code()` hands back
		// whatever the transport put there, and a numeric string fails the
		// strict comparison below — routing a perfectly good response into
		// the failure branch, where `upstream_error()`'s clamp then reports
		// it as a 500. Same reasoning at every bridge; the long version is
		// on `Rest_Controller::upstream_error()`.
		$status_code = (int) wp_remote_retrieve_response_code( $response );
		if ( 200 !== $status_code ) {
			return Rest_Controller::upstream_error(
				$response,
				'activity_log_fetch_failed',
				__( 'Could not fetch the site activity log.', 'jetpack-backup-pkg' )
			);
		}

		return rest_ensure_response( json_decode( wp_remote_retrieve_body( $response ), true ) );
	}
}

```
