* @see https://docs.parse.ly/api-available-metrics/ */ public const SORT_METRICS = array( 'views', 'mobile_views', 'tablet_views', 'desktop_views', 'visitors', 'visitors_new', 'visitors_returning', 'engaged_minutes', 'avg_engaged', 'avg_engaged_new', 'avg_engaged_returning', 'social_interactions', 'fb_interactions', 'tw_interactions', 'pi_interactions', 'social_referrals', 'fb_referrals', 'tw_referrals', 'pi_referrals', 'search_refs', ); /** * The Parse.ly Content API service. * * @since 3.17.0 * * @var Content_API_Service */ public $content_api; /** * Constructor. * * @since 3.17.0 * * @param Stats_Controller $controller The stats controller. */ public function __construct( Stats_Controller $controller ) { parent::__construct( $controller ); $this->content_api = $this->parsely->get_content_api(); } /** * Returns the endpoint name. * * @since 3.17.0 * * @return string */ public static function get_endpoint_name(): string { return 'posts'; } /** * Registers the routes for the objects of the controller. * * @since 3.17.0 */ public function register_routes(): void { /** * GET /posts * Retrieves posts for the given criteria. */ $this->register_rest_route( '/', array( 'GET' ), array( $this, 'get_posts' ), array_merge( array( 'period_start' => array( 'description' => 'The start of the period to query.', 'type' => 'string', 'required' => false, ), 'period_end' => array( 'description' => 'The end of the period to query.', 'type' => 'string', 'required' => false, ), 'pub_date_start' => array( 'description' => 'The start of the publication date range to query.', 'type' => 'string', 'required' => false, ), 'pub_date_end' => array( 'description' => 'The end of the publication date range to query.', 'type' => 'string', 'required' => false, ), 'limit' => array( 'description' => 'The number of posts to return.', 'type' => 'integer', 'required' => false, 'default' => self::TOP_POSTS_DEFAULT_LIMIT, ), 'sort' => array( 'description' => 'The sort order of the posts.', 'type' => 'string', 'enum' => self::SORT_METRICS, 'default' => self::SORT_DEFAULT, 'required' => false, ), 'page' => array( 'description' => 'The page to fetch.', 'type' => 'integer', 'required' => false, 'default' => 1, ), 'author' => array( 'description' => 'Comma-separated list of authors to filter by.', 'type' => 'string', 'required' => false, 'validate_callback' => array( $this, 'validate_max_length_is_5' ), 'sanitize_callback' => array( $this, 'sanitize_string_to_array' ), ), 'section' => array( 'description' => 'Comma-separated list of sections to filter by.', 'type' => 'string', 'required' => false, 'validate_callback' => array( $this, 'validate_max_length_is_5' ), 'sanitize_callback' => array( $this, 'sanitize_string_to_array' ), ), 'tag' => array( 'description' => 'Comma-separated list of tags to filter by.', 'type' => 'string', 'required' => false, 'validate_callback' => array( $this, 'validate_max_length_is_5' ), 'sanitize_callback' => array( $this, 'sanitize_string_to_array' ), ), 'segment' => array( 'description' => 'The segment to filter by.', 'type' => 'string', 'required' => false, ), 'urls' => array( 'description' => 'The URLs to fetch data for.', 'type' => 'array', 'sanitize_callback' => array( $this, 'sanitize_urls' ), 'required' => false, ), ), $this->get_itm_source_param_args() ) ); } /** * Sanitizes a string to an array, splitting it by commas. * * @since 3.17.0 * * @param string|array $str The string to sanitize. * @return array The sanitized array. */ public function sanitize_string_to_array( $str ): array { if ( is_array( $str ) ) { return $str; } return explode( ',', $str ); } /** * Sanitizes all the items of an array as URLs. * * @since 3.18.0 * * @param array $urls The array to sanitize. * @return array The sanitized array. */ public function sanitize_urls( array $urls ): array { return array_map( 'sanitize_url', $urls ); } /** * Validates that the parameter has at most 5 items. * * @since 3.17.0 * * @param string|array $string_or_array The string or array to validate. * @return true|WP_Error */ public function validate_max_length_is_5( $string_or_array ) { if ( is_string( $string_or_array ) ) { $string_or_array = $this->sanitize_string_to_array( $string_or_array ); } if ( count( $string_or_array ) > 5 ) { return new WP_Error( 'invalid_param', __( 'The parameter must have at most 5 items.', 'wp-parsely' ) ); } return true; } /** * API Endpoint: GET /stats/posts * * Retrieves the posts with the given query parameters. * * @since 3.17.0 * * @param WP_REST_Request $request The request. * @return array|WP_Error|WP_REST_Response */ public function get_posts( WP_REST_Request $request ) { $params = $request->get_params(); // Setup the itm_source if it is provided. $this->set_itm_source_from_request( $request ); /** * The raw analytics data, received by the API. * * @var array|WP_Error $analytics_request */ $analytics_request = $this->content_api->get_posts( array( 'period_start' => $params['period_start'] ?? null, 'period_end' => $params['period_end'] ?? null, 'pub_date_start' => $params['pub_date_start'] ?? null, 'pub_date_end' => $params['pub_date_end'] ?? null, 'limit' => $params['limit'] ?? self::TOP_POSTS_DEFAULT_LIMIT, 'sort' => $params['sort'] ?? self::SORT_DEFAULT, 'page' => $params['page'] ?? 1, 'author' => $params['author'] ?? null, 'section' => $params['section'] ?? null, 'tag' => $params['tag'] ?? null, 'segment' => $params['segment'] ?? null, 'itm_source' => $params['itm_source'] ?? null, 'urls' => $params['urls'] ?? null, ) ); if ( is_wp_error( $analytics_request ) ) { return $analytics_request; } // Process the data. $posts = array(); /** * The analytics data object. * * @var array> $analytics_request */ foreach ( $analytics_request as $item ) { $posts[] = $this->extract_post_data( $item ); } $response_data = array( 'params' => $params, 'data' => $posts, ); return new WP_REST_Response( $response_data, 200 ); } }