• File: PushNotificationStatusRestController.php
  • Full Path: /home/slvgwrnz/public_html/wp-content/plugins/woocommerce/src/Internal/PushNotifications/Controllers/PushNotificationStatusRestController.php
  • Date Modified: 11/04/2026 12:40 PM
  • File size: 5.62 KB
  • MIME-type: text/x-php
  • Charset: utf-8
<?php

declare( strict_types = 1 );

namespace Automattic\WooCommerce\Internal\PushNotifications\Controllers;

defined( 'ABSPATH' ) || exit;

use Automattic\WooCommerce\Internal\PushNotifications\Services\DriverAvailabilityService;
use Automattic\WooCommerce\Internal\PushNotifications\Traits\AuthorizesPushNotificationRequests;
use Automattic\WooCommerce\Internal\RestApiControllerBase;
use WP_Error;
use WP_Http;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;

/**
 * Controller for the REST endpoint that reports which push notification drivers
 * are installed, connected, and available, and which one is enabled.
 *
 * Stays reachable even when push notifications are disabled, so clients (e.g.
 * the mobile apps) can discover which drivers are installed and configured, and
 * know to fall back to Jetpack Sync when the remote proxy is not set up on this
 * store.
 *
 * Reports configuration state only. A driver reported as available is installed
 * and connected; whether a given notification is delivered promptly is a separate
 * concern, covered by delivery logging rather than by this endpoint.
 *
 * @since 11.2.0
 */
class PushNotificationStatusRestController extends RestApiControllerBase {
	use AuthorizesPushNotificationRequests;

	/**
	 * The root namespace for the JSON REST API endpoints.
	 *
	 * @var string
	 */
	protected string $route_namespace = 'wc-push-notifications';

	/**
	 * The REST base for the endpoints URL.
	 *
	 * @var string
	 */
	protected string $rest_base = 'status';

	/**
	 * The driver availability service.
	 *
	 * @var DriverAvailabilityService
	 */
	private DriverAvailabilityService $driver_availability_service;

	/**
	 * Initialize injected dependencies.
	 *
	 * @internal
	 *
	 * @param DriverAvailabilityService $driver_availability_service The driver availability service.
	 *
	 * @since 11.2.0
	 */
	final public function init( DriverAvailabilityService $driver_availability_service ): void {
		$this->driver_availability_service = $driver_availability_service;
	}

	/**
	 * Class identifier used by `woocommerce_rest_api_get_rest_namespaces`.
	 *
	 * Intentionally distinct from the URL `$route_namespace` — the filter keys
	 * one class per value here, so sharing the value with sibling controllers
	 * in the same module would overwrite them.
	 *
	 * @since 11.2.0
	 *
	 * @return string
	 */
	protected function get_rest_api_namespace(): string {
		return 'wc-push-notifications-status';
	}

	/**
	 * Register the REST API endpoints handled by this controller.
	 *
	 * @since 11.2.0
	 *
	 * @return void
	 */
	public function register_routes(): void {
		register_rest_route(
			$this->route_namespace,
			$this->rest_base,
			array(
				array(
					'methods'             => WP_REST_Server::READABLE,
					'callback'            => fn ( WP_REST_Request $request ) => $this->run( $request, 'get_status' ),
					'permission_callback' => array( $this, 'authorize_as_from_wpcom_or_allowed_user' ),
				),
				// A sibling of the endpoint array, not a key inside it. WP_REST_Server
				// promotes only non-numeric top-level keys into its route options, and
				// reads the schema exclusively from there, so nesting this one level
				// deeper drops it silently.
				'schema' => array( $this, 'get_schema' ),
			)
		);
	}

	/**
	 * The schema for the status response.
	 *
	 * `installed_drivers` is keyed by driver identifier, so its shape is
	 * described with `additionalProperties` rather than named properties.
	 *
	 * @since 11.2.0
	 *
	 * @return array<string, mixed>
	 */
	public function get_schema(): array {
		$driver_flags = array(
			'type'       => 'object',
			'properties' => array(
				'connected' => array(
					'description' => __( "Whether the driver's underlying connection is present. Null when the check could not be performed.", 'woocommerce' ),
					'type'        => array( 'boolean', 'null' ),
					'context'     => array( 'view' ),
					'readonly'    => true,
				),
				'enabled'   => array(
					'description' => __( 'Whether the driver itself is switched on. Null when the check could not be performed.', 'woocommerce' ),
					'type'        => array( 'boolean', 'null' ),
					'context'     => array( 'view' ),
					'readonly'    => true,
				),
				'available' => array(
					'description' => __( 'Whether the driver is definitively both connected and enabled. An undetermined flag makes the driver unavailable.', 'woocommerce' ),
					'type'        => 'boolean',
					'context'     => array( 'view' ),
					'readonly'    => true,
				),
			),
		);

		return array(
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
			'title'      => 'push_notification_status',
			'type'       => 'object',
			'properties' => array(
				'installed_drivers' => array(
					'description'          => __( 'The installed notification drivers, keyed by driver identifier.', 'woocommerce' ),
					'type'                 => 'object',
					'context'              => array( 'view' ),
					'readonly'             => true,
					'additionalProperties' => $driver_flags,
				),
				'preferred_driver'  => array(
					'description' => __( 'The driver the site prefers, being the first available one in precedence order, or null when none are available. Not a statement about what is delivering notifications to a given app.', 'woocommerce' ),
					'type'        => array( 'string', 'null' ),
					'context'     => array( 'view' ),
					'readonly'    => true,
				),
			),
		);
	}

	/**
	 * Return the installed push notification drivers and the preferred one.
	 *
	 * @since 11.2.0
	 *
	 * @return WP_REST_Response|WP_Error
	 */
	public function get_status() {
		return new WP_REST_Response(
			$this->driver_availability_service->get_status(),
			WP_Http::OK
		);
	}
}