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.php

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

795 lines 21.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Handles hooking CMB2 objects/fields into the WordPres REST API
4 * which can allow fields to be read and/or updated.
5 *
6 * @since 2.2.3
7 *
8 * @category WordPress_Plugin
9 * @package CMB2
10 * @author CMB2 team
11 * @license GPL-2.0+
12 * @link https://cmb2.io
13 *
14 * @property-read read_fields Array of readable field objects.
15 * @property-read edit_fields Array of editable field objects.
16 * @property-read rest_read Whether CMB2 object is readable via the rest api.
17 * @property-read rest_edit Whether CMB2 object is editable via the rest api.
18 */
19 class CMB2_REST extends CMB2_Hookup_Base {
20
21 /**
22 * The current CMB2 REST endpoint version
23 *
24 * @var string
25 * @since 2.2.3
26 */
27 const VERSION = '1';
28
29 /**
30 * The CMB2 REST base namespace (v should always be followed by $version)
31 *
32 * @var string
33 * @since 2.2.3
34 */
35 const NAME_SPACE = 'cmb2/v1';
36
37 /**
38 * @var CMB2 object
39 * @since 2.2.3
40 */
41 public $cmb;
42
43 /**
44 * @var CMB2_REST[] objects
45 * @since 2.2.3
46 */
47 protected static $boxes = array();
48
49 /**
50 * @var array Array of cmb ids for each type.
51 * @since 2.2.3
52 */
53 protected static $type_boxes = array(
54 'post' => array(),
55 'user' => array(),
56 'comment' => array(),
57 'term' => array(),
58 );
59
60 /**
61 * Array of readable field objects.
62 *
63 * @var CMB2_Field[]
64 * @since 2.2.3
65 */
66 protected $read_fields = array();
67
68 /**
69 * Array of editable field objects.
70 *
71 * @var CMB2_Field[]
72 * @since 2.2.3
73 */
74 protected $edit_fields = array();
75
76 /**
77 * Whether CMB2 object is readable via the rest api.
78 *
79 * @var boolean
80 */
81 protected $rest_read = false;
82
83 /**
84 * Whether CMB2 object is editable via the rest api.
85 *
86 * @var boolean
87 */
88 protected $rest_edit = false;
89
90 /**
91 * A functionalized constructor, used for the hookup action callbacks.
92 *
93 * @since 2.2.6
94 *
95 * @param CMB2 $cmb The CMB2 object to hookup
96 *
97 * @return CMB2_Hookup_Base $hookup The hookup object.
98 */
99 public static function maybe_init_and_hookup( CMB2 $cmb ) {
100 if ( $cmb->prop( 'show_in_rest' ) && function_exists( 'rest_get_server' ) ) {
101
102 $hookup = new self( $cmb );
103
104 return $hookup->universal_hooks();
105 }
106
107 return false;
108 }
109
110 /**
111 * Constructor
112 *
113 * @since 2.2.3
114 *
115 * @param CMB2 $cmb The CMB2 object to be registered for the API.
116 */
117 public function __construct( CMB2 $cmb ) {
118 $this->cmb = $cmb;
119 self::$boxes[ $cmb->cmb_id ] = $this;
120
121 $show_value = $this->cmb->prop( 'show_in_rest' );
122
123 $this->rest_read = self::is_readable( $show_value );
124 $this->rest_edit = self::is_editable( $show_value );
125 }
126
127 /**
128 * Hooks to register on frontend and backend.
129 *
130 * @since 2.2.3
131 *
132 * @return void
133 */
134 public function universal_hooks() {
135 // hook up the CMB rest endpoint classes
136 $this->once( 'rest_api_init', array( __CLASS__, 'init_routes' ), 0 );
137
138 if ( function_exists( 'register_rest_field' ) ) {
139 $this->once( 'rest_api_init', array( __CLASS__, 'register_cmb2_fields' ), 50 );
140 }
141
142 $this->declare_read_edit_fields();
143
144 add_filter( 'is_protected_meta', array( $this, 'is_protected_meta' ), 10, 3 );
145
146 return $this;
147 }
148
149 /**
150 * Initiate the CMB2 Boxes and Fields routes
151 *
152 * @since 2.2.3
153 *
154 * @return void
155 */
156 public static function init_routes() {
157 $wp_rest_server = rest_get_server();
158
159 $boxes_controller = new CMB2_REST_Controller_Boxes( $wp_rest_server );
160 $boxes_controller->register_routes();
161
162 $fields_controller = new CMB2_REST_Controller_Fields( $wp_rest_server );
163 $fields_controller->register_routes();
164 }
165
166 /**
167 * Loop through REST boxes and call register_rest_field for each object type.
168 *
169 * @since 2.2.3
170 *
171 * @return void
172 */
173 public static function register_cmb2_fields() {
174 $alltypes = $taxonomies = array();
175
176 foreach ( self::$boxes as $cmb_id => $rest_box ) {
177 $types = array_flip( $rest_box->cmb->box_types( array( 'post' ) ) );
178
179 if ( isset( $types['user'] ) ) {
180 unset( $types['user'] );
181 self::$type_boxes['user'][ $cmb_id ] = $cmb_id;
182 }
183
184 if ( isset( $types['comment'] ) ) {
185 unset( $types['comment'] );
186 self::$type_boxes['comment'][ $cmb_id ] = $cmb_id;
187 }
188
189 if ( isset( $types['term'] ) ) {
190 unset( $types['term'] );
191
192 $taxonomies = array_merge(
193 $taxonomies,
194 CMB2_Utils::ensure_array( $rest_box->cmb->prop( 'taxonomies' ) )
195 );
196
197 self::$type_boxes['term'][ $cmb_id ] = $cmb_id;
198 }
199
200 if ( ! empty( $types ) ) {
201 $alltypes = array_merge( $alltypes, array_flip( $types ) );
202 self::$type_boxes['post'][ $cmb_id ] = $cmb_id;
203 }
204 }
205
206 $alltypes = array_unique( $alltypes );
207
208 if ( ! empty( $alltypes ) ) {
209 self::register_rest_field( $alltypes, 'post' );
210 }
211
212 if ( ! empty( self::$type_boxes['user'] ) ) {
213 self::register_rest_field( 'user', 'user' );
214 }
215
216 if ( ! empty( self::$type_boxes['comment'] ) ) {
217 self::register_rest_field( 'comment', 'comment' );
218 }
219
220 if ( ! empty( self::$type_boxes['term'] ) ) {
221 self::register_rest_field( $taxonomies, 'term' );
222 }
223 }
224
225 /**
226 * Wrapper for register_rest_field.
227 *
228 * @since 2.2.3
229 *
230 * @param string|array $object_types Object(s) the field is being registered
231 * to, "post"|"term"|"comment" etc.
232 * @param string $object_types Canonical object type for callbacks.
233 *
234 * @return void
235 */
236 protected static function register_rest_field( $object_types, $object_type ) {
237 register_rest_field( $object_types, 'cmb2', array(
238 'get_callback' => array( __CLASS__, "get_{$object_type}_rest_values" ),
239 'update_callback' => array( __CLASS__, "update_{$object_type}_rest_values" ),
240 'schema' => null, // @todo add schema
241 ) );
242 }
243
244 /**
245 * Setup readable and editable fields.
246 *
247 * @since 2.2.3
248 *
249 * @return void
250 */
251 protected function declare_read_edit_fields() {
252 foreach ( $this->cmb->prop( 'fields' ) as $field ) {
253 $show_in_rest = isset( $field['show_in_rest'] ) ? $field['show_in_rest'] : null;
254
255 if ( false === $show_in_rest ) {
256 continue;
257 }
258
259 if ( $this->can_read( $show_in_rest ) ) {
260 $this->read_fields[] = $field['id'];
261 }
262
263 if ( $this->can_edit( $show_in_rest ) ) {
264 $this->edit_fields[] = $field['id'];
265 }
266 }
267 }
268
269 /**
270 * Determines if a field is readable based on it's show_in_rest value
271 * and the box's show_in_rest value.
272 *
273 * @since 2.2.3
274 *
275 * @param bool $show_in_rest Field's show_in_rest value. Default null.
276 *
277 * @return bool Whether field is readable.
278 */
279 protected function can_read( $show_in_rest ) {
280 // if 'null', then use default box value.
281 if ( null === $show_in_rest ) {
282 return $this->rest_read;
283 }
284
285 // Else check if the value represents readable.
286 return self::is_readable( $show_in_rest );
287 }
288
289 /**
290 * Determines if a field is editable based on it's show_in_rest value
291 * and the box's show_in_rest value.
292 *
293 * @since 2.2.3
294 *
295 * @param bool $show_in_rest Field's show_in_rest value. Default null.
296 *
297 * @return bool Whether field is editable.
298 */
299 protected function can_edit( $show_in_rest ) {
300 // if 'null', then use default box value.
301 if ( null === $show_in_rest ) {
302 return $this->rest_edit;
303 }
304
305 // Else check if the value represents editable.
306 return self::is_editable( $show_in_rest );
307 }
308
309 /**
310 * Handler for getting post custom field data.
311 *
312 * @since 2.2.3
313 *
314 * @param array $object The object data from the response
315 * @param string $field_name Name of field
316 * @param WP_REST_Request $request Current request
317 * @param string $object_type The request object type
318 *
319 * @return mixed
320 */
321 public static function get_post_rest_values( $object, $field_name, $request, $object_type ) {
322 if ( 'cmb2' === $field_name ) {
323 return self::get_rest_values( $object, $request, $object_type, 'post' );
324 }
325 }
326
327 /**
328 * Handler for getting user custom field data.
329 *
330 * @since 2.2.3
331 *
332 * @param array $object The object data from the response
333 * @param string $field_name Name of field
334 * @param WP_REST_Request $request Current request
335 * @param string $object_type The request object type
336 *
337 * @return mixed
338 */
339 public static function get_user_rest_values( $object, $field_name, $request, $object_type ) {
340 if ( 'cmb2' === $field_name ) {
341 return self::get_rest_values( $object, $request, $object_type, 'user' );
342 }
343 }
344
345 /**
346 * Handler for getting comment custom field data.
347 *
348 * @since 2.2.3
349 *
350 * @param array $object The object data from the response
351 * @param string $field_name Name of field
352 * @param WP_REST_Request $request Current request
353 * @param string $object_type The request object type
354 *
355 * @return mixed
356 */
357 public static function get_comment_rest_values( $object, $field_name, $request, $object_type ) {
358 if ( 'cmb2' === $field_name ) {
359 return self::get_rest_values( $object, $request, $object_type, 'comment' );
360 }
361 }
362
363 /**
364 * Handler for getting term custom field data.
365 *
366 * @since 2.2.3
367 *
368 * @param array $object The object data from the response
369 * @param string $field_name Name of field
370 * @param WP_REST_Request $request Current request
371 * @param string $object_type The request object type
372 *
373 * @return mixed
374 */
375 public static function get_term_rest_values( $object, $field_name, $request, $object_type ) {
376 if ( 'cmb2' === $field_name ) {
377 return self::get_rest_values( $object, $request, $object_type, 'term' );
378 }
379 }
380
381 /**
382 * Handler for getting custom field data.
383 *
384 * @since 2.2.3
385 *
386 * @param array $object The object data from the response
387 * @param WP_REST_Request $request Current request
388 * @param string $object_type The request object type
389 * @param string $main_object_type The cmb main object type
390 *
391 * @return mixed
392 */
393 protected static function get_rest_values( $object, $request, $object_type, $main_object_type = 'post' ) {
394 if ( ! isset( $object['id'] ) ) {
395 return;
396 }
397
398 $values = array();
399
400 if ( ! empty( self::$type_boxes[ $main_object_type ] ) ) {
401 foreach ( self::$type_boxes[ $main_object_type ] as $cmb_id ) {
402 $rest_box = self::$boxes[ $cmb_id ];
403
404 foreach ( $rest_box->read_fields as $field_id ) {
405 $rest_box->cmb->object_id( $object['id'] );
406 $rest_box->cmb->object_type( $main_object_type );
407
408 $field = $rest_box->cmb->get_field( $field_id );
409
410 $field->object_id( $object['id'] );
411 $field->object_type( $main_object_type );
412
413 $values[ $cmb_id ][ $field->id( true ) ] = $field->get_data();
414 }
415 }
416 }
417
418 return $values;
419 }
420
421 /**
422 * Handler for updating post custom field data.
423 *
424 * @since 2.2.3
425 *
426 * @param mixed $values The value of the field
427 * @param object $object The object from the response
428 * @param string $field_name Name of field
429 * @param WP_REST_Request $request Current request
430 * @param string $object_type The request object type
431 *
432 * @return bool|int
433 */
434 public static function update_post_rest_values( $values, $object, $field_name, $request, $object_type ) {
435 if ( 'cmb2' === $field_name ) {
436 return self::update_rest_values( $values, $object, $request, $object_type, 'post' );
437 }
438 }
439
440 /**
441 * Handler for updating user custom field data.
442 *
443 * @since 2.2.3
444 *
445 * @param mixed $values The value of the field
446 * @param object $object The object from the response
447 * @param string $field_name Name of field
448 * @param WP_REST_Request $request Current request
449 * @param string $object_type The request object type
450 *
451 * @return bool|int
452 */
453 public static function update_user_rest_values( $values, $object, $field_name, $request, $object_type ) {
454 if ( 'cmb2' === $field_name ) {
455 return self::update_rest_values( $values, $object, $request, $object_type, 'user' );
456 }
457 }
458
459 /**
460 * Handler for updating comment custom field data.
461 *
462 * @since 2.2.3
463 *
464 * @param mixed $values The value of the field
465 * @param object $object The object from the response
466 * @param string $field_name Name of field
467 * @param WP_REST_Request $request Current request
468 * @param string $object_type The request object type
469 *
470 * @return bool|int
471 */
472 public static function update_comment_rest_values( $values, $object, $field_name, $request, $object_type ) {
473 if ( 'cmb2' === $field_name ) {
474 return self::update_rest_values( $values, $object, $request, $object_type, 'comment' );
475 }
476 }
477
478 /**
479 * Handler for updating term custom field data.
480 *
481 * @since 2.2.3
482 *
483 * @param mixed $values The value of the field
484 * @param object $object The object from the response
485 * @param string $field_name Name of field
486 * @param WP_REST_Request $request Current request
487 * @param string $object_type The request object type
488 *
489 * @return bool|int
490 */
491 public static function update_term_rest_values( $values, $object, $field_name, $request, $object_type ) {
492 if ( 'cmb2' === $field_name ) {
493 return self::update_rest_values( $values, $object, $request, $object_type, 'term' );
494 }
495 }
496
497 /**
498 * Handler for updating custom field data.
499 *
500 * @since 2.2.3
501 *
502 * @param mixed $values The value of the field
503 * @param object $object The object from the response
504 * @param WP_REST_Request $request Current request
505 * @param string $object_type The request object type
506 * @param string $main_object_type The cmb main object type
507 *
508 * @return bool|int
509 */
510 protected static function update_rest_values( $values, $object, $request, $object_type, $main_object_type = 'post' ) {
511 if ( empty( $values ) || ! is_array( $values ) ) {
512 return;
513 }
514
515 $object_id = self::get_object_id( $object, $main_object_type );
516
517 if ( ! $object_id ) {
518 return;
519 }
520
521 $updated = array();
522
523 if ( ! empty( self::$type_boxes[ $main_object_type ] ) ) {
524 foreach ( self::$type_boxes[ $main_object_type ] as $cmb_id ) {
525 $rest_box = self::$boxes[ $cmb_id ];
526
527 if ( ! array_key_exists( $cmb_id, $values ) ) {
528 continue;
529 }
530
531 $rest_box->cmb->object_id( $object_id );
532 $rest_box->cmb->object_type( $main_object_type );
533
534 $updated[ $cmb_id ] = $rest_box->sanitize_box_values( $values );
535 }
536 }
537
538 return $updated;
539 }
540
541 /**
542 * Loop through box fields and sanitize the values.
543 *
544 * @since 2.2.o
545 *
546 * @param array $values Array of values being provided.
547 * @return array Array of updated/sanitized values.
548 */
549 public function sanitize_box_values( array $values ) {
550 $updated = array();
551
552 $this->cmb->pre_process();
553
554 foreach ( $this->edit_fields as $field_id ) {
555 $updated[ $field_id ] = $this->sanitize_field_value( $values, $field_id );
556 }
557
558 $this->cmb->after_save();
559
560 return $updated;
561 }
562
563 /**
564 * Handles returning a sanitized field value.
565 *
566 * @since 2.2.3
567 *
568 * @param array $values Array of values being provided.
569 * @param string $field_id The id of the field to update.
570 *
571 * @return mixed The results of saving/sanitizing a field value.
572 */
573 protected function sanitize_field_value( array $values, $field_id ) {
574 if ( ! array_key_exists( $field_id, $values[ $this->cmb->cmb_id ] ) ) {
575 return;
576 }
577
578 $field = $this->cmb->get_field( $field_id );
579
580 if ( 'title' == $field->type() ) {
581 return;
582 }
583
584 $field->object_id( $this->cmb->object_id() );
585 $field->object_type( $this->cmb->object_type() );
586
587 if ( 'group' == $field->type() ) {
588 return $this->sanitize_group_value( $values, $field );
589 }
590
591 return $field->save_field( $values[ $this->cmb->cmb_id ][ $field_id ] );
592 }
593
594 /**
595 * Handles returning a sanitized group field value.
596 *
597 * @since 2.2.3
598 *
599 * @param array $values Array of values being provided.
600 * @param CMB2_Field $field CMB2_Field object.
601 *
602 * @return mixed The results of saving/sanitizing the group field value.
603 */
604 protected function sanitize_group_value( array $values, CMB2_Field $field ) {
605 $fields = $field->fields();
606 if ( empty( $fields ) ) {
607 return;
608 }
609
610 $this->cmb->data_to_save[ $field->_id() ] = $values[ $this->cmb->cmb_id ][ $field->_id() ];
611
612 return $this->cmb->save_group_field( $field );
613 }
614
615 /**
616 * Filter whether a meta key is protected.
617 *
618 * @since 2.2.3
619 *
620 * @param bool $protected Whether the key is protected. Default false.
621 * @param string $meta_key Meta key.
622 * @param string $meta_type Meta type.
623 */
624 public function is_protected_meta( $protected, $meta_key, $meta_type ) {
625 if ( $this->field_can_edit( $meta_key ) ) {
626 return false;
627 }
628
629 return $protected;
630 }
631
632 protected static function get_object_id( $object, $object_type = 'post' ) {
633 switch ( $object_type ) {
634 case 'user':
635 case 'post':
636 if ( isset( $object->ID ) ) {
637 return intval( $object->ID );
638 }
639 case 'comment':
640 if ( isset( $object->comment_ID ) ) {
641 return intval( $object->comment_ID );
642 }
643 case 'term':
644 if ( is_array( $object ) && isset( $object['term_id'] ) ) {
645 return intval( $object['term_id'] );
646 } elseif ( isset( $object->term_id ) ) {
647 return intval( $object->term_id );
648 }
649 }
650
651 return 0;
652 }
653
654 /**
655 * Checks if a given field can be read.
656 *
657 * @since 2.2.3
658 *
659 * @param string|CMB2_Field $field_id Field ID or CMB2_Field object.
660 * @param boolean $return_object Whether to return the Field object.
661 *
662 * @return mixed False if field can't be read or true|CMB2_Field object.
663 */
664 public function field_can_read( $field_id, $return_object = false ) {
665 return $this->field_can( 'read_fields', $field_id, $return_object );
666 }
667
668 /**
669 * Checks if a given field can be edited.
670 *
671 * @since 2.2.3
672 *
673 * @param string|CMB2_Field $field_id Field ID or CMB2_Field object.
674 * @param boolean $return_object Whether to return the Field object.
675 *
676 * @return mixed False if field can't be edited or true|CMB2_Field object.
677 */
678 public function field_can_edit( $field_id, $return_object = false ) {
679 return $this->field_can( 'edit_fields', $field_id, $return_object );
680 }
681
682 /**
683 * Checks if a given field can be read or edited.
684 *
685 * @since 2.2.3
686 *
687 * @param string $type Whether we are checking for read or edit fields.
688 * @param string|CMB2_Field $field_id Field ID or CMB2_Field object.
689 * @param boolean $return_object Whether to return the Field object.
690 *
691 * @return mixed False if field can't be read or edited or true|CMB2_Field object.
692 */
693 protected function field_can( $type = 'read_fields', $field_id, $return_object = false ) {
694 if ( ! in_array( $field_id instanceof CMB2_Field ? $field_id->id() : $field_id, $this->{$type}, true ) ) {
695 return false;
696 }
697
698 return $return_object ? $this->cmb->get_field( $field_id ) : true;
699 }
700
701 /**
702 * Get a CMB2_REST instance object from the registry by a CMB2 id.
703 *
704 * @since 2.2.3
705 *
706 * @param string $cmb_id CMB2 config id
707 *
708 * @return CMB2_REST|false The CMB2_REST object or false.
709 */
710 public static function get_rest_box( $cmb_id ) {
711 return isset( self::$boxes[ $cmb_id ] ) ? self::$boxes[ $cmb_id ] : false;
712 }
713
714 /**
715 * Remove a CMB2_REST instance object from the registry.
716 *
717 * @since 2.2.3
718 *
719 * @param string $cmb_id A CMB2 instance id.
720 */
721 public static function remove( $cmb_id ) {
722 if ( array_key_exists( $cmb_id, self::$boxes ) ) {
723 unset( self::$boxes[ $cmb_id ] );
724 }
725 }
726
727 /**
728 * Retrieve all CMB2_REST instances from the registry.
729 *
730 * @since 2.2.3
731 * @return CMB2[] Array of all registered CMB2_REST instances.
732 */
733 public static function get_all() {
734 return self::$boxes;
735 }
736
737 /**
738 * Checks if given value is readable.
739 *
740 * Value is considered readable if it is not empty and if it does not match the editable blacklist.
741 *
742 * @since 2.2.3
743 *
744 * @param mixed $value Value to check.
745 *
746 * @return boolean Whether value is considered readable.
747 */
748 public static function is_readable( $value ) {
749 return ! empty( $value ) && ! in_array( $value, array(
750 WP_REST_Server::CREATABLE,
751 WP_REST_Server::EDITABLE,
752 WP_REST_Server::DELETABLE,
753 ), true );
754 }
755
756 /**
757 * Checks if given value is editable.
758 *
759 * Value is considered editable if matches the editable whitelist.
760 *
761 * @since 2.2.3
762 *
763 * @param mixed $value Value to check.
764 *
765 * @return boolean Whether value is considered editable.
766 */
767 public static function is_editable( $value ) {
768 return in_array( $value, array(
769 WP_REST_Server::EDITABLE,
770 WP_REST_Server::ALLMETHODS,
771 ), true );
772 }
773
774 /**
775 * Magic getter for our object.
776 *
777 * @param string $field
778 * @throws Exception Throws an exception if the field is invalid.
779 *
780 * @return mixed
781 */
782 public function __get( $field ) {
783 switch ( $field ) {
784 case 'read_fields':
785 case 'edit_fields':
786 case 'rest_read':
787 case 'rest_edit':
788 return $this->{$field};
789 default:
790 throw new Exception( 'Invalid ' . __CLASS__ . ' property: ' . $field );
791 }
792 }
793
794 }
795