*/ protected $registered_routes = array(); /** * Constructor. * * @since 3.17.0 * * @param Base_API_Controller $controller The REST API controller. */ public function __construct( Base_API_Controller $controller ) { $this->api_controller = $controller; $this->parsely = $controller->get_parsely(); } /** * Initializes the API endpoint, by registering the routes. * * Allows for the endpoint to be disabled via the * `wp_parsely_api_{endpoint}_endpoint_enabled` filter. * * @since 3.17.0 */ public function init(): void { /** * Filter to enable/disable the endpoint. * * @return bool */ $filter_name = 'wp_parsely_api_' . Utils::convert_endpoint_to_filter_key( static::get_endpoint_name() ) . '_endpoint_enabled'; if ( ! apply_filters( $filter_name, true ) ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound return; } // Register the routes. add_action( 'rest_api_init', array( $this, 'register_routes' ) ); } /** * Returns the endpoint's name. * * This method should be overridden by child classes and used to return the * endpoint name. * * @since 3.17.0 * * @return string */ abstract public static function get_endpoint_name(): string; /** * Returns the default access capability for the endpoint. * * This method can be overridden by child classes to return a different * default access capability. * * @since 3.17.0 * * @return string */ protected function get_default_access_capability(): string { return 'publish_posts'; } /** * Registers the routes for the endpoint. * * This method should be overridden by child classes and used to register * the routes for the endpoint. * * @since 3.17.0 */ abstract public function register_routes(): void; /** * Registers a REST route. * * @since 3.17.0 * * @param string $route The route to register. * @param string[] $methods Array with the allowed methods. * @param callable $callback Callback function to call when the endpoint is hit. * @param array $args The endpoint arguments definition. */ public function register_rest_route( string $route, array $methods, callable $callback, array $args = array() ): void { // Trim any possible slashes from the route. $route = trim( $route, '/' ); // Store the route for later reference. $this->registered_routes[] = $route; // Create the full route for the endpoint. $route = static::get_endpoint_name() . '/' . $route; // Register the route. register_rest_route( $this->api_controller->get_full_namespace(), $this->api_controller->prefix_route( $route ), array( array( 'methods' => $methods, 'callback' => $callback, 'permission_callback' => array( $this, 'is_available_to_current_user' ), 'args' => $args, 'show_in_index' => ! is_wp_error( $this->is_available_to_current_user() ), ), ) ); } /** * Returns the full endpoint path for a given route. * * @since 3.17.0 * * @param string $route The route. * @return string */ public function get_full_endpoint( string $route = '' ): string { $route = trim( $route, '/' ); if ( '' !== $route ) { $route = static::get_endpoint_name() . '/' . $route; } else { $route = static::get_endpoint_name(); } return '/' . $this->api_controller->get_full_namespace() . '/' . $this->api_controller->prefix_route( $route ); } /** * Returns the endpoint slug. * * The slug is the endpoint's name prefixed with the route prefix, from * the API controller. * * Used as an identifier for the endpoint, when registering routes. * * @since 3.17.0 * * @return string */ public function get_endpoint_slug(): string { return $this->api_controller->prefix_route( '' ) . static::get_endpoint_name(); } /** * Returns the registered routes. * * @since 3.17.0 * * @return array */ public function get_registered_routes(): array { return $this->registered_routes; } /** * Returns whether the endpoint is available for access by the current * user. * * @since 3.14.0 Replaced `is_public_endpoint`, `user_capability` and `permission_callback()`. * @since 3.16.0 Added the `$request` parameter. * @since 3.17.0 Moved to the new API structure. * * @param WP_REST_Request|null $request The request object. * @return WP_Error|bool True if the endpoint is available. */ public function is_available_to_current_user( ?WP_REST_Request $request = null ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found // Validate the API key and secret. $api_key_validation = $this->validate_site_id_and_secret(); if ( is_wp_error( $api_key_validation ) ) { return $api_key_validation; } // Validate the user capability. $capability = $this->get_default_access_capability(); return current_user_can( // phpcs:ignore WordPress.WP.Capabilities.Undetermined $this->apply_capability_filters( $capability ) ); } /** * Returns the user capability allowing access to the endpoint, after having * applied capability filters. * * The default access capability is not passed here by default, to allow for * a more explicit declaration in child classes. * * @since 3.14.0 * @since 3.17.0 Moved to the new API structure. * * @param string $capability The original capability allowing access. * @return string The capability allowing access after applying the filters. */ public function apply_capability_filters( string $capability ): string { /** * Filter to change the default user capability for all private endpoints. * * @var string */ $default_user_capability = apply_filters( 'wp_parsely_user_capability_for_all_private_apis', $capability ); /** * Filter to change the user capability for the specific endpoint. * * @var string */ $endpoint_specific_user_capability = apply_filters( 'wp_parsely_user_capability_for_' . Utils::convert_endpoint_to_filter_key( static::get_endpoint_name() ) . '_api', $default_user_capability ); return $endpoint_specific_user_capability; } /** * Validates that the Site ID and secret are set. * If the API secret is not required, it will not be validated. * * @since 3.13.0 * @since 3.17.0 Moved to the new API structure and renamed from `validate_apikey_and_secret`. * * @param bool $require_api_secret Specifies if the API Secret is required. * @return WP_Error|bool */ public function validate_site_id_and_secret( bool $require_api_secret = true ) { if ( false === $this->parsely->site_id_is_set() ) { return new WP_Error( 'parsely_site_id_not_set', __( 'A Parse.ly Site ID must be set in site options to use this endpoint', 'wp-parsely' ), array( 'status' => 403 ) ); } if ( $require_api_secret && false === $this->parsely->api_secret_is_set() ) { return new WP_Error( 'parsely_api_secret_not_set', __( 'A Parse.ly API Secret must be set in site options to use this endpoint', 'wp-parsely' ), array( 'status' => 403 ) ); } return true; } }