PluginProbe
Cool Flipbox – Shortcode & Gutenberg Block / 1.4
Cool Flipbox – Shortcode & Gutenberg Block v1.4
trunk 1.4 1.5 1.6 1.6.2 1.7.1 1.8.3 1.9.0 1.9.1 1.9.2 1.9.3 1.9.4 1.9.5 1.9.6 1.9.7 1.9.8 1.9.9 2.0.0 2.0.1
flip-boxes / CMB2 / includes / rest-api / CMB2_REST_Controller_Fields.php

CMB2_REST_Controller_Fields.php in Cool Flipbox – Shortcode & Gutenberg Block 1.4, at CMB2/includes/rest-api/CMB2_REST_Controller_Fields.php

503 lines 15.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * CMB2 objects/fields endpoint for WordPres REST API.
4 * Allows access to fields registered to a specific box.
5 *
6 * @todo Add better documentation.
7 * @todo Research proper schema.
8 *
9 * @since 2.2.3
10 *
11 * @category WordPress_Plugin
12 * @package CMB2
13 * @author CMB2 team
14 * @license GPL-2.0+
15 * @link https://cmb2.io
16 */
17 class CMB2_REST_Controller_Fields extends CMB2_REST_Controller_Boxes {
18
19 /**
20 * Register the routes for the objects of the controller.
21 *
22 * @since 2.2.3
23 */
24 public function register_routes() {
25 $args = array(
26 '_embed' => array(
27 'description' => __( 'Includes the box object which the fields are registered to in the response.', 'cmb2' ),
28 ),
29 '_rendered' => array(
30 'description' => __( 'When the \'_rendered\' argument is passed, the renderable field attributes will be returned fully rendered. By default, the names of the callback handers for the renderable attributes will be returned.', 'cmb2' ),
31 ),
32 'object_id' => array(
33 'description' => __( 'To view or modify the field\'s value, the \'object_id\' and \'object_type\' arguments are required.', 'cmb2' ),
34 ),
35 'object_type' => array(
36 'description' => __( 'To view or modify the field\'s value, the \'object_id\' and \'object_type\' arguments are required.', 'cmb2' ),
37 ),
38 );
39
40 // Returns specific box's fields.
41 register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<cmb_id>[\w-]+)/fields/', array(
42 array(
43 'methods' => WP_REST_Server::READABLE,
44 'permission_callback' => array( $this, 'get_items_permissions_check' ),
45 'callback' => array( $this, 'get_items' ),
46 'args' => $args,
47 ),
48 'schema' => array( $this, 'get_item_schema' ),
49 ) );
50
51 $delete_args = $args;
52 $delete_args['object_id']['required'] = true;
53 $delete_args['object_type']['required'] = true;
54
55 // Returns specific field data.
56 register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<cmb_id>[\w-]+)/fields/(?P<field_id>[\w-]+)', array(
57 array(
58 'methods' => WP_REST_Server::READABLE,
59 'permission_callback' => array( $this, 'get_item_permissions_check' ),
60 'callback' => array( $this, 'get_item' ),
61 'args' => $args,
62 ),
63 array(
64 'methods' => WP_REST_Server::EDITABLE,
65 'permission_callback' => array( $this, 'update_item_permissions_check' ),
66 'callback' => array( $this, 'update_item' ),
67 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
68 'args' => $args,
69 ),
70 array(
71 'methods' => WP_REST_Server::DELETABLE,
72 'permission_callback' => array( $this, 'delete_item_permissions_check' ),
73 'callback' => array( $this, 'delete_item' ),
74 'args' => $delete_args,
75 ),
76 'schema' => array( $this, 'get_item_schema' ),
77 ) );
78 }
79
80 /**
81 * Check if a given request has access to get fields.
82 * By default, no special permissions needed, but filtering return value.
83 *
84 * @since 2.2.3
85 *
86 * @param WP_REST_Request $request Full data about the request.
87 * @return WP_Error|boolean
88 */
89 public function get_items_permissions_check( $request ) {
90 $this->initiate_rest_read_box( $request, 'fields_read' );
91 $can_access = true;
92
93 /**
94 * By default, no special permissions needed.
95 *
96 * @since 2.2.3
97 *
98 * @param bool $can_access Whether this CMB2 endpoint can be accessed.
99 * @param object $controller This CMB2_REST_Controller object.
100 */
101 return $this->maybe_hook_callback_and_apply_filters( 'cmb2_api_get_fields_permissions_check', $can_access );
102 }
103
104 /**
105 * Get all public CMB2 box fields.
106 *
107 * @since 2.2.3
108 *
109 * @param WP_REST_Request $request Full data about the request.
110 * @return WP_Error|WP_REST_Response
111 */
112 public function get_items( $request ) {
113 if ( ! $this->rest_box ) {
114 $this->initiate_rest_read_box( $request, 'fields_read' );
115 }
116
117 if ( is_wp_error( $this->rest_box ) ) {
118 return $this->rest_box;
119 }
120
121 $fields = array();
122 foreach ( $this->rest_box->cmb->prop( 'fields', array() ) as $field ) {
123
124 // Make sure this field can be read.
125 $this->field = $this->rest_box->field_can_read( $field['id'], true );
126
127 // And make sure current user can view this box.
128 if ( $this->field && $this->get_item_permissions_check_filter() ) {
129 $fields[ $field['id'] ] = $this->server->response_to_data(
130 $this->prepare_field_response(),
131 isset( $this->request['_embed'] )
132 );
133 }
134 }
135
136 return $this->prepare_item( $fields );
137 }
138
139 /**
140 * Check if a given request has access to a field.
141 * By default, no special permissions needed, but filtering return value.
142 *
143 * @since 2.2.3
144 *
145 * @param WP_REST_Request $request Full details about the request.
146 * @return WP_Error|boolean
147 */
148 public function get_item_permissions_check( $request ) {
149 $this->initiate_rest_read_box( $request, 'field_read' );
150 if ( ! is_wp_error( $this->rest_box ) ) {
151 $this->field = $this->rest_box->field_can_read( $this->request->get_param( 'field_id' ), true );
152 }
153
154 return $this->get_item_permissions_check_filter();
155 }
156
157 /**
158 * Check by filter if a given request has access to a field.
159 * By default, no special permissions needed, but filtering return value.
160 *
161 * @since 2.2.3
162 *
163 * @param bool $can_access Whether the current request has access to view the field by default.
164 * @return WP_Error|boolean
165 */
166 public function get_item_permissions_check_filter( $can_access = true ) {
167 /**
168 * By default, no special permissions needed.
169 *
170 * @since 2.2.3
171 *
172 * @param bool $can_access Whether this CMB2 endpoint can be accessed.
173 * @param object $controller This CMB2_REST_Controller object.
174 */
175 return $this->maybe_hook_callback_and_apply_filters( 'cmb2_api_get_field_permissions_check', $can_access );
176 }
177
178 /**
179 * Get one CMB2 field from the collection.
180 *
181 * @since 2.2.3
182 *
183 * @param WP_REST_Request $request Full data about the request.
184 * @return WP_Error|WP_REST_Response
185 */
186 public function get_item( $request ) {
187 $this->initiate_rest_read_box( $request, 'field_read' );
188
189 if ( is_wp_error( $this->rest_box ) ) {
190 return $this->rest_box;
191 }
192
193 return $this->prepare_read_field( $this->request->get_param( 'field_id' ) );
194 }
195
196 /**
197 * Check if a given request has access to update a field value.
198 * By default, requires 'edit_others_posts' capability, but filtering return value.
199 *
200 * @since 2.2.3
201 *
202 * @param WP_REST_Request $request Full details about the request.
203 * @return WP_Error|boolean
204 */
205 public function update_item_permissions_check( $request ) {
206 $this->initiate_rest_read_box( $request, 'field_value_update' );
207 if ( ! is_wp_error( $this->rest_box ) ) {
208 $this->field = $this->rest_box->field_can_edit( $this->request->get_param( 'field_id' ), true );
209 }
210
211 $can_update = current_user_can( 'edit_others_posts' );
212
213 /**
214 * By default, 'edit_others_posts' is required capability.
215 *
216 * @since 2.2.3
217 *
218 * @param bool $can_update Whether this CMB2 endpoint can be accessed.
219 * @param object $controller This CMB2_REST_Controller object.
220 */
221 return $this->maybe_hook_callback_and_apply_filters( 'cmb2_api_update_field_value_permissions_check', $can_update );
222 }
223
224 /**
225 * Update CMB2 field value.
226 *
227 * @since 2.2.3
228 *
229 * @param WP_REST_Request $request Full data about the request.
230 * @return WP_Error|WP_REST_Response
231 */
232 public function update_item( $request ) {
233 $this->initiate_rest_read_box( $request, 'field_value_update' );
234
235 if ( ! $this->request['value'] ) {
236 return new WP_Error( 'cmb2_rest_update_field_error', __( 'CMB2 Field value cannot be updated without the value parameter specified.', 'cmb2' ), array(
237 'status' => 400,
238 ) );
239 }
240
241 return $this->modify_field_value( 'updated' );
242 }
243
244 /**
245 * Check if a given request has access to delete a field value.
246 * By default, requires 'delete_others_posts' capability, but filtering return value.
247 *
248 * @since 2.2.3
249 *
250 * @param WP_REST_Request $request Full details about the request.
251 * @return WP_Error|boolean
252 */
253 public function delete_item_permissions_check( $request ) {
254 $this->initiate_rest_read_box( $request, 'field_value_delete' );
255 if ( ! is_wp_error( $this->rest_box ) ) {
256 $this->field = $this->rest_box->field_can_edit( $this->request->get_param( 'field_id' ), true );
257 }
258
259 $can_delete = current_user_can( 'delete_others_posts' );
260
261 /**
262 * By default, 'delete_others_posts' is required capability.
263 *
264 * @since 2.2.3
265 *
266 * @param bool $can_delete Whether this CMB2 endpoint can be accessed.
267 * @param object $controller This CMB2_REST_Controller object.
268 */
269 return $this->maybe_hook_callback_and_apply_filters( 'cmb2_api_delete_field_value_permissions_check', $can_delete );
270 }
271
272 /**
273 * Delete CMB2 field value.
274 *
275 * @since 2.2.3
276 *
277 * @param WP_REST_Request $request Full data about the request.
278 * @return WP_Error|WP_REST_Response
279 */
280 public function delete_item( $request ) {
281 $this->initiate_rest_read_box( $request, 'field_value_delete' );
282
283 return $this->modify_field_value( 'deleted' );
284 }
285
286 /**
287 * Modify CMB2 field value.
288 *
289 * @since 2.2.3
290 *
291 * @param string $activity The modification activity (updated or deleted).
292 * @return WP_Error|WP_REST_Response
293 */
294 public function modify_field_value( $activity ) {
295
296 if ( ! $this->request['object_id'] || ! $this->request['object_type'] ) {
297 return new WP_Error( 'cmb2_rest_modify_field_value_error', __( 'CMB2 Field value cannot be modified without the object_id and object_type parameters specified.', 'cmb2' ), array(
298 'status' => 400,
299 ) );
300 }
301
302 if ( is_wp_error( $this->rest_box ) ) {
303 return $this->rest_box;
304 }
305
306 $this->field = $this->rest_box->field_can_edit(
307 $this->field ? $this->field : $this->request->get_param( 'field_id' ),
308 true
309 );
310
311 if ( ! $this->field ) {
312 return new WP_Error( 'cmb2_rest_no_field_by_id_error', __( 'No field found by that id.', 'cmb2' ), array(
313 'status' => 403,
314 ) );
315 }
316
317 $this->field->args[ "value_{$activity}" ] = (bool) 'deleted' === $activity
318 ? $this->field->remove_data()
319 : $this->field->save_field( $this->request['value'] );
320
321 // If options page, save the $activity options
322 if ( 'options-page' == $this->request['object_type'] ) {
323 $this->field->args[ "value_{$activity}" ] = cmb2_options( $this->request['object_id'] )->set();
324 }
325
326 return $this->prepare_read_field( $this->field );
327 }
328
329 /**
330 * Get a response object for a specific field ID.
331 *
332 * @since 2.2.3
333 *
334 * @param string\CMB2_Field Field id or Field object.
335 * @return WP_Error|WP_REST_Response
336 */
337 public function prepare_read_field( $field ) {
338 $this->field = $this->rest_box->field_can_read( $field, true );
339
340 if ( ! $this->field ) {
341 return new WP_Error( 'cmb2_rest_no_field_by_id_error', __( 'No field found by that id.', 'cmb2' ), array(
342 'status' => 403,
343 ) );
344 }
345
346 return $this->prepare_item( $this->prepare_field_response() );
347 }
348
349 /**
350 * Get a specific field response.
351 *
352 * @since 2.2.3
353 *
354 * @param CMB2_Field Field object.
355 * @return array Response array.
356 */
357 public function prepare_field_response() {
358 $field_data = $this->prepare_field_data( $this->field );
359 $response = rest_ensure_response( $field_data );
360
361 $response->add_links( $this->prepare_links( $this->field ) );
362
363 return $response;
364 }
365
366 /**
367 * Prepare the field data array for JSON.
368 *
369 * @since 2.2.3
370 *
371 * @param CMB2_Field $field field object.
372 *
373 * @return array Array of field data.
374 */
375 protected function prepare_field_data( CMB2_Field $field ) {
376 $field_data = array();
377 $params_to_ignore = array( 'show_in_rest', 'options' );
378 $params_to_rename = array(
379 'label_cb' => 'label',
380 'options_cb' => 'options',
381 );
382
383 // Run this first so the js_dependencies arg is populated.
384 $rendered = ( $cb = $field->maybe_callback( 'render_row_cb' ) )
385 // Ok, callback is good, let's run it.
386 ? $this->get_cb_results( $cb, $field->args(), $field )
387 : false;
388
389 $field_args = $field->args();
390
391 foreach ( $field_args as $key => $value ) {
392 if ( in_array( $key, $params_to_ignore, true ) ) {
393 continue;
394 }
395
396 if ( 'options_cb' === $key ) {
397 $value = $field->options();
398 } elseif ( in_array( $key, CMB2_Field::$callable_fields, true ) ) {
399
400 if ( isset( $this->request['_rendered'] ) ) {
401 $value = $key === 'render_row_cb' ? $rendered : $field->get_param_callback_result( $key );
402 } elseif ( is_array( $value ) ) {
403 // We need to rewrite callbacks as string as they will cause
404 // JSON recursion errors.
405 $class = is_string( $value[0] ) ? $value[0] : get_class( $value[0] );
406 $value = $class . '::' . $value[1];
407 }
408 }
409
410 $key = isset( $params_to_rename[ $key ] ) ? $params_to_rename[ $key ] : $key;
411
412 if ( empty( $value ) || is_scalar( $value ) || is_array( $value ) ) {
413 $field_data[ $key ] = $value;
414 } else {
415 $field_data[ $key ] = sprintf( __( 'Value Error for %s', 'cmb2' ), $key );
416 }
417 }
418
419 if ( $this->request['object_id'] && $this->request['object_type'] ) {
420 $field_data['value'] = $field->get_rest_value();
421 }
422
423 return $field_data;
424 }
425
426 /**
427 * Return an array of contextual links for field/fields.
428 *
429 * @since 2.2.3
430 *
431 * @param CMB2_Field $field Field object to build links from.
432 *
433 * @return array Array of links
434 */
435 protected function prepare_links( $field ) {
436 $boxbase = $this->namespace_base . '/' . $this->rest_box->cmb->cmb_id;
437 $query_string = $this->get_query_string();
438
439 $links = array(
440 'self' => array(
441 'href' => rest_url( trailingslashit( $boxbase ) . 'fields/' . $field->_id() . $query_string ),
442 ),
443 'collection' => array(
444 'href' => rest_url( trailingslashit( $boxbase ) . 'fields' . $query_string ),
445 ),
446 'up' => array(
447 'embeddable' => true,
448 'href' => rest_url( $boxbase . $query_string ),
449 ),
450 );
451
452 return $links;
453 }
454
455 /**
456 * Checks if the CMB2 box or field has any registered callback parameters for the given filter.
457 *
458 * The registered handlers will have a property name which matches the filter, except:
459 * - The 'cmb2_api' prefix will be removed
460 * - A '_cb' suffix will be added (to stay inline with other '*_cb' parameters).
461 *
462 * @since 2.2.3
463 *
464 * @param string $filter The filter name.
465 * @param bool $default_val The default filter value.
466 *
467 * @return bool The possibly-modified filter value (if the _cb param is a non-callable).
468 */
469 public function maybe_hook_registered_callback( $filter, $default_val ) {
470 $default_val = parent::maybe_hook_registered_callback( $filter, $default_val );
471
472 if ( $this->field ) {
473
474 // Hook field specific filter callbacks.
475 $val = $this->field->maybe_hook_parameter( $filter, $default_val );
476 if ( null !== $val ) {
477 $default_val = $val;
478 }
479 }
480
481 return $default_val;
482 }
483
484 /**
485 * Unhooks any CMB2 box or field registered callback parameters for the given filter.
486 *
487 * @since 2.2.3
488 *
489 * @param string $filter The filter name.
490 *
491 * @return void
492 */
493 public function maybe_unhook_registered_callback( $filter ) {
494 parent::maybe_unhook_registered_callback( $filter );
495
496 if ( $this->field ) {
497 // Unhook field specific filter callbacks.
498 $this->field->maybe_hook_parameter( $filter, null, 'remove_filter' );
499 }
500 }
501
502 }
503