PluginProbe
Virtue/Ascend/Pinnacle Toolkit / 4.9.12.2
Virtue/Ascend/Pinnacle Toolkit v4.9.12.2
4.9.12.2 trunk 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3.0 3.1 3.2 3.3 3.4 3.7 All 48 releases
virtue-toolkit / cmb / includes / CMB2_Types.php

CMB2_Types.php in Virtue/Ascend/Pinnacle Toolkit 4.9.12.2, at cmb/includes/CMB2_Types.php

676 lines 20.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * CMB field type objects
4 *
5 * @since 1.0.0
6 *
7 * @category WordPress_Plugin
8 * @package CMB2
9 * @author CMB2 team
10 * @license GPL-2.0+
11 * @link https://cmb2.io
12 */
13 class CMB2_Types {
14
15 /**
16 * An iterator value for repeatable fields
17 *
18 * @var integer
19 * @since 1.0.0
20 */
21 public $iterator = 0;
22
23 /**
24 * Current CMB2_Field field object
25 *
26 * @var CMB2_Field object
27 * @since 1.0.0
28 */
29 public $field;
30
31 /**
32 * Current CMB2_Type_Base object
33 *
34 * @var CMB2_Type_Base object
35 * @since 2.2.2
36 */
37 public $type = null;
38
39 public function __construct( CMB2_Field $field ) {
40 $this->field = $field;
41 }
42
43 /**
44 * Default fallback. Allows rendering fields via "cmb2_render_$fieldtype" hook
45 *
46 * @since 1.0.0
47 * @param string $fieldtype Non-existent field type name
48 * @param array $arguments All arguments passed to the method
49 */
50 public function __call( $fieldtype, $arguments ) {
51
52 // Check for methods to be proxied to the CMB2_Type_Base object.
53 if ( $exists = $this->maybe_proxy_method( $fieldtype, $arguments ) ) {
54 return $exists['value'];
55 }
56
57 // Check for custom field type class.
58 if ( $object = $this->maybe_custom_field_object( $fieldtype, $arguments ) ) {
59 return $object->render();
60 }
61
62 /**
63 * Pass non-existent field types through an action.
64 *
65 * The dynamic portion of the hook name, $fieldtype, refers to the field type.
66 *
67 * @param array $field The passed in `CMB2_Field` object
68 * @param mixed $escaped_value The value of this field escaped.
69 * It defaults to `sanitize_text_field`.
70 * If you need the unescaped value, you can access it
71 * via `$field->value()`
72 * @param int $object_id The ID of the current object
73 * @param string $object_type The type of object you are working with.
74 * Most commonly, `post` (this applies to all post-types),
75 * but could also be `comment`, `user` or `options-page`.
76 * @param object $field_type_object This `CMB2_Types` object
77 */
78 do_action( "cmb2_render_{$fieldtype}", $this->field, $this->field->escaped_value(), $this->field->object_id, $this->field->object_type, $this );
79 }
80
81 /**
82 * Render a field (and handle repeatable)
83 *
84 * @since 1.1.0
85 */
86 public function render() {
87 if ( $this->field->args( 'repeatable' ) ) {
88 $this->render_repeatable_field();
89 } else {
90 $this->_render();
91 }
92 }
93
94 /**
95 * Render a field type
96 *
97 * @since 1.1.0
98 */
99 protected function _render() {
100 $this->field->peform_param_callback( 'before_field' );
101 echo $this->{$this->field->type()}();
102 $this->field->peform_param_callback( 'after_field' );
103 }
104
105 /**
106 * Proxies the method call to the CMB2_Type_Base object, if it exists, otherwise returns a default fallback value.
107 *
108 * @since 2.2.2
109 *
110 * @param string $method Method to call on the CMB2_Type_Base object.
111 * @param mixed $default Default fallback value if method is not found.
112 * @param array $args Optional arguments to pass to proxy method.
113 *
114 * @return mixed Results from called method.
115 */
116 protected function proxy_method( $method, $default, $args = array() ) {
117 if ( ! is_object( $this->type ) ) {
118 $this->guess_type_object( $method );
119 }
120
121 if ( is_object( $this->type ) && method_exists( $this->type, $method ) ) {
122
123 return empty( $args )
124 ? $this->type->$method()
125 : call_user_func_array( array( $this->type, $method ), $args );
126 }
127
128 return $default;
129 }
130
131 /**
132 * If no CMB2_Types::$type object is initiated when a proxy method is called, it means
133 * it's a custom field type (which SHOULD be instantiating a Type), but let's try and
134 * guess the type object for them and instantiate it.
135 *
136 * @since 2.2.3
137 *
138 * @param string $method Method attempting to be called on the CMB2_Type_Base object.
139 * @return bool
140 */
141 protected function guess_type_object( $method ) {
142 $fieldtype = $this->field->type();
143
144 // Try to "guess" the Type object based on the method requested.
145 switch ( $method ) {
146 case 'select_option':
147 case 'list_input':
148 case 'list_input_checkbox':
149 case 'concat_items':
150 $this->get_new_render_type( $fieldtype, 'CMB2_Type_Select' );
151 break;
152 case 'is_valid_img_ext':
153 case 'img_status_output':
154 case 'file_status_output':
155 $this->get_new_render_type( $fieldtype, 'CMB2_Type_File_Base' );
156 break;
157 case 'parse_picker_options':
158 $this->get_new_render_type( $fieldtype, 'CMB2_Type_Text_Date' );
159 break;
160 case 'get_object_terms':
161 case 'get_terms':
162 $this->get_new_render_type( $fieldtype, 'CMB2_Type_Taxonomy_Multicheck' );
163 break;
164 case 'date_args':
165 case 'time_args':
166 $this->get_new_render_type( $fieldtype, 'CMB2_Type_Text_Datetime_Timestamp' );
167 break;
168 case 'parse_args':
169 $this->get_new_render_type( $fieldtype, 'CMB2_Type_Text' );
170 break;
171 }
172
173 return null !== $this->type;
174 }
175
176 /**
177 * Check for methods to be proxied to the CMB2_Type_Base object.
178 *
179 * @since 2.2.4
180 * @param string $method The possible method to proxy.
181 * @param array $arguments All arguments passed to the method.
182 * @return bool|array False if not proxied, else array with 'value' key being the return of the method.
183 */
184 public function maybe_proxy_method( $method, $arguments ) {
185 $exists = false;
186
187 $proxied = array(
188 'get_object_terms' => array(),
189 'is_valid_img_ext' => false,
190 'parse_args' => array(),
191 'concat_items' => '',
192 'select_option' => '',
193 'list_input' => '',
194 'list_input_checkbox' => '',
195 'img_status_output' => '',
196 'file_status_output' => '',
197 'parse_picker_options' => array(),
198 );
199 if ( isset( $proxied[ $method ] ) ) {
200 $exists = array(
201 // Ok, proxy the method call to the CMB2_Type_Base object.
202 'value' => $this->proxy_method( $method, $proxied[ $method ], $arguments ),
203 );
204 }
205
206 return $exists;
207 }
208
209 /**
210 * Checks for a custom field CMB2_Type_Base class to use for rendering.
211 *
212 * @since 2.2.4
213 *
214 * @param string $fieldtype Non-existent field type name.
215 * @param array $args Optional field arguments.
216 *
217 * @return bool|CMB2_Type_Base Type object if custom field is an object, false if field was added with
218 * `cmb2_render_{$field_type}` action.
219 * @throws Exception if custom field type class does not extend CMB2_Type_Base.
220 */
221 public function maybe_custom_field_object( $fieldtype, $args = array() ) {
222 if ( $render_class_name = $this->get_render_type_class( $fieldtype ) ) {
223 $this->type = new $render_class_name( $this, $args );
224
225 if ( ! ( $this->type instanceof CMB2_Type_Base ) ) {
226 throw new Exception( __( 'Custom CMB2 field type classes must extend CMB2_Type_Base.', 'cmb2' ) );
227 }
228
229 return $this->type;
230 }
231
232 return false;
233 }
234
235 /**
236 * Gets the render type CMB2_Type_Base object to use for rendering the field.
237 *
238 * @since 2.2.4
239 * @param string $fieldtype The type of field being rendered.
240 * @param string $render_class_name The default field type class to use. Defaults to null.
241 * @param array $args Optional arguments to pass to type class.
242 * @param mixed $additional Optional additional argument to pass to type class.
243 * @return CMB2_Type_Base Type object.
244 */
245 public function get_new_render_type( $fieldtype, $render_class_name = null, $args = array(), $additional = '' ) {
246 $render_class_name = $this->get_render_type_class( $fieldtype, $render_class_name );
247 $this->type = new $render_class_name( $this, $args, $additional );
248
249 return $this->type;
250 }
251
252 /**
253 * Checks for the render type class to use for rendering the field.
254 *
255 * @since 2.2.4
256 * @param string $fieldtype The type of field being rendered.
257 * @param string $render_class_name The default field type class to use. Defaults to null.
258 * @return string The field type class to use.
259 */
260 public function get_render_type_class( $fieldtype, $render_class_name = null ) {
261 $render_class_name = $this->field->args( 'render_class' ) ? $this->field->args( 'render_class' ) : $render_class_name;
262
263 if ( has_action( "cmb2_render_class_{$fieldtype}" ) ) {
264
265 /**
266 * Filters the custom field type class used for rendering the field. Class is required to extend CMB2_Type_Base.
267 *
268 * The dynamic portion of the hook name, $fieldtype, refers to the (custom) field type.
269 *
270 * @since 2.2.4
271 *
272 * @param string $render_class_name The custom field type class to use. Default null.
273 * @param object $field_type_object This `CMB2_Types` object.
274 */
275 $render_class_name = apply_filters( "cmb2_render_class_{$fieldtype}", $render_class_name, $this );
276 }
277
278 return $render_class_name && class_exists( $render_class_name ) ? $render_class_name : false;
279 }
280
281 /**
282 * Retrieve text parameter from field's options array (if it has one), or use fallback text
283 *
284 * @since 2.0.0
285 * @param string $text_key Key in field's options array.
286 * @param string $fallback Fallback text.
287 * @return string
288 */
289 public function _text( $text_key, $fallback = '' ) {
290 return $this->field->get_string( $text_key, $fallback );
291 }
292
293 /**
294 * Determine a file's extension
295 *
296 * @since 1.0.0
297 * @param string $file File url
298 * @return string|false File extension or false
299 */
300 public function get_file_ext( $file ) {
301 return CMB2_Utils::get_file_ext( $file );
302 }
303
304 /**
305 * Get the file name from a url
306 *
307 * @since 2.0.0
308 * @param string $value File url or path
309 * @return string File name
310 */
311 public function get_file_name_from_path( $value ) {
312 return CMB2_Utils::get_file_name_from_path( $value );
313 }
314
315 /**
316 * Combines attributes into a string for a form element
317 *
318 * @since 1.1.0
319 * @param array $attrs Attributes to concatenate
320 * @param array $attr_exclude Attributes that should NOT be concatenated
321 * @return string String of attributes for form element
322 */
323 public function concat_attrs( $attrs, $attr_exclude = array() ) {
324 return CMB2_Utils::concat_attrs( $attrs, $attr_exclude );
325 }
326
327 /**
328 * Generates repeatable field table markup
329 *
330 * @since 1.0.0
331 */
332 public function render_repeatable_field() {
333 $table_id = $this->field->id() . '_repeat';
334
335 $this->_desc( true, true, true );
336 ?>
337
338 <div id="<?php echo $table_id; ?>" class="cmb-repeat-table cmb-nested">
339 <div class="cmb-tbody cmb-field-list">
340 <?php $this->repeatable_rows(); ?>
341 </div>
342 </div>
343 <p class="cmb-add-row">
344 <button type="button" data-selector="<?php echo $table_id; ?>" class="cmb-add-row-button button-secondary"><?php echo esc_html( $this->_text( 'add_row_text', esc_html__( 'Add Row', 'cmb2' ) ) ); ?></button>
345 </p>
346
347 <?php
348 // reset iterator
349 $this->iterator = 0;
350 }
351
352 /**
353 * Generates repeatable field rows
354 *
355 * @since 1.1.0
356 */
357 public function repeatable_rows() {
358 $meta_value = array_filter( (array) $this->field->escaped_value() );
359 // check for default content
360 $default = $this->field->get_default();
361
362 // check for saved data
363 if ( ! empty( $meta_value ) ) {
364 $meta_value = is_array( $meta_value ) ? array_filter( $meta_value ) : $meta_value;
365 $meta_value = ! empty( $meta_value ) ? $meta_value : $default;
366 } else {
367 $meta_value = $default;
368 }
369
370 // Loop value array and add a row
371 if ( ! empty( $meta_value ) ) {
372 foreach ( (array) $meta_value as $val ) {
373 $this->field->escaped_value = $val;
374 $this->repeat_row();
375 $this->iterator++;
376 }
377 } else {
378
379 // If value is empty (including empty array), then clear the value.
380 $this->field->escaped_value = $this->field->value = null;
381
382 // Otherwise add one row
383 $this->repeat_row();
384 }
385
386 // Then add an empty row
387 $this->field->escaped_value = $default;
388 $this->iterator = $this->iterator ? $this->iterator : 1;
389 $this->repeat_row( 'empty-row hidden' );
390 }
391
392 /**
393 * Generates a repeatable row's markup
394 *
395 * @since 1.1.0
396 * @param string $classes Repeatable table row's class
397 */
398 protected function repeat_row( $classes = 'cmb-repeat-row' ) {
399 $classes = explode( ' ', $classes );
400 $classes = array_map( 'sanitize_html_class', $classes );
401 ?>
402
403 <div class="cmb-row <?php echo esc_attr( implode( ' ', $classes ) ); ?>">
404 <div class="cmb-td">
405 <?php $this->_render(); ?>
406 </div>
407 <div class="cmb-td cmb-remove-row">
408 <button type="button" class="button-secondary cmb-remove-row-button" title="<?php echo esc_attr( $this->_text( 'remove_row_button_title', esc_html__( 'Remove Row', 'cmb2' ) ) ); ?>"><?php echo esc_html( $this->_text( 'remove_row_text', esc_html__( 'Remove', 'cmb2' ) ) ); ?></button>
409 </div>
410 </div>
411
412 <?php
413 }
414
415 /**
416 * Generates description markup.
417 *
418 * @since 1.0.0
419 * @param bool $paragraph Paragraph tag or span.
420 * @param bool $echo Whether to echo description or only return it.
421 * @param bool $repeat_group Whether to repeat the group.
422 * @return string Field's description markup.
423 */
424 public function _desc( $paragraph = false, $echo = false, $repeat_group = false ) {
425 // Prevent description from printing multiple times for repeatable fields
426 if ( ! $repeat_group && ( $this->field->args( 'repeatable' ) || $this->iterator > 0 ) ) {
427 return '';
428 }
429
430 $desc = $this->field->args( 'description' );
431
432 if ( ! $desc ) {
433 return;
434 }
435
436 $tag = $paragraph ? 'p' : 'span';
437 $desc = sprintf( "\n" . '<%1$s class="cmb2-metabox-description">%2$s</%1$s>' . "\n", $tag, $desc );
438
439 if ( $echo ) {
440 echo $desc;
441 }
442
443 return $desc;
444 }
445
446 /**
447 * Generate field name attribute
448 *
449 * @since 1.1.0
450 * @param string $suffix For multi-part fields
451 * @return string Name attribute
452 */
453 public function _name( $suffix = '' ) {
454 return $this->field->args( '_name' ) . ( $this->field->args( 'repeatable' ) ? '[' . $this->iterator . ']' : '' ) . $suffix;
455 }
456
457 /**
458 * Generate field id attribute
459 *
460 * @since 1.1.0
461 * @param string $suffix For multi-part fields
462 * @param bool $append_repeatable_iterator Whether to append the iterator attribue if the field is repeatable.
463 * @return string Id attribute
464 */
465 public function _id( $suffix = '', $append_repeatable_iterator = true ) {
466 $id = $this->field->id() . $suffix . ( $this->field->args( 'repeatable' ) ? '_' . $this->iterator : '' );
467
468 if ( $append_repeatable_iterator && $this->field->args( 'repeatable' ) ) {
469 $id .= '" data-iterator="' . $this->iterator;
470 }
471
472 return $id;
473 }
474
475 /**
476 * Handles outputting an 'input' element
477 *
478 * @since 1.1.0
479 * @param array $args Override arguments
480 * @param string $type Field type
481 * @return string Form input element
482 */
483 public function input( $args = array(), $type = __FUNCTION__ ) {
484 return $this->get_new_render_type( 'text', 'CMB2_Type_Text', $args, $type )->render();
485 }
486
487 /**
488 * Handles outputting an 'textarea' element
489 *
490 * @since 1.1.0
491 * @param array $args Override arguments
492 * @return string Form textarea element
493 */
494 public function textarea( $args = array() ) {
495 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Textarea', $args )->render();
496 }
497
498 /**
499 * Begin Field Types
500 */
501
502 public function text() {
503 return $this->input();
504 }
505
506 public function hidden() {
507 $args = array(
508 'type' => 'hidden',
509 'desc' => '',
510 'class' => 'cmb2-hidden',
511 );
512 if ( $this->field->group ) {
513 $args['data-groupid'] = $this->field->group->id();
514 $args['data-iterator'] = $this->iterator;
515 }
516
517 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Text', $args, 'input' )->render();
518 }
519
520 public function text_small() {
521 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Text', array(
522 'class' => 'cmb2-text-small',
523 'desc' => $this->_desc(),
524 ), 'input' )->render();
525 }
526
527 public function text_medium() {
528 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Text', array(
529 'class' => 'cmb2-text-medium',
530 'desc' => $this->_desc(),
531 ), 'input' )->render();
532 }
533
534 public function text_email() {
535 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Text', array(
536 'class' => 'cmb2-text-email cmb2-text-medium',
537 'type' => 'email',
538 ), 'input' )->render();
539 }
540
541 public function text_url() {
542 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Text', array(
543 'class' => 'cmb2-text-url cmb2-text-medium regular-text',
544 'value' => $this->field->escaped_value( 'esc_url' ),
545 ), 'input' )->render();
546 }
547
548 public function text_money() {
549 $input = $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Text', array(
550 'class' => 'cmb2-text-money',
551 'desc' => $this->_desc(),
552 ), 'input' )->render();
553 return ( ! $this->field->get_param_callback_result( 'before_field' ) ? '$ ' : ' ' ) . $input;
554 }
555
556 public function textarea_small() {
557 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Textarea', array(
558 'class' => 'cmb2-textarea-small',
559 'rows' => 4,
560 ) )->render();
561 }
562
563 public function textarea_code( $args = array() ) {
564 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Textarea_Code', $args )->render();
565 }
566
567 public function wysiwyg( $args = array() ) {
568 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Wysiwyg', $args )->render();
569 }
570
571 public function text_date( $args = array() ) {
572 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Text_Date', $args )->render();
573 }
574
575 // Alias for text_date
576 public function text_date_timestamp( $args = array() ) {
577 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Text_Date', $args )->render();
578 }
579
580 public function text_time( $args = array() ) {
581 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Text_Time', $args )->render();
582 }
583
584 public function text_datetime_timestamp( $args = array() ) {
585 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Text_Datetime_Timestamp', $args )->render();
586 }
587
588 public function text_datetime_timestamp_timezone( $args = array() ) {
589 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Text_Datetime_Timestamp_Timezone', $args )->render();
590 }
591
592 public function select_timezone( $args = array() ) {
593 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Select_Timezone', $args )->render();
594 }
595
596 public function colorpicker( $args = array(), $meta_value = '' ) {
597 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Colorpicker', $args, $meta_value )->render();
598 }
599
600 public function title( $args = array() ) {
601 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Title', $args )->render();
602 }
603
604 public function select( $args = array() ) {
605 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Select', $args )->render();
606 }
607
608 public function taxonomy_select( $args = array() ) {
609 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Taxonomy_Select', $args )->render();
610 }
611
612 public function taxonomy_select_hierarchical( $args = array() ) {
613 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Taxonomy_Select_Hierarchical', $args )->render();
614 }
615
616 public function radio( $args = array(), $type = __FUNCTION__ ) {
617 return $this->get_new_render_type( $type, 'CMB2_Type_Radio', $args, $type )->render();
618 }
619
620 public function radio_inline( $args = array() ) {
621 return $this->radio( $args, __FUNCTION__ );
622 }
623
624 public function multicheck( $type = 'checkbox' ) {
625 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Multicheck', array(), $type )->render();
626 }
627
628 public function multicheck_inline() {
629 return $this->multicheck( 'multicheck_inline' );
630 }
631
632 public function checkbox( $args = array(), $is_checked = null ) {
633 // Avoid get_new_render_type since we need a different default for the 3rd argument than ''.
634 $render_class_name = $this->get_render_type_class( __FUNCTION__, 'CMB2_Type_Checkbox' );
635 $this->type = new $render_class_name( $this, $args, $is_checked );
636 return $this->type->render();
637 }
638
639 public function taxonomy_radio( $args = array() ) {
640 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Taxonomy_Radio', $args )->render();
641 }
642
643 public function taxonomy_radio_hierarchical( $args = array() ) {
644 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Taxonomy_Radio_Hierarchical', $args )->render();
645 }
646
647 public function taxonomy_radio_inline( $args = array() ) {
648 return $this->taxonomy_radio( $args );
649 }
650
651 public function taxonomy_multicheck( $args = array() ) {
652 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Taxonomy_Multicheck', $args )->render();
653 }
654
655 public function taxonomy_multicheck_hierarchical( $args = array() ) {
656 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Taxonomy_Multicheck_Hierarchical', $args )->render();
657 }
658
659 public function taxonomy_multicheck_inline( $args = array() ) {
660 return $this->taxonomy_multicheck( $args );
661 }
662
663 public function oembed( $args = array() ) {
664 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_Oembed', $args )->render();
665 }
666
667 public function file_list( $args = array() ) {
668 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_File_List', $args )->render();
669 }
670
671 public function file( $args = array() ) {
672 return $this->get_new_render_type( __FUNCTION__, 'CMB2_Type_File', $args )->render();
673 }
674
675 }
676