1
|
|
|
<?php |
|
|
|
|
2
|
|
|
if ( ! class_exists( 'WP_REST_Controller' ) ) { |
3
|
|
|
// Shim the WP_REST_Controller class if wp-api plugin not installed, & not in core. |
4
|
|
|
require_once cmb2_dir( 'includes/shim/WP_REST_Controller.php' ); |
5
|
|
|
} |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* Creates CMB2 objects/fields endpoint for WordPres REST API. |
9
|
|
|
* Allows access to fields registered to a specific post type and more. |
10
|
|
|
* |
11
|
|
|
* @todo Add better documentation. |
12
|
|
|
* @todo Research proper schema. |
13
|
|
|
* |
14
|
|
|
* @since 2.2.3 |
15
|
|
|
* |
16
|
|
|
* @category WordPress_Plugin |
17
|
|
|
* @package CMB2 |
18
|
|
|
* @author WebDevStudios |
19
|
|
|
* @license GPL-2.0+ |
20
|
|
|
* @link http://webdevstudios.com |
21
|
|
|
*/ |
22
|
|
|
abstract class CMB2_REST_Controller extends WP_REST_Controller { |
|
|
|
|
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* The namespace of this controller's route. |
26
|
|
|
* |
27
|
|
|
* @var string |
28
|
|
|
*/ |
29
|
|
|
protected $namespace = CMB2_REST::NAME_SPACE; |
30
|
|
|
|
31
|
|
|
/** |
32
|
|
|
* The base of this controller's route. |
33
|
|
|
* |
34
|
|
|
* @var string |
35
|
|
|
*/ |
36
|
|
|
protected $rest_base; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* The current request object |
40
|
|
|
* |
41
|
|
|
* @var WP_REST_Request $request |
42
|
|
|
* @since 2.2.3 |
43
|
|
|
*/ |
44
|
|
|
public $request; |
45
|
|
|
|
46
|
|
|
/** |
47
|
|
|
* The current server object |
48
|
|
|
* |
49
|
|
|
* @var WP_REST_Server $server |
50
|
|
|
* @since 2.2.3 |
51
|
|
|
*/ |
52
|
|
|
public $server; |
53
|
|
|
|
54
|
|
|
/** |
55
|
|
|
* Box object id |
56
|
|
|
* |
57
|
|
|
* @var mixed |
58
|
|
|
* @since 2.2.3 |
59
|
|
|
*/ |
60
|
|
|
public $object_id = null; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Box object type |
64
|
|
|
* |
65
|
|
|
* @var string |
66
|
|
|
* @since 2.2.3 |
67
|
|
|
*/ |
68
|
|
|
public $object_type = ''; |
69
|
|
|
|
70
|
|
|
/** |
71
|
|
|
* CMB2 Instance |
72
|
|
|
* |
73
|
|
|
* @var CMB2_REST |
74
|
|
|
*/ |
75
|
|
|
protected $rest_box; |
76
|
|
|
|
77
|
|
|
/** |
78
|
|
|
* CMB2_Field Instance |
79
|
|
|
* |
80
|
|
|
* @var CMB2_Field |
81
|
|
|
*/ |
82
|
|
|
protected $field; |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* The initial route |
86
|
|
|
* |
87
|
|
|
* @var string |
88
|
|
|
* @since 2.2.3 |
89
|
|
|
*/ |
90
|
|
|
protected static $route = ''; |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* Defines which endpoint the initial request is. |
94
|
|
|
* |
95
|
|
|
* @var string $request_type |
96
|
|
|
* @since 2.2.3 |
97
|
|
|
*/ |
98
|
|
|
protected static $request_type = ''; |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* Constructor |
102
|
|
|
* |
103
|
|
|
* @since 2.2.3 |
104
|
|
|
*/ |
105
|
|
|
public function __construct( WP_REST_Server $wp_rest_server ) { |
106
|
|
|
$this->server = $wp_rest_server; |
107
|
|
|
} |
108
|
|
|
|
109
|
|
|
/** |
110
|
|
|
* A wrapper for `apply_filters` which checks for box/field properties to hook to the filter. |
111
|
|
|
* |
112
|
|
|
* Checks if a CMB object callback property exists, and if it does, |
113
|
|
|
* hook it to the permissions filter. |
114
|
|
|
* |
115
|
|
|
* @since 2.2.3 |
116
|
|
|
* |
117
|
|
|
* @param string $filter The name of the filter to apply. |
118
|
|
|
* @param bool $default_access The default access for this request. |
119
|
|
|
* |
120
|
|
|
* @return void |
121
|
|
|
*/ |
122
|
|
|
public function maybe_hook_callback_and_apply_filters( $filter, $default_access ) { |
123
|
|
|
if ( ! $this->rest_box && $this->request->get_param( 'cmb_id' ) ) { |
124
|
|
|
$this->rest_box = CMB2_REST::get_rest_box( $this->request->get_param( 'cmb_id' ) ); |
|
|
|
|
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
$default_access = $this->maybe_hook_registered_callback( $filter, $default_access ); |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Apply the permissions check filter. |
131
|
|
|
* |
132
|
|
|
* @since 2.2.3 |
133
|
|
|
* |
134
|
|
|
* @param bool $default_access Whether this CMB2 endpoint can be accessed. |
135
|
|
|
* @param object $controller This CMB2_REST_Controller object. |
136
|
|
|
*/ |
137
|
|
|
$default_access = apply_filters( $filter, $default_access, $this ); |
138
|
|
|
|
139
|
|
|
$this->maybe_unhook_registered_callback( $filter ); |
140
|
|
|
|
141
|
|
|
return $default_access; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
/** |
145
|
|
|
* Checks if the CMB2 box has any registered callback parameters for the given filter. |
146
|
|
|
* |
147
|
|
|
* The registered handlers will have a property name which matches the filter, except: |
148
|
|
|
* - The 'cmb2_api' prefix will be removed |
149
|
|
|
* - A '_cb' suffix will be added (to stay inline with other '*_cb' parameters). |
150
|
|
|
* |
151
|
|
|
* @since 2.2.3 |
152
|
|
|
* |
153
|
|
|
* @param string $filter The filter name. |
154
|
|
|
* @param bool $default_val The default filter value. |
155
|
|
|
* |
156
|
|
|
* @return bool The possibly-modified filter value (if the '*_cb' param is non-callable). |
157
|
|
|
*/ |
158
|
|
|
public function maybe_hook_registered_callback( $filter, $default_val ) { |
159
|
|
|
if ( ! $this->rest_box || is_wp_error( $this->rest_box ) ) { |
160
|
|
|
return $default_val; |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
// Hook box specific filter callbacks. |
164
|
|
|
$val = $this->rest_box->cmb->maybe_hook_parameter( $filter, $default_val ); |
165
|
|
|
if ( null !== $val ) { |
166
|
|
|
$default_val = $val; |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
return $default_val; |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
/** |
173
|
|
|
* Unhooks any CMB2 box registered callback parameters for the given filter. |
174
|
|
|
* |
175
|
|
|
* @since 2.2.3 |
176
|
|
|
* |
177
|
|
|
* @param string $filter The filter name. |
178
|
|
|
* |
179
|
|
|
* @return void |
180
|
|
|
*/ |
181
|
|
|
public function maybe_unhook_registered_callback( $filter ) { |
182
|
|
|
if ( ! $this->rest_box || is_wp_error( $this->rest_box ) ) { |
183
|
|
|
return; |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
// Unhook box specific filter callbacks. |
187
|
|
|
$this->rest_box->cmb->maybe_hook_parameter( $filter, null, 'remove_filter' ); |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* Prepare a CMB2 object for serialization |
192
|
|
|
* |
193
|
|
|
* @since 2.2.3 |
194
|
|
|
* |
195
|
|
|
* @param mixed $data |
196
|
|
|
* @return array $data |
197
|
|
|
*/ |
198
|
|
|
public function prepare_item( $data ) { |
199
|
|
|
return $this->prepare_item_for_response( $data, $this->request ); |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* Output buffers a callback and returns the results. |
204
|
|
|
* |
205
|
|
|
* @since 2.2.3 |
206
|
|
|
* |
207
|
|
|
* @param mixed $cb Callable function/method. |
208
|
|
|
* @return mixed Results of output buffer after calling function/method. |
209
|
|
|
*/ |
210
|
|
|
public function get_cb_results( $cb ) { |
211
|
|
|
$args = func_get_args(); |
212
|
|
|
array_shift( $args ); // ignore $cb |
213
|
|
|
ob_start(); |
214
|
|
|
call_user_func_array( $cb, $args ); |
215
|
|
|
|
216
|
|
|
return ob_get_clean(); |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
/** |
220
|
|
|
* Prepare the CMB2 item for the REST response. |
221
|
|
|
* |
222
|
|
|
* @since 2.2.3 |
223
|
|
|
* |
224
|
|
|
* @param mixed $item WordPress representation of the item. |
|
|
|
|
225
|
|
|
* @param WP_REST_Request $request Request object. |
226
|
|
|
* @return WP_REST_Response $response |
227
|
|
|
*/ |
228
|
|
|
public function prepare_item_for_response( $data, $request = null ) { |
229
|
|
|
$data = $this->filter_response_by_context( $data, $this->request['context'] ); |
230
|
|
|
|
231
|
|
|
/** |
232
|
|
|
* Filter the prepared CMB2 item response. |
233
|
|
|
* |
234
|
|
|
* @since 2.2.3 |
235
|
|
|
* |
236
|
|
|
* @param mixed $data Prepared data |
237
|
|
|
* @param object $request The WP_REST_Request object |
238
|
|
|
* @param object $cmb2_endpoints This endpoints object |
239
|
|
|
*/ |
240
|
|
|
return apply_filters( 'cmb2_rest_prepare', rest_ensure_response( $data ), $this->request, $this ); |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
/** |
244
|
|
|
* Initiates the request property and the rest_box property if box is readable. |
245
|
|
|
* |
246
|
|
|
* @since 2.2.3 |
247
|
|
|
* |
248
|
|
|
* @param WP_REST_Request $request Request object. |
249
|
|
|
* @param string $request_type A description of the type of request being made. |
250
|
|
|
* |
251
|
|
|
* @return void |
252
|
|
|
*/ |
253
|
|
View Code Duplication |
protected function initiate_rest_read_box( $request, $request_type ) { |
|
|
|
|
254
|
|
|
$this->initiate_rest_box( $request, $request_type ); |
255
|
|
|
|
256
|
|
|
if ( ! is_wp_error( $this->rest_box ) && ! $this->rest_box->rest_read ) { |
|
|
|
|
257
|
|
|
$this->rest_box = new WP_Error( 'cmb2_rest_no_read_error', __( 'This box does not have read permissions.', 'cmb2' ), array( |
|
|
|
|
258
|
|
|
'status' => 403, |
259
|
|
|
) ); |
260
|
|
|
} |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* Initiates the request property and the rest_box property if box is writeable. |
265
|
|
|
* |
266
|
|
|
* @since 2.2.3 |
267
|
|
|
* |
268
|
|
|
* @param WP_REST_Request $request Request object. |
269
|
|
|
* @param string $request_type A description of the type of request being made. |
270
|
|
|
* |
271
|
|
|
* @return void |
272
|
|
|
*/ |
273
|
|
View Code Duplication |
protected function initiate_rest_edit_box( $request, $request_type ) { |
|
|
|
|
274
|
|
|
$this->initiate_rest_box( $request, $request_type ); |
275
|
|
|
|
276
|
|
|
if ( ! is_wp_error( $this->rest_box ) && ! $this->rest_box->rest_edit ) { |
|
|
|
|
277
|
|
|
$this->rest_box = new WP_Error( 'cmb2_rest_no_write_error', __( 'This box does not have write permissions.', 'cmb2' ), array( |
|
|
|
|
278
|
|
|
'status' => 403, |
279
|
|
|
) ); |
280
|
|
|
} |
281
|
|
|
} |
282
|
|
|
|
283
|
|
|
/** |
284
|
|
|
* Initiates the request property and the rest_box property. |
285
|
|
|
* |
286
|
|
|
* @since 2.2.3 |
287
|
|
|
* |
288
|
|
|
* @param WP_REST_Request $request Request object. |
289
|
|
|
* @param string $request_type A description of the type of request being made. |
290
|
|
|
* |
291
|
|
|
* @return void |
292
|
|
|
*/ |
293
|
|
|
protected function initiate_rest_box( $request, $request_type ) { |
294
|
|
|
$this->initiate_request( $request, $request_type ); |
295
|
|
|
|
296
|
|
|
$this->rest_box = CMB2_REST::get_rest_box( $this->request->get_param( 'cmb_id' ) ); |
|
|
|
|
297
|
|
|
|
298
|
|
|
if ( ! $this->rest_box ) { |
299
|
|
|
|
300
|
|
|
$this->rest_box = new WP_Error( 'cmb2_rest_box_not_found_error', __( 'No box found by that id. A box needs to be registered with the "show_in_rest" parameter configured.', 'cmb2' ), array( |
|
|
|
|
301
|
|
|
'status' => 403, |
302
|
|
|
) ); |
303
|
|
|
|
304
|
|
|
} else { |
305
|
|
|
|
306
|
|
View Code Duplication |
if ( isset( $this->request['object_id'] ) ) { |
|
|
|
|
307
|
|
|
$this->rest_box->cmb->object_id( sanitize_text_field( $this->request['object_id'] ) ); |
308
|
|
|
} |
309
|
|
|
|
310
|
|
View Code Duplication |
if ( isset( $this->request['object_type'] ) ) { |
|
|
|
|
311
|
|
|
$this->rest_box->cmb->object_type( sanitize_text_field( $this->request['object_type'] ) ); |
312
|
|
|
} |
313
|
|
|
} |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
/** |
317
|
|
|
* Initiates the request property and sets up the initial static properties. |
318
|
|
|
* |
319
|
|
|
* @since 2.2.3 |
320
|
|
|
* |
321
|
|
|
* @param WP_REST_Request $request Request object. |
322
|
|
|
* @param string $request_type A description of the type of request being made. |
323
|
|
|
* |
324
|
|
|
* @return void |
325
|
|
|
*/ |
326
|
|
|
public function initiate_request( $request, $request_type ) { |
327
|
|
|
$this->request = $request; |
328
|
|
|
|
329
|
|
|
if ( ! isset( $this->request['context'] ) || empty( $this->request['context'] ) ) { |
330
|
|
|
$this->request['context'] = 'view'; |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
if ( ! self::$request_type ) { |
334
|
|
|
self::$request_type = $request_type; |
335
|
|
|
} |
336
|
|
|
|
337
|
|
|
if ( ! self::$route ) { |
338
|
|
|
self::$route = $this->request->get_route(); |
339
|
|
|
} |
340
|
|
|
} |
341
|
|
|
|
342
|
|
|
/** |
343
|
|
|
* Useful when getting `_embed`-ed items |
344
|
|
|
* |
345
|
|
|
* @since 2.2.3 |
346
|
|
|
* |
347
|
|
|
* @return string Initial requested type. |
348
|
|
|
*/ |
349
|
|
|
public static function get_intial_request_type() { |
350
|
|
|
return self::$request_type; |
351
|
|
|
} |
352
|
|
|
|
353
|
|
|
/** |
354
|
|
|
* Useful when getting `_embed`-ed items |
355
|
|
|
* |
356
|
|
|
* @since 2.2.3 |
357
|
|
|
* |
358
|
|
|
* @return string Initial requested route. |
359
|
|
|
*/ |
360
|
|
|
public static function get_intial_route() { |
361
|
|
|
return self::$route; |
362
|
|
|
} |
363
|
|
|
|
364
|
|
|
/** |
365
|
|
|
* Get CMB2 fields schema, conforming to JSON Schema |
366
|
|
|
* |
367
|
|
|
* @since 2.2.3 |
368
|
|
|
* |
369
|
|
|
* @return array |
370
|
|
|
*/ |
371
|
|
|
public function get_item_schema() { |
372
|
|
|
$schema = array( |
373
|
|
|
'$schema' => 'http://json-schema.org/draft-04/schema#', |
374
|
|
|
'title' => 'CMB2', |
375
|
|
|
'type' => 'object', |
376
|
|
|
'properties' => array( |
377
|
|
|
'description' => array( |
378
|
|
|
'description' => __( 'A human-readable description of the object.', 'cmb2' ), |
379
|
|
|
'type' => 'string', |
380
|
|
|
'context' => array( |
381
|
|
|
'view', |
382
|
|
|
), |
383
|
|
|
), |
384
|
|
|
'name' => array( |
385
|
|
|
'description' => __( 'The id for the object.', 'cmb2' ), |
386
|
|
|
'type' => 'integer', |
387
|
|
|
'context' => array( |
388
|
|
|
'view', |
389
|
|
|
), |
390
|
|
|
), |
391
|
|
|
'name' => array( |
392
|
|
|
'description' => __( 'The title for the object.', 'cmb2' ), |
393
|
|
|
'type' => 'string', |
394
|
|
|
'context' => array( |
395
|
|
|
'view', |
396
|
|
|
), |
397
|
|
|
), |
398
|
|
|
), |
399
|
|
|
); |
400
|
|
|
|
401
|
|
|
return $this->add_additional_fields_schema( $schema ); |
402
|
|
|
} |
403
|
|
|
|
404
|
|
|
/** |
405
|
|
|
* Return an array of contextual links for endpoint/object |
406
|
|
|
* |
407
|
|
|
* @link http://v2.wp-api.org/extending/linking/ |
408
|
|
|
* @link http://www.iana.org/assignments/link-relations/link-relations.xhtml |
409
|
|
|
* |
410
|
|
|
* @since 2.2.3 |
411
|
|
|
* |
412
|
|
|
* @param mixed $object Object to build links from. |
413
|
|
|
* |
414
|
|
|
* @return array Array of links |
415
|
|
|
*/ |
416
|
|
|
abstract protected function prepare_links( $object ); |
417
|
|
|
|
418
|
|
|
/** |
419
|
|
|
* Get whitelisted query strings from URL for appending to link URLS. |
420
|
|
|
* |
421
|
|
|
* @since 2.2.3 |
422
|
|
|
* |
423
|
|
|
* @return string URL query stringl |
424
|
|
|
*/ |
425
|
|
|
public function get_query_string() { |
426
|
|
|
$defaults = array( |
427
|
|
|
'object_id' => 0, |
428
|
|
|
'object_type' => '', |
429
|
|
|
'_rendered' => '', |
430
|
|
|
// '_embed' => '', |
|
|
|
|
431
|
|
|
); |
432
|
|
|
|
433
|
|
|
$query_string = ''; |
434
|
|
|
|
435
|
|
|
foreach ( $defaults as $key => $value ) { |
436
|
|
|
if ( isset( $this->request[ $key ] ) ) { |
437
|
|
|
$query_string .= $query_string ? '&' : '?'; |
438
|
|
|
$query_string .= $key; |
439
|
|
|
if ( $value = sanitize_text_field( $this->request[ $key ] ) ) { |
440
|
|
|
$query_string .= '=' . $value; |
441
|
|
|
} |
442
|
|
|
} |
443
|
|
|
} |
444
|
|
|
|
445
|
|
|
return $query_string; |
446
|
|
|
} |
447
|
|
|
|
448
|
|
|
} |
449
|
|
|
|
The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.
The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.
To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.