PluginProbe ʕ •ᴥ•ʔ
Pods – Custom Content Types and Fields / 3.3.4
Pods – Custom Content Types and Fields v3.3.4
trunk 1.14.8 2.7.31.3 2.8.23.3 2.9.19.3 3.0.10.3 3.1.4.1 3.2.0 3.2.1 3.2.1.1 3.2.2 3.2.4 3.2.5 3.2.6 3.2.7 3.2.7.1 3.2.8 3.2.8.1 3.2.8.2 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9
pods / classes / fields / number.php
pods / classes / fields Last commit date
avatar.php 3 years ago boolean.php 3 years ago code.php 2 years ago color.php 1 year ago comment.php 4 years ago currency.php 9 months ago date.php 2 years ago datetime.php 1 year ago email.php 1 year ago file.php 11 months ago heading.php 1 year ago html.php 2 years ago link.php 1 year ago number.php 9 months ago oembed.php 1 year ago paragraph.php 2 years ago password.php 1 year ago phone.php 1 year ago pick.php 11 months ago slug.php 3 years ago taxonomy.php 4 years ago text.php 2 years ago time.php 2 years ago website.php 11 months ago wysiwyg.php 1 year ago
number.php
570 lines
1 <?php
2
3 /**
4 * @package Pods\Fields
5 */
6 class PodsField_Number extends PodsField {
7
8 /**
9 * {@inheritdoc}
10 */
11 public static $group = 'Number';
12
13 /**
14 * {@inheritdoc}
15 */
16 public static $type = 'number';
17
18 /**
19 * {@inheritdoc}
20 */
21 public static $label = 'Plain Number';
22
23 /**
24 * {@inheritdoc}
25 */
26 public static $prepare = '%d';
27
28 /**
29 * {@inheritdoc}
30 */
31 public function setup() {
32
33 static::$group = __( 'Number', 'pods' );
34 static::$label = __( 'Plain Number', 'pods' );
35 }
36
37 /**
38 * {@inheritdoc}
39 */
40 public function options() {
41
42 $options = array(
43 static::$type . '_format_type' => array(
44 'label' => __( 'Input Type', 'pods' ),
45 'default' => 'number',
46 'type' => 'pick',
47 'data' => array(
48 'number' => __( 'Freeform Number', 'pods' ),
49 'slider' => __( 'Slider', 'pods' ),
50 ),
51 'pick_format_single' => 'dropdown',
52 'pick_show_select_text' => 0,
53 'dependency' => true,
54 ),
55 static::$type . '_format' => array(
56 'label' => __( 'Number Format', 'pods' ),
57 'default' => apply_filters( 'pods_form_ui_field_number_format_default', 'i18n' ),
58 'type' => 'pick',
59 'data' => array(
60 'i18n' => __( 'Localized Default', 'pods' ),
61 '9,999.99' => '1,234.00',
62 '9999.99' => '1234.00',
63 '9.999,99' => '1.234,00',
64 '9999,99' => '1234,00',
65 '9 999,99' => '1 234,00',
66 '9\'999.99' => '1\'234.00',
67 ),
68 'pick_format_single' => 'dropdown',
69 'pick_show_select_text' => 0,
70 ),
71 static::$type . '_decimals' => array(
72 'label' => __( 'Decimals', 'pods' ),
73 'default' => 0,
74 'type' => 'number',
75 'dependency' => true,
76 'help' => __( 'Set to a positive number to enable decimals. The upper limit in the database for this field is 30 decimals.', 'pods' ),
77 ),
78 static::$type . '_format_soft' => array(
79 'label' => __( 'Soft Formatting (deprecated, use Decimal handling instead)', 'pods' ),
80 'help' => __( 'Remove trailing decimals (0)', 'pods' ),
81 'default' => 0,
82 'type' => 'boolean',
83 'excludes-on' => array( static::$type . '_decimals' => 0 ),
84 ),
85 static::$type . '_decimal_handling' => array(
86 'label' => __( 'Decimal handling for trailing zero decimals', 'pods' ),
87 'description' => __( '', 'pods' ),
88 'default' => 'none',
89 'type' => 'pick',
90 'data' => array(
91 'none' => __( 'Default (Examples: "1.00", "0.00")', 'pods' ),
92 'remove' => __( 'Remove decimals (Examples: "1", "0")', 'pods' ),
93 'remove_only_zero' => __( 'Remove decimals only when number is zero (Examples: "1.00", "0")', 'pods' ),
94 'dash' => __( 'Convert to dash (Examples: "1.-", "0.-")', 'pods' ),
95 'dash_only_zero' => __( 'Convert to dash only when number is zero (Examples: "1.00", "0.-")', 'pods' ),
96 'dash_whole_zero' => __( 'Convert to the whole value to dash when number is zero (Examples: "1.00", "-")', 'pods' ),
97 ),
98 'pick_format_single' => 'dropdown',
99 'pick_show_select_text' => 0,
100 ),
101 static::$type . '_step' => array(
102 'label' => __( 'Slider Increment (Step)', 'pods' ),
103 'depends-on' => array( static::$type . '_format_type' => 'slider' ),
104 'default' => 1,
105 'type' => 'text',
106 ),
107 static::$type . '_min' => array(
108 'label' => __( 'Minimum Number', 'pods' ),
109 'depends-on-any' => array(
110 static::$type . '_format_type' => 'slider',
111 static::$type . '_html5' => true,
112 ),
113 'default' => '',
114 'type' => 'text',
115 ),
116 static::$type . '_max' => array(
117 'label' => __( 'Maximum Number', 'pods' ),
118 'depends-on-any' => array(
119 static::$type . '_format_type' => 'slider',
120 static::$type . '_html5' => true,
121 ),
122 'default' => '',
123 'type' => 'text',
124 ),
125 static::$type . '_max_length' => array(
126 'label' => __( 'Maximum Digits', 'pods' ),
127 'default' => 12,
128 'type' => 'number',
129 'help' => __( 'Set to -1 for no limit. The upper limit in the database for this field is 64 digits.', 'pods' ),
130 ),
131 static::$type . '_html5' => array(
132 'label' => __( 'Enable HTML5 Input Field', 'pods' ),
133 'default' => apply_filters( 'pods_form_ui_field_html5', 0, static::$type ),
134 'depends-on' => array( static::$type . '_format_type' => 'number' ),
135 'type' => 'boolean',
136 ),
137 static::$type . '_placeholder' => array(
138 'label' => __( 'HTML Placeholder', 'pods' ),
139 'default' => '',
140 'type' => 'text',
141 'help' => array(
142 __( 'Placeholders can provide instructions or an example of the required data format for a field. Please note: It is not a replacement for labels or description text, and it is less accessible for people using screen readers.', 'pods' ),
143 'https://www.w3.org/WAI/tutorials/forms/instructions/#placeholder-text',
144 ),
145 ),
146 );
147
148 return $options;
149 }
150
151 /**
152 * {@inheritdoc}
153 */
154 public function schema( $options = null ) {
155
156 $length = (int) pods_v( static::$type . '_max_length', $options, 12, true );
157
158 if ( $length < 1 || 64 < $length ) {
159 $length = 64;
160 }
161
162 $decimals = $this->get_max_decimals( $options );
163
164 $schema = 'DECIMAL(' . $length . ',' . $decimals . ')';
165
166 return $schema;
167
168 }
169
170 /**
171 * {@inheritdoc}
172 */
173 public function prepare( $options = null ) {
174
175 $format = static::$prepare;
176
177 $decimals = $this->get_max_decimals( $options );
178
179 if( 6 < $decimals ) {
180 // %F only allows 6 decimals by default
181 $format = '%.' . $decimals . 'F';
182 } else if ( 0 < $decimals ) {
183 $format = '%F';
184 }
185
186 return $format;
187
188 }
189
190 /**
191 * @todo 2.8 Centralize the usage of this method. See PR #5540.
192 * {@inheritdoc}
193 */
194 public function is_empty( $value = null ) {
195
196 $is_empty = false;
197
198 $value = (float) $value;
199
200 if ( empty( $value ) ) {
201 $is_empty = true;
202 }
203
204 return $is_empty;
205
206 }
207
208 /**
209 * {@inheritdoc}
210 */
211 public function display( $value = null, $name = null, $options = null, $pod = null, $id = null ) {
212 return $this->format( $value, $name, $options, $pod, $id );
213 }
214
215 /**
216 * {@inheritdoc}
217 */
218 public function input( $name, $value = null, $options = null, $pod = null, $id = null ) {
219
220 $options = ( is_array( $options ) || is_object( $options ) ) ? $options : (array) $options;
221 $form_field_type = PodsForm::$field_type;
222 $is_read_only = false;
223
224 $value = $this->normalize_value_for_input( $value, $options, '' );
225
226 if ( 'slider' === pods_v( static::$type . '_format_type', $options, 'number' ) ) {
227 $field_type = 'slider';
228 } else {
229 $field_type = static::$type;
230 }
231
232 if ( isset( $options['name'] ) && ! pods_permission( $options ) ) {
233 if ( pods_v( 'read_only', $options, false ) ) {
234 $is_read_only = true;
235 } else {
236 return;
237 }
238 } elseif ( ! pods_has_permissions( $options ) && pods_v( 'read_only', $options, false ) ) {
239 $is_read_only = true;
240 }
241
242 if ( $is_read_only ) {
243 $options['readonly'] = true;
244
245 $field_type = 'text';
246 }
247
248 // Enforce boolean.
249 $options[ static::$type . '_html5' ] = filter_var( pods_v( static::$type . '_html5', $options, false ), FILTER_VALIDATE_BOOLEAN );
250 $options[ static::$type . '_format_soft' ] = filter_var( pods_v( static::$type . '_format_soft', $options, false ), FILTER_VALIDATE_BOOLEAN );
251
252 // Only format the value for non-HTML5 inputs.
253 if ( ! $options[ static::$type . '_html5' ] ) {
254 // Ensure proper format
255 if ( is_array( $value ) ) {
256 foreach ( $value as $k => $repeatable_value ) {
257 $value[ $k ] = $this->format( $repeatable_value, $name, $options, $pod, $id );
258 }
259 } else {
260 $value = $this->format( $value, $name, $options, $pod, $id );
261 }
262 }
263
264 if ( ! empty( $options['disable_dfv'] ) ) {
265 return pods_view( PODS_DIR . 'ui/fields/number.php', compact( array_keys( get_defined_vars() ) ) );
266 }
267
268 $type = pods_v( 'type', $options, static::$type );
269
270 $args = compact( array_keys( get_defined_vars() ) );
271 $args = (object) $args;
272
273 $this->render_input_script( $args );
274 }
275
276 /**
277 * {@inheritdoc}
278 */
279 public function regex( $value = null, $name = null, $options = null, $pod = null, $id = null ) {
280
281 $format_args = $this->get_number_format_args( $options );
282 $thousands = $format_args['thousands'];
283 $dot = $format_args['dot'];
284
285 return '\-*[0-9\\' . implode( '\\', array_filter( array( $dot, $thousands ) ) ) . ']+';
286 }
287
288 /**
289 * {@inheritdoc}
290 */
291 public function validate( $value, $name = null, $options = null, $fields = null, $pod = null, $id = null, $params = null ) {
292 $validate = parent::validate( $value, $name, $options, $fields, $pod, $id, $params );
293
294 $errors = array();
295
296 if ( is_array( $validate ) ) {
297 $errors = $validate;
298 }
299
300 $format_args = $this->get_number_format_args( $options );
301 $thousands = $format_args['thousands'];
302 $dot = $format_args['dot'];
303
304 $check = str_replace(
305 array( $thousands, $dot, html_entity_decode( $thousands ) ),
306 array( '', '.', '' ),
307 $value
308 );
309
310 $check = trim( $check );
311
312 $check = preg_replace( '/[0-9\.\-\s]/', '', $check );
313
314 $label = pods_v( 'label', $options, ucwords( str_replace( '_', ' ', $name ) ) );
315
316 if ( 0 < strlen( (string) $check ) ) {
317 // Translators: %s stands for the input value.
318 $errors[] = sprintf( esc_html__( '%s is not numeric', 'pods' ), $label );
319 }
320
321 if ( ! empty( $errors ) ) {
322 return $errors;
323 }
324
325 return $validate;
326 }
327
328 /**
329 * {@inheritdoc}
330 */
331 public function pre_save( $value, $id = null, $name = null, $options = null, $fields = null, $pod = null, $params = null ) {
332
333 $format_args = $this->get_number_format_args( $options );
334 $thousands = $format_args['thousands'];
335 $dot = $format_args['dot'];
336 $decimals = $format_args['decimals'];
337
338 // Slider only supports `1234.00` format so no need for replacing characters.
339 if ( 'slider' !== pods_v( static::$type . '_format_type', $options ) ) {
340 // Not a slider so we need to replace format characters.
341 $value = str_replace(
342 array( $thousands, html_entity_decode( $thousands ), $dot, html_entity_decode( $dot ) ),
343 array( '', '', '.', '.' ),
344 $value
345 );
346
347 // HTML5 supports both `1234.00` and `1234,00` formats so let's replace commas as decimals (thousands replaced above).
348 if ( 1 === (int) pods_v( static::$type . '_html5', $options, false ) ) {
349 $value = str_replace( ',', '.', $value );
350 }
351 }
352
353 $value = trim( $value );
354
355 $value = preg_replace( '/[^0-9\.\-]/', '', $value );
356
357 if ( $this->is_empty( $value ) && ( ! is_numeric( $value ) || 0.0 !== (float) $value ) ) {
358 // Don't enforce a default value here.
359 return null;
360 }
361
362 $value = number_format( (float) $value, $decimals, '.', '' );
363
364 // Optionally remove trailing decimal zero's.
365 if ( pods_v( static::$type . '_format_soft', $options, false ) ) {
366 $value = $this->trim_decimals( $value, '.' );
367 }
368
369 return $value;
370 }
371
372 /**
373 * {@inheritdoc}
374 */
375 public function format( $value = null, $name = null, $options = null, $pod = null, $id = null ) {
376
377 if ( $this->is_empty( $value ) && ( ! is_numeric( $value ) || 0.0 !== (float) $value ) ) {
378 // Don't enforce a default value here.
379 return null;
380 }
381
382 $format_args = $this->get_number_format_args( $options );
383 $thousands = $format_args['thousands'];
384 $dot = $format_args['dot'];
385 $decimals = $format_args['decimals'];
386
387 if ( 'i18n' === pods_v( static::$type . '_format', $options ) ) {
388 $value = number_format_i18n( (float) $value, $decimals );
389 } else {
390 $value = number_format( (float) $value, $decimals, $dot, $thousands );
391 }
392
393 // Additional output handling for decimals
394 $decimals = (int) pods_v( static::$type . '_decimals', $options, 2 );
395
396 if ( $decimals < 1 ) {
397 return $value;
398 }
399
400 $decimal_handling = pods_v( static::$type . '_decimal_handling', $options, 'none' );
401
402 if ( 'none' === $decimal_handling && (bool) pods_v( static::$type . '_format_soft', $options, false ) ) {
403 $decimal_handling = 'remove';
404 }
405
406 if ( 'none' !== $decimal_handling ) {
407 $format_args = $this->get_number_format_args( $options );
408 $dot = $format_args['dot'];
409 $thousands = $format_args['thousands'];
410
411 $value_parts = explode( $dot, $value );
412
413 $value_number_int = isset( $value_parts[0] ) ? (int) str_replace( $thousands, '', $value_parts[0] ) : 0;
414 $is_zero = 0 === $value_number_int;
415
416 if ( isset( $value_parts[1] ) && 0 === (int) $value_parts[1] ) {
417 switch ( $decimal_handling ) {
418 case 'remove':
419 // Remove decimals.
420 $value = $value_parts[0];
421 break;
422
423 case 'remove_only_zero':
424 // Remove decimals only when number is zero.
425 if ( $is_zero ) {
426 $value = $value_parts[0];
427 }
428 break;
429
430 case 'dash':
431 // Convert to dash.
432 $value = $value_parts[0] . $dot . '-';
433 break;
434
435 case 'dash_only_zero':
436 // Convert to dash only when number is zero.
437 if ( $is_zero ) {
438 $value = $value_parts[0] . $dot . '-';
439 }
440 break;
441
442 case 'dash_whole_zero':
443 // Convert to the whole value to dash when number is zero.
444 if ( $is_zero ) {
445 $value = '-';
446 }
447 break;
448 }
449 }
450 }
451
452 return $value;
453 }
454
455 /**
456 * Trim trailing 0 decimals from numbers.
457 *
458 * @since 2.7.15
459 *
460 * @param string $value
461 * @param string $dot
462 *
463 * @return string
464 */
465 public function trim_decimals( $value, $dot ) {
466 $parts = explode( $dot, $value );
467
468 if ( isset( $parts[1] ) ) {
469 $parts[1] = rtrim( $parts[1], '0' );
470
471 if ( empty( $parts[1] ) ) {
472 unset( $parts[1] );
473 }
474 }
475
476 return implode( $dot, $parts );
477 }
478
479 /**
480 * Get the formatting arguments for numbers.
481 *
482 * @since 2.7.0
483 *
484 * @param array $options Field options.
485 *
486 * @return array {
487 * @type string $thousands
488 * @type string $dot
489 * @type int $decimals
490 * }
491 */
492 public function get_number_format_args( $options ) {
493
494 $format = pods_v( static::$type . '_format', $options );
495 $format = pods_unslash( $format );
496
497 switch ( $format ) {
498 case '9.999,99':
499 $thousands = '.';
500 $dot = ',';
501 break;
502 case '9,999.99':
503 $thousands = ',';
504 $dot = '.';
505 break;
506 case '9\'999.99':
507 $thousands = '\'';
508 $dot = '.';
509 break;
510 case '9 999,99':
511 $thousands = ' ';
512 $dot = ',';
513 break;
514 case '9999.99':
515 $thousands = '';
516 $dot = '.';
517 break;
518 case '9999,99':
519 $thousands = '';
520 $dot = ',';
521 break;
522 default:
523 global $wp_locale;
524 $thousands = $wp_locale->number_format['thousands_sep'];
525 $dot = $wp_locale->number_format['decimal_point'];
526 break;
527 }
528
529 $decimals = $this->get_max_decimals( $options );
530
531 return array(
532 'thousands' => $thousands,
533 'dot' => $dot,
534 'decimals' => $decimals,
535 );
536 }
537
538 /**
539 * Get the max allowed decimals.
540 *
541 * @since 2.7.0
542 *
543 * @param array $options Field options.
544 *
545 * @return int
546 */
547 public function get_max_decimals( $options ) {
548
549 $length = (int) pods_v( static::$type . '_max_length', $options, 12, true );
550
551 if ( $length < 1 || 64 < $length ) {
552 $length = 64;
553 }
554
555 $decimals = (int) pods_v( static::$type . '_decimals', $options, 0 );
556
557 if ( $decimals < 1 ) {
558 $decimals = 0;
559 } elseif ( 30 < $decimals ) {
560 $decimals = 30;
561 }
562
563 if ( $length < $decimals ) {
564 $decimals = $length;
565 }
566
567 return $decimals;
568 }
569 }
570