context = $context; $this->options = $options ?: new Options( $context ); $this->google_proxy = new Google_Proxy( $this->context ); $this->authentication = $authentication ?: new Authentication( $this->context ); $this->credentials = $this->authentication->credentials(); } /** * Registers core notifications. * * @since 1.4.0 */ public function register() { add_filter( 'googlesitekit_rest_routes', function ( $routes ) { return array_merge( $routes, $this->get_rest_routes() ); } ); } /** * Gets related REST routes. * * @since 1.4.0 * * @return array List of REST_Route objects. */ private function get_rest_routes() { $can_use_notifications = function () { return current_user_can( Permissions::SETUP ) && $this->credentials->has(); }; return array( new REST_Route( 'core/site/data/notifications', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => function () { $endpoint = add_query_arg( array( 'site_id' => $this->credentials->get()['oauth2_client_id'], ), $this->google_proxy->url( '/notifications/' ) ); // Return an empty array of notifications if the user isn't using the proxy. if ( ! $this->credentials->using_proxy() ) { return new WP_REST_Response( array() ); } $response = wp_remote_get( $endpoint ); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.wp_remote_get_wp_remote_get if ( is_wp_error( $response ) ) { return $response; } try { $response = $this->parse_response( $response ); } catch ( Exception $e ) { return new WP_Error( 'exception', $e->getMessage() ); } $data = array_map( function ( Notification $notification ) { return $notification->prepare_for_js(); }, $this->map_response_to_notifications( $response ) ); return new WP_REST_Response( $data ); }, 'permission_callback' => $can_use_notifications, ), ) ), new REST_Route( 'core/site/data/mark-notification', array( array( 'methods' => WP_REST_Server::EDITABLE, 'callback' => function ( WP_REST_Request $request ) { $data = $request['data']; if ( empty( $data['notificationID'] ) ) { return $this->missing_required_param( 'data.notificationID' ); } if ( empty( $data['notificationState'] ) ) { return $this->missing_required_param( 'data.notificationState' ); } $credentials = $this->credentials->get(); $response = wp_remote_post( $this->google_proxy->url( '/notifications/mark/' ), array( 'body' => array( 'site_id' => $credentials['oauth2_client_id'], 'site_secret' => $credentials['oauth2_client_secret'], 'notification_id' => $data['notificationID'], 'notification_state' => $data['notificationState'], ), ) ); if ( is_wp_error( $response ) ) { return $response; } try { $response = $this->parse_response( $response ); } catch ( Exception $e ) { return new WP_Error( 'exception', $e->getMessage() ); } return new WP_REST_Response( array( 'success' => isset( $response['success'] ) ? (bool) $response['success'] : false, ) ); }, 'args' => array( 'data' => array( 'required' => true, 'type' => 'object', ), ), 'permission_callback' => $can_use_notifications, ), ) ), ); } /** * Validates and parses the given JSON response into an array. * * @since 1.4.0 * * @param array $response HTTP response array. * @return mixed JSON decoded response. * @throws Exception Throws exception if response cannot be parsed or if an error is returned. */ private function parse_response( $response ) { $body = wp_remote_retrieve_body( $response ); $decoded = json_decode( $body, true ); if ( json_last_error() ) { throw new Exception( 'Error while decoding response: ' . json_last_error() ); } if ( ! empty( $decoded['error'] ) ) { throw new Exception( $decoded['error'] ); } return $decoded; } /** * Maps the response objects into Notification objects. * * @since 1.4.0 * * @param array $response Array of notification objects from API. * @return Notification[] Array of Notification objects. */ private function map_response_to_notifications( array $response ) { return array_map( function ( $notification ) { return new Notification( $notification['id'], array( 'title' => $notification['title'], 'content' => $notification['content'], 'cta_url' => $notification['ctaURL'], 'cta_label' => $notification['ctaLabel'], 'cta_target' => $notification['ctaTarget'], 'learn_more_url' => $notification['learnMoreURL'], 'learn_more_label' => $notification['learnMoreLabel'], 'dismissible' => $notification['dismissible'], 'dismiss_label' => $notification['dismissLabel'], ) ); }, $response ); } /** * Gets a WP_Error instance for the given missing required parameter. * * @since 1.4.0 * * @param string $param Missing required parameter. * @return WP_Error */ private function missing_required_param( $param ) { return new WP_Error( 'missing_required_param', /* translators: %s: Missing parameter name */ sprintf( __( 'Request parameter is empty: %s.', 'google-site-kit' ), $param ), array( 'status' => 400 ) ); } }