PluginProbe
Parse.ly / 3.23.6
Parse.ly v3.23.6
3.24.1 3.24.0 3.23.7 3.23.6 3.23.5 3.23.4 3.23.3 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.17.0 3.18.0 3.18.1 3.19.0 3.19.1 3.19.2 3.19.3 3.2.0 3.2.1 3.20.0 3.20.1 3.20.2 3.20.3 All 105 releases
wp-parsely / src / rest-api / class-base-endpoint.php

class-base-endpoint.php in Parse.ly 3.23.6, at src/rest-api/class-base-endpoint.php

308 lines 7.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Base API Endpoint
4 *
5 * @package Parsely
6 * @since 3.17.0
7 */
8
9 declare(strict_types=1);
10
11 namespace Parsely\REST_API;
12
13 use Parsely\Parsely;
14 use Parsely\Utils\Utils;
15 use WP_Error;
16 use WP_REST_Request;
17
18 /**
19 * Base class for API endpoints.
20 *
21 * Most endpoint classes should derive from this class. Child classes should
22 * implement the `get_endpoint_name` and `register_routes` methods.
23 *
24 * @since 3.17.0
25 */
26 abstract class Base_Endpoint {
27 /**
28 * The Parsely instance.
29 *
30 * @since 3.17.0
31 *
32 * @var Parsely
33 */
34 protected $parsely;
35
36 /**
37 * The REST API instance.
38 *
39 * @since 3.17.0
40 *
41 * @var Base_API_Controller $api_controller
42 */
43 protected $api_controller;
44
45 /**
46 * The registered routes.
47 *
48 * @since 3.17.0
49 *
50 * @var array<string>
51 */
52 protected $registered_routes = array();
53
54 /**
55 * Constructor.
56 *
57 * @since 3.17.0
58 *
59 * @param Base_API_Controller $controller The REST API controller.
60 */
61 public function __construct( Base_API_Controller $controller ) {
62 $this->api_controller = $controller;
63 $this->parsely = $controller->get_parsely();
64 }
65
66 /**
67 * Initializes the API endpoint, by registering the routes.
68 *
69 * Allows for the endpoint to be disabled via the
70 * `wp_parsely_api_{endpoint}_endpoint_enabled` filter.
71 *
72 * @since 3.17.0
73 */
74 public function init(): void {
75 /**
76 * Filter to enable/disable the endpoint.
77 *
78 * @return bool
79 */
80 $filter_name = 'wp_parsely_api_' .
81 Utils::convert_endpoint_to_filter_key( static::get_endpoint_name() ) .
82 '_endpoint_enabled';
83 if ( ! apply_filters( $filter_name, true ) ) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.DynamicHooknameFound
84 return;
85 }
86
87 // Register the routes.
88 add_action( 'rest_api_init', array( $this, 'register_routes' ) );
89 }
90
91 /**
92 * Returns the endpoint's name.
93 *
94 * This method should be overridden by child classes and used to return the
95 * endpoint name.
96 *
97 * @since 3.17.0
98 *
99 * @return string
100 */
101 abstract public static function get_endpoint_name(): string;
102
103 /**
104 * Returns the default access capability for the endpoint.
105 *
106 * This method can be overridden by child classes to return a different
107 * default access capability.
108 *
109 * @since 3.17.0
110 *
111 * @return string
112 */
113 protected function get_default_access_capability(): string {
114 return 'publish_posts';
115 }
116
117 /**
118 * Registers the routes for the endpoint.
119 *
120 * This method should be overridden by child classes and used to register
121 * the routes for the endpoint.
122 *
123 * @since 3.17.0
124 */
125 abstract public function register_routes(): void;
126
127 /**
128 * Registers a REST route.
129 *
130 * @since 3.17.0
131 *
132 * @param string $route The route to register.
133 * @param string[] $methods Array with the allowed methods.
134 * @param callable $callback Callback function to call when the endpoint is hit.
135 * @param array<mixed> $args The endpoint arguments definition.
136 */
137 public function register_rest_route( string $route, array $methods, callable $callback, array $args = array() ): void {
138 // Trim any possible slashes from the route.
139 $route = trim( $route, '/' );
140
141 // Store the route for later reference.
142 $this->registered_routes[] = $route;
143
144 // Create the full route for the endpoint.
145 $route = static::get_endpoint_name() . '/' . $route;
146
147 // Register the route.
148 register_rest_route(
149 $this->api_controller->get_full_namespace(),
150 $this->api_controller->prefix_route( $route ),
151 array(
152 array(
153 'methods' => $methods,
154 'callback' => $callback,
155 'permission_callback' => array( $this, 'is_available_to_current_user' ),
156 'args' => $args,
157 'show_in_index' => ! is_wp_error( $this->is_available_to_current_user() ),
158 ),
159 )
160 );
161 }
162
163 /**
164 * Returns the full endpoint path for a given route.
165 *
166 * @since 3.17.0
167 *
168 * @param string $route The route.
169 * @return string
170 */
171 public function get_full_endpoint( string $route = '' ): string {
172 $route = trim( $route, '/' );
173
174 if ( '' !== $route ) {
175 $route = static::get_endpoint_name() . '/' . $route;
176 } else {
177 $route = static::get_endpoint_name();
178 }
179
180 return '/' .
181 $this->api_controller->get_full_namespace() .
182 '/' .
183 $this->api_controller->prefix_route( $route );
184 }
185
186 /**
187 * Returns the endpoint slug.
188 *
189 * The slug is the endpoint's name prefixed with the route prefix, from
190 * the API controller.
191 *
192 * Used as an identifier for the endpoint, when registering routes.
193 *
194 * @since 3.17.0
195 *
196 * @return string
197 */
198 public function get_endpoint_slug(): string {
199 return $this->api_controller->prefix_route( '' ) . static::get_endpoint_name();
200 }
201
202 /**
203 * Returns the registered routes.
204 *
205 * @since 3.17.0
206 *
207 * @return array<string>
208 */
209 public function get_registered_routes(): array {
210 return $this->registered_routes;
211 }
212
213 /**
214 * Returns whether the endpoint is available for access by the current
215 * user.
216 *
217 * @since 3.14.0 Replaced `is_public_endpoint`, `user_capability` and `permission_callback()`.
218 * @since 3.16.0 Added the `$request` parameter.
219 * @since 3.17.0 Moved to the new API structure.
220 *
221 * @param WP_REST_Request|null $request The request object.
222 * @return WP_Error|bool True if the endpoint is available.
223 */
224 public function is_available_to_current_user( ?WP_REST_Request $request = null ) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
225 // Validate the API key and secret.
226 $api_key_validation = $this->validate_site_id_and_secret();
227 if ( is_wp_error( $api_key_validation ) ) {
228 return $api_key_validation;
229 }
230
231 // Validate the user capability.
232 $capability = $this->get_default_access_capability();
233 return current_user_can(
234 // phpcs:ignore WordPress.WP.Capabilities.Undetermined
235 $this->apply_capability_filters( $capability )
236 );
237 }
238
239 /**
240 * Returns the user capability allowing access to the endpoint, after having
241 * applied capability filters.
242 *
243 * The default access capability is not passed here by default, to allow for
244 * a more explicit declaration in child classes.
245 *
246 * @since 3.14.0
247 * @since 3.17.0 Moved to the new API structure.
248 *
249 * @param string $capability The original capability allowing access.
250 * @return string The capability allowing access after applying the filters.
251 */
252 public function apply_capability_filters( string $capability ): string {
253 /**
254 * Filter to change the default user capability for all private endpoints.
255 *
256 * @var string
257 */
258 $default_user_capability = apply_filters(
259 'wp_parsely_user_capability_for_all_private_apis',
260 $capability
261 );
262
263 /**
264 * Filter to change the user capability for the specific endpoint.
265 *
266 * @var string
267 */
268 $endpoint_specific_user_capability = apply_filters(
269 'wp_parsely_user_capability_for_' .
270 Utils::convert_endpoint_to_filter_key( static::get_endpoint_name() ) .
271 '_api',
272 $default_user_capability
273 );
274
275 return $endpoint_specific_user_capability;
276 }
277
278 /**
279 * Validates that the Site ID and secret are set.
280 * If the API secret is not required, it will not be validated.
281 *
282 * @since 3.13.0
283 * @since 3.17.0 Moved to the new API structure and renamed from `validate_apikey_and_secret`.
284 *
285 * @param bool $require_api_secret Specifies if the API Secret is required.
286 * @return WP_Error|bool
287 */
288 public function validate_site_id_and_secret( bool $require_api_secret = true ) {
289 if ( false === $this->parsely->site_id_is_set() ) {
290 return new WP_Error(
291 'parsely_site_id_not_set',
292 __( 'A Parse.ly Site ID must be set in site options to use this endpoint', 'wp-parsely' ),
293 array( 'status' => 403 )
294 );
295 }
296
297 if ( $require_api_secret && false === $this->parsely->api_secret_is_set() ) {
298 return new WP_Error(
299 'parsely_api_secret_not_set',
300 __( 'A Parse.ly API Secret must be set in site options to use this endpoint', 'wp-parsely' ),
301 array( 'status' => 403 )
302 );
303 }
304
305 return true;
306 }
307 }
308