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

CMB2_Sanitize.php in Cool Flipbox – Shortcode & Gutenberg Block 1.4, at CMB2/includes/CMB2_Sanitize.php

588 lines 15.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * CMB2 field sanitization
4 *
5 * @since 0.0.4
6 *
7 * @category WordPress_Plugin
8 * @package CMB2
9 * @author CMB2 team
10 * @license GPL-2.0+
11 * @link https://cmb2.io
12 *
13 * @method string _id()
14 */
15 class CMB2_Sanitize {
16
17 /**
18 * A CMB field object
19 *
20 * @var CMB2_Field object
21 */
22 public $field;
23
24 /**
25 * Field's value
26 *
27 * @var mixed
28 */
29 public $value;
30
31 /**
32 * Setup our class vars
33 *
34 * @since 1.1.0
35 * @param CMB2_Field $field A CMB2 field object.
36 * @param mixed $value Field value.
37 */
38 public function __construct( CMB2_Field $field, $value ) {
39 $this->field = $field;
40 $this->value = $value;
41 }
42
43 /**
44 * Catchall method if field's 'sanitization_cb' is NOT defined,
45 * or field type does not have a corresponding validation method.
46 *
47 * @since 1.0.0
48 *
49 * @param string $name Non-existent method name.
50 * @param array $arguments All arguments passed to the method.
51 * @return mixed
52 */
53 public function __call( $name, $arguments ) {
54 return $this->default_sanitization();
55 }
56
57 /**
58 * Default fallback sanitization method. Applies filters.
59 *
60 * @since 1.0.2
61 */
62 public function default_sanitization() {
63 $field_type = $this->field->type();
64
65 /**
66 * This exists for back-compatibility, but validation
67 * is not what happens here.
68 *
69 * @deprecated See documentation for "cmb2_sanitize_{$field_type}".
70 */
71 if ( function_exists( 'apply_filters_deprecated' ) ) {
72 $override_value = apply_filters_deprecated( "cmb2_validate_{$field_type}", array( null, $this->value, $this->field->object_id, $this->field->args(), $this ), '2.0.0', "cmb2_sanitize_{$field_type}" );
73 } else {
74 $override_value = apply_filters( "cmb2_validate_{$field_type}", null, $this->value, $this->field->object_id, $this->field->args(), $this );
75 }
76
77 if ( null !== $override_value ) {
78 return $override_value;
79 }
80
81 $sanitized_value = '';
82 switch ( $field_type ) {
83 case 'wysiwyg':
84 case 'textarea_small':
85 case 'oembed':
86 $sanitized_value = $this->textarea();
87 break;
88 case 'taxonomy_select':
89 case 'taxonomy_radio':
90 case 'taxonomy_radio_inline':
91 case 'taxonomy_radio_hierarchical':
92 case 'taxonomy_multicheck':
93 case 'taxonomy_multicheck_hierarchical':
94 case 'taxonomy_multicheck_inline':
95 $sanitized_value = $this->taxonomy();
96 break;
97 case 'multicheck':
98 case 'multicheck_inline':
99 case 'file_list':
100 case 'group':
101 // no filtering
102 $sanitized_value = $this->value;
103 break;
104 default:
105 // Handle repeatable fields array
106 // We'll fallback to 'sanitize_text_field'
107 $sanitized_value = $this->_default_sanitization();
108 break;
109 }
110
111 return $this->_is_empty_array( $sanitized_value ) ? '' : $sanitized_value;
112 }
113
114 /**
115 * Default sanitization method, sanitize_text_field. Checks if value is array.
116 *
117 * @since 2.2.4
118 * @return mixed Sanitized value.
119 */
120 protected function _default_sanitization() {
121 // Handle repeatable fields array.
122 return is_array( $this->value ) ? array_map( 'sanitize_text_field', $this->value ) : sanitize_text_field( $this->value );
123 }
124
125 /**
126 * Sets the object terms to the object (if not options-page) and optionally returns the sanitized term values.
127 *
128 * @since 2.2.4
129 * @return mixed Blank value, or sanitized term values if "cmb2_return_taxonomy_values_{$cmb_id}" is true.
130 */
131 public function taxonomy() {
132 $sanitized_value = '';
133
134 if ( ! $this->field->args( 'taxonomy' ) ) {
135 CMB2_Utils::log_if_debug( __METHOD__, __LINE__, "{$this->field->type()} {$this->field->_id()} is missing the 'taxonomy' parameter." );
136 } else {
137
138 if ( in_array( $this->field->object_type, array( 'options-page', 'term' ), true ) ) {
139 $return_values = true;
140 } else {
141 wp_set_object_terms( $this->field->object_id, $this->value, $this->field->args( 'taxonomy' ) );
142 $return_values = false;
143 }
144
145 $cmb_id = $this->field->cmb_id;
146
147 /**
148 * Filter whether 'taxonomy_*' fields should return their value when being sanitized.
149 *
150 * By default, these fields do not return a value as we do not want them stored to meta
151 * (as they are stored as terms). This allows overriding that and is used by CMB2::get_sanitized_values().
152 *
153 * The dynamic portion of the hook, $cmb_id, refers to the this field's CMB2 box id.
154 *
155 * @since 2.2.4
156 *
157 * @param bool $return_values By default, this is only true for 'options-page' boxes. To enable:
158 * `add_filter( "cmb2_return_taxonomy_values_{$cmb_id}", '__return_true' );`
159 * @param CMB2_Sanitize $sanitizer This object.
160 */
161 if ( apply_filters( "cmb2_return_taxonomy_values_{$cmb_id}", $return_values, $this ) ) {
162 $sanitized_value = $this->_default_sanitization();
163 }
164 }
165
166 return $sanitized_value;
167 }
168
169 /**
170 * Simple checkbox validation
171 *
172 * @since 1.0.1
173 * @return string|false 'on' or false
174 */
175 public function checkbox() {
176 return $this->value === 'on' ? 'on' : false;
177 }
178
179 /**
180 * Validate url in a meta value.
181 *
182 * @since 1.0.1
183 * @return string Empty string or escaped url
184 */
185 public function text_url() {
186 $protocols = $this->field->args( 'protocols' );
187 // for repeatable.
188 if ( is_array( $this->value ) ) {
189 foreach ( $this->value as $key => $val ) {
190 $this->value[ $key ] = $val ? esc_url_raw( $val, $protocols ) : $this->field->get_default();
191 }
192 } else {
193 $this->value = $this->value ? esc_url_raw( $this->value, $protocols ) : $this->field->get_default();
194 }
195
196 return $this->value;
197 }
198
199 public function colorpicker() {
200 // for repeatable.
201 if ( is_array( $this->value ) ) {
202 $check = $this->value;
203 $this->value = array();
204 foreach ( $check as $key => $val ) {
205 if ( $val && '#' != $val ) {
206 $this->value[ $key ] = esc_attr( $val );
207 }
208 }
209 } else {
210 $this->value = ! $this->value || '#' == $this->value ? '' : esc_attr( $this->value );
211 }
212 return $this->value;
213 }
214
215 /**
216 * Validate email in a meta value
217 *
218 * @since 1.0.1
219 * @return string Empty string or sanitized email
220 */
221 public function text_email() {
222 // for repeatable.
223 if ( is_array( $this->value ) ) {
224 foreach ( $this->value as $key => $val ) {
225 $val = trim( $val );
226 $this->value[ $key ] = is_email( $val ) ? $val : '';
227 }
228 } else {
229 $this->value = trim( $this->value );
230 $this->value = is_email( $this->value ) ? $this->value : '';
231 }
232
233 return $this->value;
234 }
235
236 /**
237 * Validate money in a meta value
238 *
239 * @since 1.0.1
240 * @return string Empty string or sanitized money value
241 */
242 public function text_money() {
243 if ( ! $this->value ) {
244 return '';
245 }
246
247 global $wp_locale;
248
249 $search = array( $wp_locale->number_format['thousands_sep'], $wp_locale->number_format['decimal_point'] );
250 $replace = array( '', '.' );
251
252 // Strip slashes. Example: 2\'180.00.
253 // See https://github.com/CMB2/CMB2/issues/1014.
254 $this->value = wp_unslash( $this->value );
255
256 // for repeatable.
257 if ( is_array( $this->value ) ) {
258 foreach ( $this->value as $key => $val ) {
259 if ( $val ) {
260 $this->value[ $key ] = number_format_i18n( (float) str_ireplace( $search, $replace, $val ), 2 );
261 }
262 }
263 } else {
264 $this->value = number_format_i18n( (float) str_ireplace( $search, $replace, $this->value ), 2 );
265 }
266
267 return $this->value;
268 }
269
270 /**
271 * Converts text date to timestamp
272 *
273 * @since 1.0.2
274 * @return string Timestring
275 */
276 public function text_date_timestamp() {
277 // date_create_from_format if there is a slash in the value.
278 $this->value = wp_unslash( $this->value );
279
280 return is_array( $this->value )
281 ? array_map( array( $this->field, 'get_timestamp_from_value' ), $this->value )
282 : $this->field->get_timestamp_from_value( $this->value );
283 }
284
285 /**
286 * Datetime to timestamp
287 *
288 * @since 1.0.1
289 *
290 * @param bool $repeat Whether or not to repeat.
291 * @return string|array Timestring
292 */
293 public function text_datetime_timestamp( $repeat = false ) {
294 // date_create_from_format if there is a slash in the value.
295 $this->value = wp_unslash( $this->value );
296
297 $test = is_array( $this->value ) ? array_filter( $this->value ) : '';
298 if ( empty( $test ) ) {
299 return '';
300 }
301
302 $repeat_value = $this->_check_repeat( __FUNCTION__, $repeat );
303 if ( false !== $repeat_value ) {
304 return $repeat_value;
305 }
306
307 if ( isset( $this->value['date'], $this->value['time'] ) ) {
308 $this->value = $this->field->get_timestamp_from_value( $this->value['date'] . ' ' . $this->value['time'] );
309 }
310
311 if ( $tz_offset = $this->field->field_timezone_offset() ) {
312 $this->value += (int) $tz_offset;
313 }
314
315 return $this->value;
316 }
317
318 /**
319 * Datetime to timestamp with timezone
320 *
321 * @since 1.0.1
322 *
323 * @param bool $repeat Whether or not to repeat.
324 * @return string Timestring
325 */
326 public function text_datetime_timestamp_timezone( $repeat = false ) {
327 static $utc_values = array();
328
329 $test = is_array( $this->value ) ? array_filter( $this->value ) : '';
330 if ( empty( $test ) ) {
331 return '';
332 }
333
334 // date_create_from_format if there is a slash in the value.
335 $this->value = wp_unslash( $this->value );
336
337 $utc_key = $this->field->_id() . '_utc';
338
339 $repeat_value = $this->_check_repeat( __FUNCTION__, $repeat );
340 if ( false !== $repeat_value ) {
341 if ( ! empty( $utc_values[ $utc_key ] ) ) {
342 $this->_save_utc_value( $utc_key, $utc_values[ $utc_key ] );
343 unset( $utc_values[ $utc_key ] );
344 }
345
346 return $repeat_value;
347 }
348
349 $tzstring = null;
350
351 if ( is_array( $this->value ) && array_key_exists( 'timezone', $this->value ) ) {
352 $tzstring = $this->value['timezone'];
353 }
354
355 if ( empty( $tzstring ) ) {
356 $tzstring = CMB2_Utils::timezone_string();
357 }
358
359 $offset = CMB2_Utils::timezone_offset( $tzstring );
360
361 if ( 'UTC' === substr( $tzstring, 0, 3 ) ) {
362 $tzstring = timezone_name_from_abbr( '', $offset, 0 );
363 /**
364 * The timezone_name_from_abbr() returns false if not found based on offset.
365 * Since there are currently some invalid timezones in wp_timezone_dropdown(),
366 * fallback to an offset of 0 (UTC+0)
367 * https://core.trac.wordpress.org/ticket/29205
368 */
369 $tzstring = false !== $tzstring ? $tzstring : timezone_name_from_abbr( '', 0, 0 );
370 }
371
372 $full_format = $this->field->args['date_format'] . ' ' . $this->field->args['time_format'];
373 $full_date = $this->value['date'] . ' ' . $this->value['time'];
374
375 try {
376
377 $datetime = date_create_from_format( $full_format, $full_date );
378
379 if ( ! is_object( $datetime ) ) {
380 $this->value = $utc_stamp = '';
381 } else {
382 $datetime->setTimezone( new DateTimeZone( $tzstring ) );
383 $utc_stamp = date_timestamp_get( $datetime ) - $offset;
384 $this->value = serialize( $datetime );
385 }
386
387 if ( $this->field->group ) {
388 $this->value = array(
389 'supporting_field_value' => $utc_stamp,
390 'supporting_field_id' => $utc_key,
391 'value' => $this->value,
392 );
393 } else {
394 // Save the utc timestamp supporting field.
395 if ( $repeat ) {
396 $utc_values[ $utc_key ][] = $utc_stamp;
397 } else {
398 $this->_save_utc_value( $utc_key, $utc_stamp );
399 }
400 }
401 } catch ( Exception $e ) {
402 $this->value = '';
403 CMB2_Utils::log_if_debug( __METHOD__, __LINE__, $e->getMessage() );
404 }
405
406 return $this->value;
407 }
408
409 /**
410 * Sanitize textareas and wysiwyg fields
411 *
412 * @since 1.0.1
413 * @return string Sanitized data
414 */
415 public function textarea() {
416 return is_array( $this->value ) ? array_map( 'wp_kses_post', $this->value ) : wp_kses_post( $this->value );
417 }
418
419 /**
420 * Sanitize code textareas
421 *
422 * @since 1.0.2
423 *
424 * @param bool $repeat Whether or not to repeat.
425 * @return string Sanitized data
426 */
427 public function textarea_code( $repeat = false ) {
428 $repeat_value = $this->_check_repeat( __FUNCTION__, $repeat );
429 if ( false !== $repeat_value ) {
430 return $repeat_value;
431 }
432
433 return htmlspecialchars_decode( stripslashes( $this->value ) );
434 }
435
436 /**
437 * Handles saving of attachment post ID and sanitizing file url
438 *
439 * @since 1.1.0
440 * @return string Sanitized url
441 */
442 public function file() {
443 $file_id_key = $this->field->_id() . '_id';
444
445 if ( $this->field->group ) {
446 // Return an array with url/id if saving a group field.
447 $this->value = $this->_get_group_file_value_array( $file_id_key );
448 } else {
449 $this->_save_file_id_value( $file_id_key );
450 $this->text_url();
451 }
452
453 return $this->value;
454 }
455
456 /**
457 * Gets the values for the `file` field type from the data being saved.
458 *
459 * @since 2.2.0
460 *
461 * @param mixed $id_key ID key to use.
462 * @return array
463 */
464 public function _get_group_file_value_array( $id_key ) {
465 $alldata = $this->field->group->data_to_save;
466 $base_id = $this->field->group->_id();
467 $i = $this->field->group->index;
468
469 // Check group $alldata data.
470 $id_val = isset( $alldata[ $base_id ][ $i ][ $id_key ] )
471 ? absint( $alldata[ $base_id ][ $i ][ $id_key ] )
472 : '';
473
474 // We don't want to save 0 to the DB for file fields.
475 if ( 0 === $id_val ) {
476 $id_val = '';
477 }
478
479 return array(
480 'value' => $this->text_url(),
481 'supporting_field_value' => $id_val,
482 'supporting_field_id' => $id_key,
483 );
484 }
485
486 /**
487 * Peforms saving of `file` attachement's ID
488 *
489 * @since 1.1.0
490 *
491 * @param mixed $file_id_key ID key to use.
492 * @return mixed
493 */
494 public function _save_file_id_value( $file_id_key ) {
495 $id_field = $this->_new_supporting_field( $file_id_key );
496
497 // Check standard data_to_save data.
498 $id_val = isset( $this->field->data_to_save[ $file_id_key ] )
499 ? $this->field->data_to_save[ $file_id_key ]
500 : null;
501
502 // If there is no ID saved yet, try to get it from the url.
503 if ( $this->value && ! $id_val ) {
504 $id_val = CMB2_Utils::image_id_from_url( $this->value );
505
506 // If there is an ID but user emptied the input value, remove the ID.
507 } elseif ( ! $this->value && $id_val ) {
508 $id_val = null;
509 }
510
511 return $id_field->save_field( $id_val );
512 }
513
514 /**
515 * Peforms saving of `text_datetime_timestamp_timezone` utc timestamp
516 *
517 * @since 2.2.0
518 *
519 * @param mixed $utc_key UTC key.
520 * @param mixed $utc_stamp UTC timestamp.
521 * @return mixed
522 */
523 public function _save_utc_value( $utc_key, $utc_stamp ) {
524 return $this->_new_supporting_field( $utc_key )->save_field( $utc_stamp );
525 }
526
527 /**
528 * Returns a new, supporting, CMB2_Field object based on a new field id.
529 *
530 * @since 2.2.0
531 *
532 * @param mixed $new_field_id New field ID.
533 * @return CMB2_Field
534 */
535 public function _new_supporting_field( $new_field_id ) {
536 return $this->field->get_field_clone( array(
537 'id' => $new_field_id,
538 'sanitization_cb' => false,
539 ) );
540 }
541
542 /**
543 * If repeating, loop through and re-apply sanitization method
544 *
545 * @since 1.1.0
546 * @param string $method Class method.
547 * @param bool $repeat Whether repeating or not.
548 * @return mixed Sanitized value
549 */
550 public function _check_repeat( $method, $repeat ) {
551 if ( $repeat || ! $this->field->args( 'repeatable' ) ) {
552 return false;
553 }
554
555 $values_array = $this->value;
556
557 $new_value = array();
558 foreach ( $values_array as $iterator => $this->value ) {
559 if ( $this->value ) {
560 $val = $this->$method( true );
561 if ( ! empty( $val ) ) {
562 $new_value[] = $val;
563 }
564 }
565 }
566
567 $this->value = $new_value;
568
569 return empty( $this->value ) ? null : $this->value;
570 }
571
572 /**
573 * Determine if passed value is an empty array
574 *
575 * @since 2.0.6
576 * @param mixed $to_check Value to check.
577 * @return boolean Whether value is an array that's empty
578 */
579 public function _is_empty_array( $to_check ) {
580 if ( is_array( $to_check ) ) {
581 $cleaned_up = array_filter( $to_check );
582 return empty( $cleaned_up );
583 }
584 return false;
585 }
586
587 }
588