PluginProbe ʕ •ᴥ•ʔ
Pods – Custom Content Types and Fields / 2.8.23.4
Pods – Custom Content Types and Fields v2.8.23.4
2.8.23.4 2.9.19.4 3.0.10.4 3.1.4.2 3.2.8.3 3.3.9.1 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 / datetime.php
pods / classes / fields Last commit date
avatar.php 2 weeks ago boolean.php 2 weeks ago code.php 2 weeks ago color.php 2 weeks ago comment.php 2 weeks ago currency.php 2 weeks ago date.php 2 weeks ago datetime.php 2 weeks ago email.php 2 weeks ago file.php 2 weeks ago heading.php 2 weeks ago html.php 2 weeks ago link.php 2 weeks ago number.php 2 weeks ago oembed.php 2 weeks ago paragraph.php 2 weeks ago password.php 2 weeks ago phone.php 2 weeks ago pick.php 2 weeks ago slug.php 2 weeks ago taxonomy.php 2 weeks ago text.php 2 weeks ago time.php 2 weeks ago website.php 2 weeks ago wysiwyg.php 2 weeks ago
datetime.php
1321 lines
1 <?php
2
3 use Pods\Static_Cache;
4 use Pods\Whatsit\Field;
5
6 /**
7 * @package Pods\Fields
8 */
9 class PodsField_DateTime extends PodsField {
10
11 /**
12 * {@inheritdoc}
13 */
14 public static $group = 'Date / Time';
15
16 /**
17 * {@inheritdoc}
18 */
19 public static $type = 'datetime';
20
21 /**
22 * {@inheritdoc}
23 */
24 public static $label = 'Date / Time';
25
26 /**
27 * {@inheritdoc}
28 */
29 public static $prepare = '%s';
30
31 /**
32 * Storage format.
33 *
34 * @var string
35 * @since 2.7.0
36 */
37 public static $storage_format = 'Y-m-d H:i:s';
38
39 /**
40 * The default empty value (database)
41 *
42 * @var string
43 * @since 2.7.0
44 */
45 public static $empty_value = '0000-00-00 00:00:00';
46
47 /**
48 * {@inheritdoc}
49 */
50 public function setup() {
51
52 static::$group = __( 'Date / Time', 'pods' );
53 static::$label = __( 'Date / Time', 'pods' );
54 }
55
56 /**
57 * {@inheritdoc}
58 */
59 public function options() {
60
61 $options = array(
62 static::$type . '_repeatable' => array(
63 'label' => __( 'Repeatable Field', 'pods' ),
64 'default' => 0,
65 'type' => 'boolean',
66 'help' => __( 'Making a field repeatable will add controls next to the field which allows users to Add/Remove/Reorder additional values. These values are saved in the database as an array, so searching and filtering by them may require further adjustments".', 'pods' ),
67 'boolean_yes_label' => '',
68 'dependency' => true,
69 'developer_mode' => true,
70 ),
71 static::$type . '_type' => array(
72 'label' => __( 'Date Format Type', 'pods' ),
73 'default' => 'format',
74 // Backwards compatibility
75 'type' => 'pick',
76 'help' => __( 'WordPress Default is the format used in Settings, General under "Date Format".', 'pods' ) . '<br>' . __( 'Predefined Format will allow you to select from a list of commonly used date formats.', 'pods' ) . '<br>' . __( 'Custom will allow you to enter your own using PHP Date/Time Strings.', 'pods' ),
77 'data' => array(
78 'wp' => __( 'WordPress default', 'pods' ) . ': ' . date_i18n( get_option( 'date_format' ) ),
79 'format' => __( 'Predefined format', 'pods' ),
80 'custom' => __( 'Custom format', 'pods' ),
81 ),
82 'pick_show_select_text' => 0,
83 'dependency' => true,
84 ),
85 static::$type . '_format_custom' => array(
86 'label' => __( 'Date Format for Display', 'pods' ),
87 'depends-on' => array( static::$type . '_type' => 'custom' ),
88 'default' => '',
89 'type' => 'text',
90 'help' => sprintf(
91 '<a href="https://docs.pods.io/fields/date-time-fields/datetime/" target="_blank" rel="noopener noreferrer">%1$s</a>',
92 esc_html__( 'Date / Time field documentation', 'pods' )
93 ),
94 ),
95 static::$type . '_format_custom_js' => array(
96 'label' => __( 'Date Format for Input', 'pods' ),
97 'depends-on' => array( static::$type . '_type' => 'custom' ),
98 'default' => '',
99 'type' => 'text',
100 'help' => sprintf(
101 '<a href="https://docs.pods.io/fields/date-time-fields/datetime/" target="_blank" rel="noopener noreferrer">%1$s</a><br />%2$s',
102 esc_html__( 'Date / Time field documentation', 'pods' ),
103 esc_html__( 'Leave empty to auto-generate from PHP format.', 'pods' )
104 ),
105 ),
106 static::$type . '_format' => array(
107 'label' => __( 'Date Format (predefined)', 'pods' ),
108 'depends-on' => array( static::$type . '_type' => 'format' ),
109 'default' => 'mdy',
110 'type' => 'pick',
111 'data' => array(
112 'mdy' => date_i18n( 'm/d/Y' ),
113 'mdy_dash' => date_i18n( 'm-d-Y' ),
114 'mdy_dot' => date_i18n( 'm.d.Y' ),
115 'ymd_slash' => date_i18n( 'Y/m/d' ),
116 'ymd_dash' => date_i18n( 'Y-m-d' ),
117 'ymd_dot' => date_i18n( 'Y.m.d' ),
118 'fjy' => date_i18n( 'F j, Y' ),
119 'fjsy' => date_i18n( 'F jS, Y' ),
120 'c' => date_i18n( 'c' ),
121 ),
122 'pick_show_select_text' => 0,
123 'dependency' => true,
124 ),
125 static::$type . '_time_type' => array(
126 'label' => __( 'Time Format Type', 'pods' ),
127 'excludes-on' => array( static::$type . '_format' => 'c' ),
128 'default' => '12',
129 // Backwards compatibility
130 'type' => 'pick',
131 'help' => __( 'WordPress Default is the format used in Settings, General under "Time Format".', 'pods' ) . '<br>' . __( '12/24 hour will allow you to select from a list of commonly used time formats.', 'pods' ) . '<br>' . __( 'Custom will allow you to enter your own using PHP Date/Time Strings.', 'pods' ),
132 'data' => array(
133 'wp' => __( 'WordPress default', 'pods' ) . ': ' . date_i18n( get_option( 'time_format' ) ),
134 '12' => __( '12 hour', 'pods' ),
135 '24' => __( '24 hour', 'pods' ),
136 'custom' => __( 'Custom', 'pods' ),
137 ),
138 'pick_show_select_text' => 0,
139 'dependency' => true,
140 ),
141 static::$type . '_time_format_custom' => array(
142 'label' => __( 'Time Format for Display', 'pods' ),
143 'depends-on' => array( static::$type . '_time_type' => 'custom' ),
144 'excludes-on' => array( static::$type . '_format' => 'c' ),
145 'default' => '',
146 'type' => 'text',
147 'help' => sprintf(
148 '<a href="https://docs.pods.io/fields/date-time-fields/datetime/" target="_blank" rel="noopener noreferrer">%1$s</a>',
149 esc_html__( 'Date / Time field documentation', 'pods' )
150 ),
151 ),
152 static::$type . '_time_format_custom_js' => array(
153 'label' => __( 'Time Format for Input', 'pods' ),
154 'depends-on' => array( static::$type . '_time_type' => 'custom' ),
155 'excludes-on' => array( static::$type . '_format' => 'c' ),
156 'default' => '',
157 'type' => 'text',
158 'help' => sprintf(
159 '<a href="https://docs.pods.io/fields/date-time-fields/datetime/" target="_blank" rel="noopener noreferrer">%1$s</a><br />%2$s',
160 esc_html__( 'Date / Time field documentation', 'pods' ),
161 esc_html__( 'Leave empty to auto-generate from PHP format.', 'pods' )
162 ),
163 ),
164 static::$type . '_time_format' => array(
165 'label' => __( 'Time Format (12 hour)', 'pods' ),
166 'depends-on' => array( static::$type . '_time_type' => '12' ),
167 'excludes-on' => array( static::$type . '_format' => 'c' ),
168 'default' => 'h_mma',
169 'type' => 'pick',
170 'data' => array(
171 'h_mm_A' => date_i18n( 'g:i A' ),
172 'h_mm_ss_A' => date_i18n( 'g:i:s A' ),
173 'hh_mm_A' => date_i18n( 'h:i A' ),
174 'hh_mm_ss_A' => date_i18n( 'h:i:s A' ),
175 'h_mma' => date_i18n( 'g:ia' ),
176 'hh_mma' => date_i18n( 'h:ia' ),
177 'h_mm' => date_i18n( 'g:i' ),
178 'h_mm_ss' => date_i18n( 'g:i:s' ),
179 'hh_mm' => date_i18n( 'h:i' ),
180 'hh_mm_ss' => date_i18n( 'h:i:s' ),
181 ),
182 'pick_show_select_text' => 0,
183 ),
184 static::$type . '_time_format_24' => array(
185 'label' => __( 'Time Format (24 hour)', 'pods' ),
186 'depends-on' => array( static::$type . '_time_type' => '24' ),
187 'excludes-on' => array( static::$type . '_format' => 'c' ),
188 'default' => 'hh_mm',
189 'type' => 'pick',
190 'data' => array(
191 'hh_mm' => date_i18n( 'H:i' ),
192 'hh_mm_ss' => date_i18n( 'H:i:s' ),
193 ),
194 'pick_show_select_text' => 0,
195 ),
196 static::$type . '_year_range_custom' => array(
197 'label' => __( 'Year Range', 'pods' ),
198 'default' => '',
199 'type' => 'text',
200 'help' => sprintf(
201 '%1$s<br /><a href="https://docs.pods.io/fields/date-time-fields/datetime/" target="_blank" rel="noopener noreferrer">%2$s</a>',
202 sprintf(
203 esc_html__( 'Example: %1$s for specifying a hard coded year range or %2$s for the last and next 10 years.', 'pods' ),
204 '<code>2010:2030</code>',
205 '<code>-10:+10</code>'
206 ),
207 esc_html__( 'Date / Time field documentation', 'pods' )
208 ),
209 ),
210 static::$type . '_allow_empty' => array(
211 'label' => __( 'Allow empty value', 'pods' ),
212 'default' => 1,
213 'type' => 'boolean',
214 ),
215 static::$type . '_html5' => array(
216 'label' => __( 'Enable HTML5 Input Field', 'pods' ),
217 'default' => apply_filters( 'pods_form_ui_field_html5', 0, static::$type ),
218 'type' => 'boolean',
219 ),
220 );
221
222 // Check if PHP DateTime::createFromFormat exists for additional supported formats
223 if ( method_exists( 'DateTime', 'createFromFormat' ) || apply_filters( 'pods_form_ui_field_datetime_custom_formatter', false ) ) {
224 $options[ static::$type . '_format' ]['data'] = array_merge(
225 $options[ static::$type . '_format' ]['data'], array(
226 'dmy' => date_i18n( 'd/m/Y' ),
227 'dmy_dash' => date_i18n( 'd-m-Y' ),
228 'dmy_dot' => date_i18n( 'd.m.Y' ),
229 'dMy' => date_i18n( 'd/M/Y' ),
230 'dMy_dash' => date_i18n( 'd-M-Y' ),
231 )
232 );
233 }
234
235 $options[ static::$type . '_format' ]['data'] = apply_filters( 'pods_form_ui_field_date_format_options', $options[ static::$type . '_format' ]['data'] );
236 $options[ static::$type . '_format' ]['default'] = apply_filters( 'pods_form_ui_field_date_format_default', $options[ static::$type . '_format' ]['default'] );
237
238 $options[ static::$type . '_time_type' ]['default'] = apply_filters( 'pods_form_ui_field_time_format_type_default', $options[ static::$type . '_time_type' ]['default'] );
239 $options[ static::$type . '_time_format' ]['data'] = apply_filters( 'pods_form_ui_field_time_format_options', $options[ static::$type . '_time_format' ]['data'] );
240 $options[ static::$type . '_time_format' ]['default'] = apply_filters( 'pods_form_ui_field_time_format_default', $options[ static::$type . '_time_format' ]['default'] );
241 $options[ static::$type . '_time_format_24' ]['data'] = apply_filters( 'pods_form_ui_field_time_format_24_options', $options[ static::$type . '_time_format_24' ]['data'] );
242 $options[ static::$type . '_time_format_24' ]['default'] = apply_filters( 'pods_form_ui_field_time_format_24_default', $options[ static::$type . '_time_format_24' ]['default'] );
243
244 return $options;
245 }
246
247 /**
248 * {@inheritdoc}
249 */
250 public function schema( $options = null ) {
251
252 $schema = 'DATETIME NOT NULL default "0000-00-00 00:00:00"';
253
254 return $schema;
255 }
256
257 /**
258 * {@inheritdoc}
259 */
260 public function is_empty( $value = null ) {
261
262 $is_empty = false;
263
264 $value = trim( $value );
265
266 if ( empty( $value ) || in_array( $value, array( '0000-00-00', '0000-00-00 00:00:00' ), true ) ) {
267 $is_empty = true;
268 }
269
270 return $is_empty;
271
272 }
273
274 /**
275 * {@inheritdoc}
276 */
277 public function display( $value = null, $name = null, $options = null, $pod = null, $id = null ) {
278 return $this->format_value_display( $value, $options, false );
279 }
280
281 /**
282 * {@inheritdoc}
283 */
284 public function input( $name, $value = null, $options = null, $pod = null, $id = null ) {
285
286 $options = ( is_array( $options ) || is_object( $options ) ) ? $options : (array) $options;
287 $form_field_type = PodsForm::$field_type;
288
289 if ( is_array( $value ) ) {
290 $value = implode( ' ', $value );
291 }
292
293 // @todo Remove? Format Value (done in field template).
294 //$value = $this->format_value_display( $value, $options, true );
295
296 $field_type = static::$type;
297
298 $is_read_only = (boolean) pods_v( 'read_only', $options, false );
299
300 if ( isset( $options['name'] ) && ! pods_permission( $options ) ) {
301 if ( $is_read_only ) {
302 $options['readonly'] = true;
303
304 $field_type = 'text';
305 } else {
306 return;
307 }
308 } elseif ( ! pods_has_permissions( $options ) && $is_read_only ) {
309 $options['readonly'] = true;
310
311 $field_type = 'text';
312 }
313
314 if ( ! empty( $options['disable_dfv'] ) ) {
315 return pods_view( PODS_DIR . 'ui/fields/' . $field_type . '.php', compact( array_keys( get_defined_vars() ) ) );
316 }
317
318 // Convert the date/time formats to MomentJS.
319 $options = $this->prepare_options_for_moment_js( $options );
320
321 $type = pods_v( 'type', $options, static::$type );
322
323 $args = compact( array_keys( get_defined_vars() ) );
324 $args = (object) $args;
325
326 $this->render_input_script( $args );
327 }
328
329 /**
330 * {@inheritdoc}
331 */
332 public function validate( $value, $name = null, $options = null, $fields = null, $pod = null, $id = null, $params = null ) {
333 $validate = parent::validate( $value, $name, $options, $fields, $pod, $id, $params );
334
335 $errors = array();
336
337 if ( is_array( $validate ) ) {
338 $errors = $validate;
339 }
340
341 if ( ! $this->is_empty( $value ) ) {
342
343 // Value should always be passed as storage format since 2.7.15.
344 $formats = [
345 static::$storage_format,
346 ];
347
348 if ( ! $this->is_storage_format( $value ) ) {
349 // Allow input values compatible with the input (JS) or display (PHP) formats.
350 $formats = [
351 $this->format_display( $options, true ),
352 $this->format_display( $options, false ),
353 ];
354
355 $formats = array_unique( array_filter( $formats ) );
356 }
357
358 $check = $this->convert_date( $value, static::$storage_format, $formats, true );
359
360 if ( false === $check ) {
361 $label = pods_v( 'label', $options, ucwords( str_replace( '_', ' ', $name ) ) );
362
363 // Translators: %1$s is the field label and %2$s is the input value.
364 $errors[] = sprintf( esc_html__( '%1$s was not provided in a recognizable format: "%2$s"', 'pods' ), $label, $value );
365 }
366 }
367
368 if ( ! empty( $errors ) ) {
369 return $errors;
370 }
371
372 return $validate;
373 }
374
375 /**
376 * {@inheritdoc}
377 */
378 public function pre_save( $value, $id = null, $name = null, $options = null, $fields = null, $pod = null, $params = null ) {
379
380 // Value should always be passed as storage format since 2.7.15.
381 $format = static::$storage_format;
382
383 if ( ! $this->is_empty( $value ) ) {
384 if ( ! $this->is_storage_format( $value ) ) {
385 // Allow input values compatible with the display format.
386 $format = $this->format_display( $options, false );
387 }
388 $value = $this->convert_date( $value, static::$storage_format, $format );
389 } elseif ( pods_v( static::$type . '_allow_empty', $options, 1 ) ) {
390 $value = static::$empty_value;
391 } else {
392 $value = date_i18n( static::$storage_format );
393 }
394
395 return $value;
396 }
397
398 /**
399 * {@inheritdoc}
400 */
401 public function ui( $id, $value, $name = null, $options = null, $fields = null, $pod = null ) {
402
403 $value = $this->display( $value, $name, $options, $pod, $id );
404
405 if ( $this->is_empty( $value ) && pods_v( static::$type . '_allow_empty', $options, 1 ) ) {
406 $value = false;
407 }
408
409 return $value;
410 }
411
412 /**
413 * Convert value to the correct format for display.
414 *
415 * @param string $value Field value.
416 * @param array $options Field options.
417 * @param bool $js Return formatted from jQuery UI format? (only for custom formats).
418 *
419 * @return string
420 * @since 2.7.0
421 */
422 public function format_value_display( $value, $options, $js = false ) {
423
424 $format = $this->format_display( $options, $js );
425
426 if ( ! $this->is_empty( $value ) ) {
427 // Try default storage format.
428 $date = $this->createFromFormat( static::$storage_format, (string) $value );
429
430 // Convert to timestamp.
431 if ( $date instanceof DateTime ) {
432 $timestamp = $date->getTimestamp();
433 } else {
434 // Try field format.
435 $date_local = $this->createFromFormat( $format, (string) $value );
436
437 if ( $date_local instanceof DateTime ) {
438 $timestamp = $date_local->getTimestamp();
439 } else {
440 // Fallback.
441 $timestamp = strtotime( (string) $value );
442 }
443 }
444
445 $value = date_i18n( $format, $timestamp );
446 } elseif ( ! pods_v( static::$type . '_allow_empty', $options, 1 ) ) {
447 $value = date_i18n( $format );
448 } else {
449 $value = '';
450 }
451
452 return $value;
453 }
454
455 /**
456 * Build date and/or time display format string based on options
457 *
458 * @since 2.7.13
459 *
460 * @param array $options Field options.
461 * @param bool $js Whether to return format for jQuery UI.
462 *
463 * @return string
464 */
465 public function format_display( $options, $js = false ) {
466
467 if ( 'custom' === pods_v( static::$type . '_type', $options, 'format' ) ) {
468 if ( $js ) {
469
470 // Gets format strings in jQuery UI format.
471 $date = $this->format_date( $options, $js );
472 $time = $this->format_time( $options, $js );
473
474 // Convert them to PHP date format.
475 $date = $this->convert_format( $date, array( 'source' => 'jquery_ui', 'type' => 'date' ) );
476 $time = $this->convert_format( $time, array( 'source' => 'jquery_ui', 'type' => 'time' ) );
477
478 return $date . ' ' . $time;
479
480 } else {
481 $format = $this->format_datetime( $options, $js );
482 }
483 } else {
484 $js = false;
485 $format = $this->format_datetime( $options, $js );
486 }
487
488 return $format;
489 }
490
491 /**
492 * Build date and/or time format string based on options
493 *
494 * @since 2.7.0
495 *
496 * @param array $options Field options.
497 * @param bool $js Whether to return format for jQuery UI.
498 *
499 * @return string
500 */
501 public function format_datetime( $options, $js = false ) {
502
503 $format = $this->format_date( $options, $js );
504
505 $type = pods_v( static::$type . '_type', $options, 'format' );
506
507 if ( 'format' !== $type || 'c' !== pods_v( static::$type . '_format', $options, '' ) ) {
508 $format .= ' ' . $this->format_time( $options, $js );
509 }
510
511 return $format;
512 }
513
514 /**
515 * Build date format string based on options
516 *
517 * @since 2.7.0
518 *
519 * @param array $options Field options.
520 * @param bool $js Whether to return format for jQuery UI.
521 *
522 * @return string
523 */
524 public function format_date( $options, $js = false ) {
525
526 switch ( (string) pods_v( static::$type . '_type', $options, 'format', true ) ) {
527 case 'wp':
528 $format = get_option( 'date_format' );
529
530 if ( $js ) {
531 $format = $this->convert_format( $format, array( 'source' => 'php', 'type' => 'date' ) );
532 }
533
534 break;
535 case 'custom':
536 if ( ! $js ) {
537 $format = pods_v( static::$type . '_format_custom', $options, '' );
538 } else {
539 $format = pods_v( static::$type . '_format_custom_js', $options, '' );
540
541 if ( empty( $format ) ) {
542 $format = pods_v( static::$type . '_format_custom', $options, '' );
543
544 if ( $js ) {
545 $format = $this->convert_format( $format, array( 'source' => 'php', 'type' => 'date' ) );
546 }
547 }
548 }
549
550 break;
551 default:
552 $date_format = $this->get_date_formats( $js );
553
554 $format = $date_format[ pods_v( static::$type . '_format', $options, 'ymd_dash', true ) ];
555
556 break;
557 }//end switch
558
559 return $format;
560 }
561
562 /**
563 * Build time format string based on options
564 *
565 * @since 2.7.0
566 *
567 * @param array $options Field options.
568 * @param bool $js Whether to return format for jQuery UI.
569 *
570 * @return string
571 */
572 public function format_time( $options, $js = false ) {
573
574 switch ( (string) pods_v( static::$type . '_time_type', $options, '12', true ) ) {
575 case '12':
576 $time_format = $this->get_time_formats( $js );
577
578 $format = $time_format[ pods_v( static::$type . '_time_format', $options, 'hh_mm', true ) ];
579
580 break;
581 case '24':
582 $time_format_24 = $this->get_time_formats_24( $js );
583
584 $format = $time_format_24[ pods_v( static::$type . '_time_format_24', $options, 'hh_mm', true ) ];
585
586 break;
587 case 'custom':
588 if ( ! $js ) {
589 $format = pods_v( static::$type . '_time_format_custom', $options, '' );
590 } else {
591 $format = pods_v( static::$type . '_time_format_custom_js', $options, '' );
592 $js = false; // Already in JS format.
593
594 if ( empty( $format ) ) {
595 $format = pods_v( static::$type . '_time_format_custom', $options, '' );
596 $js = true;
597 }
598 }
599
600 break;
601 default:
602 $format = get_option( 'time_format' );
603
604 break;
605 }//end switch
606
607 return $format;
608 }
609
610 /**
611 * Get the date formats.
612 *
613 * @since 2.7.0
614 *
615 * @param bool $js Whether to return format for jQuery UI.
616 *
617 * @return array
618 */
619 public function get_date_formats( $js = false ) {
620
621 $date_format = array(
622 'mdy' => 'm/d/Y',
623 'mdy_dash' => 'm-d-Y',
624 'mdy_dot' => 'm.d.Y',
625 'dmy' => 'd/m/Y',
626 'dmy_dash' => 'd-m-Y',
627 'dmy_dot' => 'd.m.Y',
628 'ymd_slash' => 'Y/m/d',
629 'ymd_dash' => 'Y-m-d',
630 'ymd_dot' => 'Y.m.d',
631 'dMy' => 'd/M/Y',
632 'dMy_dash' => 'd-M-Y',
633 'fjy' => 'F j, Y',
634 'fjsy' => 'F jS, Y',
635 'y' => 'Y',
636 'c' => 'c',
637 );
638
639 $filter = 'pods_form_ui_field_date_formats';
640
641 if ( $js ) {
642 foreach ( $date_format as $key => $value ) {
643 $date_format[ $key ] = $this->convert_format( $value, array( 'type' => 'date' ) );
644 }
645
646 $filter = 'pods_form_ui_field_date_js_formats';
647 }
648
649 return apply_filters( $filter, $date_format );
650 }
651
652 /**
653 * Get the time formats.
654 *
655 * @since 2.7.0
656 *
657 * @param bool $js Whether to return format for jQuery UI.
658 *
659 * @return array
660 */
661 public function get_time_formats( $js = false ) {
662
663 $time_format = array(
664 'h_mm_A' => 'g:i A',
665 'h_mm_ss_A' => 'g:i:s A',
666 'hh_mm_A' => 'h:i A',
667 'hh_mm_ss_A' => 'h:i:s A',
668 'h_mma' => 'g:ia',
669 'hh_mma' => 'h:ia',
670 'h_mm' => 'g:i',
671 'h_mm_ss' => 'g:i:s',
672 'hh_mm' => 'h:i',
673 'hh_mm_ss' => 'h:i:s',
674 );
675
676 $filter = 'pods_form_ui_field_time_formats';
677
678 if ( $js ) {
679 foreach ( $time_format as $key => $value ) {
680 $time_format[ $key ] = $this->convert_format( $value, array( 'type' => 'time' ) );
681 }
682
683 $filter = 'pods_form_ui_field_time_js_formats';
684 }
685
686 return apply_filters( $filter, $time_format );
687 }
688
689 /**
690 * Get the time formats.
691 *
692 * @since 2.7.0
693 *
694 * @param bool $js Whether to return format for jQuery UI.
695 *
696 * @return array
697 */
698 public function get_time_formats_24( $js = false ) {
699
700 $time_format_24 = array(
701 'hh_mm' => 'H:i',
702 'hh_mm_ss' => 'H:i:s',
703 );
704
705 $filter = 'pods_form_ui_field_time_formats_24';
706
707 if ( $js ) {
708 foreach ( $time_format_24 as $key => $value ) {
709 $time_format_24[ $key ] = $this->convert_format( $value, array( 'type' => 'time' ) );
710 }
711
712 $filter = 'pods_form_ui_field_time_js_formats_24';
713 }
714
715 return apply_filters( $filter, $time_format_24 );
716 }
717
718 /**
719 * PHP backwards compatibility for createFromFormat.
720 *
721 * @param string $format Format string.
722 * @param string $date Defaults to time() if empty.
723 * @param boolean $return_timestamp Whether to return the strtotime() or createFromFormat result or not.
724 *
725 * @return DateTime|null|int|false
726 */
727 public function createFromFormat( $format, $date, $return_timestamp = false ) {
728
729 $datetime = null;
730
731 try {
732 if ( method_exists( 'DateTime', 'createFromFormat' ) ) {
733
734 $datetime = DateTime::createFromFormat( $format, (string) $date );
735
736 if ( false === $datetime ) {
737 $datetime = DateTime::createFromFormat( static::$storage_format, (string) $date );
738 }
739
740 if ( false !== $datetime && $return_timestamp ) {
741 return $datetime;
742 }
743
744 }//end if
745
746 if ( in_array( $datetime, array( null, false ), true ) ) {
747 if ( empty( $date ) ) {
748 $timestamp = time();
749 } else {
750 $timestamp = strtotime( (string) $date );
751
752 if ( $return_timestamp ) {
753 return $timestamp;
754 }
755 }
756
757 if ( $timestamp ) {
758 $datetime = new DateTime( date_i18n( static::$storage_format, $timestamp ) );
759 }
760 }
761 } catch ( Exception $e ) {
762 // There is no saving this time value, it's an exception to the rule.
763 }
764
765 return apply_filters( 'pods_form_ui_field_datetime_formatter', $datetime, $format, $date );
766 }
767
768 /**
769 * Check if a value is compatible with the storage format.
770 *
771 * Valid:
772 * - 0000-00-00 00:00:00
773 * - 0000-00-00 00:00
774 * - 0000-00-00
775 * - 0000-00
776 * - etc.
777 *
778 * @param string $value The date value.
779 * @return bool
780 */
781 public function is_storage_format( $value ) {
782 $value_parts = str_split( $value );
783 $format_parts = str_split( gmdate( static::$storage_format ) );
784
785 $valid = true;
786 foreach ( $value_parts as $i => $part ) {
787 if ( isset( $format_parts[ $i ] ) ) {
788 if ( is_numeric( $format_parts[ $i ] ) ) {
789 if ( ! is_numeric( $part ) ) {
790 $valid = false;
791 break;
792 }
793 } elseif ( $format_parts[ $i ] !== $part ) {
794 $valid = false;
795 break;
796 }
797 }
798 }
799 return $valid;
800 }
801
802 /**
803 * Convert a date from one format to another.
804 *
805 * @param string $value Field value.
806 * @param string $new_format New format string.
807 * @param string|array $original_format Original format string(s) (if known).
808 * @param boolean $return_timestamp Whether to return the strtotime() or createFromFormat result or not.
809 *
810 * @return string|int|boolean|DateTime
811 */
812 public function convert_date( $value, $new_format, $original_format = '', $return_timestamp = false ) {
813
814 if ( empty( $original_format ) ) {
815 $original_format = static::$storage_format;
816 }
817
818 if ( is_array( $original_format ) ) {
819 foreach ( $original_format as $original_format_option ) {
820 $value = $this->convert_date( $value, $new_format, $original_format_option, $return_timestamp );
821
822 if ( false !== $value ) {
823 return $value;
824 }
825 }
826
827 return false;
828 }
829
830 $date = '';
831
832 if ( ! $this->is_empty( $value ) ) {
833 $date = $this->createFromFormat( $original_format, (string) $value, $return_timestamp );
834
835 if ( $date instanceof DateTime ) {
836 $value = $date->format( $new_format );
837 } elseif ( false !== $date ) {
838 $date = strtotime( (string) $value );
839
840 $value = date_i18n( $new_format, $date );
841 }
842 } else {
843 $value = date_i18n( $new_format );
844 }
845
846 // Return timestamp conversion result instead
847 if ( $return_timestamp ) {
848 return $date;
849 }
850
851 return $value;
852 }
853
854 /**
855 * Matches each symbol of PHP date format standard with jQuery equivalent codeword.
856 *
857 * @link http://stackoverflow.com/questions/16702398/convert-a-php-date-format-to-a-jqueryui-datepicker-date-format
858 * @link https://api.jqueryui.com/datepicker/
859 * @link http://trentrichardson.com/examples/timepicker/
860 *
861 * @since 2.7.0
862 *
863 * @param string $source_format Source format string.
864 * @param array $args Format arguments.
865 *
866 * @return string
867 */
868 public function convert_format( $source_format, $args = array() ) {
869
870 // @todo Improve source/target logic.
871 $args = array_merge(
872 array(
873 'source' => 'php',
874 'type' => 'date',
875 // 'jquery_ui' for reverse.
876 ), $args
877 );
878
879 // Keep keys and values sorted by string length.
880 if ( 'time' === $args['type'] || 'time' === static::$type ) {
881
882 $symbols = array(
883 // AM/PM.
884 'a' => 'tt',
885 'A' => 'TT',
886 // Swatch internet time (not supported).
887 'B' => '',
888 // Hour.
889 'h' => 'hh',
890 'H' => 'HH',
891 'g' => 'h',
892 'G' => 'H',
893 // Minute.
894 'i' => 'mm',
895 // Second.
896 's' => 'ss',
897 // Microsecond.
898 'u' => 'c',
899 // Timezone.
900 'O' => 'z',
901 'P' => 'Z',
902 );
903
904 if ( version_compare( PHP_VERSION, '7.0.0' ) >= 0 ) {
905 // Millisecond.
906 $symbols['v'] = 'l';
907 }
908
909 } else {
910
911 $symbols = array(
912 // Day.
913 'd' => 'dd',
914 'l' => 'DD',
915 'D' => 'D',
916 'j' => 'd',
917 'N' => '',
918 'S' => '',
919 'w' => '',
920 'z' => 'o',
921 // Week.
922 'W' => '',
923 // Month.
924 'F' => 'MM',
925 'm' => 'mm',
926 'M' => 'M',
927 'n' => 'm',
928 't' => '',
929 // Year.
930 'L' => '',
931 'o' => '',
932 'Y' => 'yy',
933 'y' => 'y',
934 );
935 }
936
937 if ( 'jquery_ui' === $args['source'] ) {
938 // Remove empty values.
939 $symbols = array_filter( $symbols );
940 $symbols = array_flip( $symbols );
941 }
942
943 $new_format = '';
944 $escaping = false;
945
946 $source_format_length = strlen( $source_format );
947
948 for ( $i = 0; $i < $source_format_length; $i ++ ) {
949 $char = $source_format[ $i ];
950
951 // PHP date format escaping character
952 // @todo Do we want to support non-format characters?
953 if ( '\\' === $char ) {
954 $i ++;
955
956 if ( $escaping ) {
957 $new_format .= $source_format[ $i ];
958 } else {
959 $new_format .= '\'' . $source_format[ $i ];
960 }
961
962 $escaping = true;
963 } else {
964 if ( $escaping ) {
965 $new_format .= "'";
966 $escaping = false;
967 }
968
969 $symbol_key = false;
970
971 if ( isset( $source_format[ $i + 1 ] ) ) {
972 $symbol_key = $char . $source_format[ $i + 1 ];
973 }
974
975 // Support 2 characters.
976 if ( $symbol_key && isset( $symbols[ $symbol_key ] ) ) {
977 $new_format .= $symbols[ $symbol_key ];
978
979 $i ++;
980 } elseif ( isset( $symbols[ $char ] ) ) {
981 $new_format .= $symbols[ $char ];
982 } else {
983 $new_format .= $char;
984 }
985 }//end if
986 }//end for
987
988 return $new_format;
989 }
990
991 /**
992 * Prepare the date/datetime/time field object or options for MomentJS formatting.
993 *
994 * @since 2.8.11
995 *
996 * @param array|Field $options The field object or options.
997 *
998 * @return array|Field The field object or options.
999 */
1000 public function prepare_options_for_moment_js( $options ) {
1001 // Handle time formats for datetime.
1002 if ( 'datetime' === static::$type ) {
1003 $date_format = $this->get_format_from_options_for_type( $options, static::$type, '', true );
1004 $time_format = $this->get_format_from_options_for_type( $options, static::$type, '_time', true );
1005
1006 $date_format_moment_js = $this->convert_format_to_moment_js( $date_format['format'], [
1007 'source' => $date_format['is_js'] ? 'jquery_ui' : 'php',
1008 'type' => 'date',
1009 ] );
1010 $time_format_moment_js = $this->convert_format_to_moment_js( $time_format['format'], [
1011 'source' => $time_format['is_js'] ? 'jquery_ui' : 'php',
1012 'type' => 'time',
1013 ] );
1014
1015 $options[ static::$type . '_date_format_moment_js' ] = $date_format_moment_js;
1016 $options[ static::$type . '_time_format_moment_js' ] = $time_format_moment_js;
1017 } elseif ( 'date' === static::$type ) {
1018 $date_format = $this->get_format_from_options_for_type( $options, static::$type, '', true );
1019
1020 $date_format_moment_js = $this->convert_format_to_moment_js( $date_format['format'], [
1021 'source' => $date_format['is_js'] ? 'jquery_ui' : 'php',
1022 'type' => 'date',
1023 ] );
1024
1025 $options[ static::$type . '_format_moment_js' ] = $date_format_moment_js;
1026 } elseif ( 'time' === static::$type ) {
1027 $time_format = $this->get_format_from_options_for_type( $options, static::$type, '', true );
1028
1029 $time_format_moment_js = $this->convert_format_to_moment_js( $time_format['format'], [
1030 'source' => $time_format['is_js'] ? 'jquery_ui' : 'php',
1031 'type' => 'time',
1032 ] );
1033
1034 $options[ static::$type . '_format_moment_js' ] = $time_format_moment_js;
1035 }
1036
1037 return $options;
1038 }
1039
1040 /**
1041 * Get the format from the options for a specific type (date/datetime/time).
1042 *
1043 * @since 2.8.11
1044 *
1045 * @param array|Field $options The field object or options.
1046 * @param string $type The specific field type.
1047 * @param string $prefix The prefix to use on the format options if needed (like "_time" in datetime_time_format).
1048 * @param bool $js Whether we want to get the format in the JS context.
1049 *
1050 * @return array The format information including if found/using the JS context option and the format.
1051 */
1052 public function get_format_from_options_for_type( $options, $type, $prefix = '', $js = false ) {
1053 $format_type = pods_v( $type . $prefix . '_type', $options );
1054
1055 $is_date_format = (
1056 'date' === $type
1057 || (
1058 'datetime' === $type
1059 && '' === $prefix
1060 )
1061 );
1062
1063 $is_24_hour = '24' === $format_type;
1064
1065 if ( '12' === $format_type || $is_24_hour ) {
1066 $format_type = 'format';
1067 }
1068
1069 // Get the format from the field setting.
1070 if ( 'format' === $format_type ) {
1071 if ( $is_date_format ) {
1072 // Get the format for date.
1073 $formats = $this->get_date_formats();
1074 } elseif ( $is_24_hour ) {
1075 // Get the format for time (24-hour).
1076 $formats = $this->get_time_formats_24();
1077 } else {
1078 // Get the format for time (12-hour).
1079 $formats = $this->get_time_formats();
1080 }
1081
1082 $format = pods_v( $type . $prefix . '_format', $options );
1083
1084 // Get 24-hour format.
1085 if ( $is_24_hour ) {
1086 $format = pods_v( $type . $prefix . '_format_24', $options );
1087 }
1088
1089 // Check if format is registered.
1090 if ( ! empty( $formats[ $format ] ) ) {
1091 return [
1092 'is_js' => false,
1093 'format' => $formats[ $format ],
1094 ];
1095 }
1096 }
1097
1098 // Get the custom format from the field setting.
1099 if ( 'custom' === $format_type ) {
1100 $format_custom = pods_v( $type . $prefix . '_format_custom', $options );
1101
1102 $is_js = false;
1103
1104 if ( $js ) {
1105 $format_custom_js = pods_v( $type . $prefix . '_format_custom_js', $options );
1106
1107 if ( ! empty( $format_custom_js ) ) {
1108 $is_js = true;
1109
1110 $format_custom = $format_custom_js;
1111 }
1112 }
1113
1114 // Check if there's a custom format.
1115 if ( ! empty( $format_custom ) ) {
1116 return [
1117 'is_js' => $is_js,
1118 'format' => $format_custom,
1119 ];
1120 }
1121 }
1122
1123 // Fallback to wp format.
1124 $options[ $type . $prefix . '_type' ] = 'wp';
1125
1126 // Maybe get the date format from WordPress.
1127 if ( $is_date_format ) {
1128 return [
1129 'is_js' => false,
1130 'format' => get_option( 'date_format' ),
1131 ];
1132 }
1133
1134 // Get the time format from WordPress.
1135 return [
1136 'is_js' => false,
1137 'format' => get_option( 'time_format' ),
1138 ];
1139 }
1140
1141 /**
1142 * Convert the source format to MomentJS format for PHP / jQuery UI formats.
1143 *
1144 * @since 2.8.11
1145 *
1146 * @param string|mixed $source_format The source format.
1147 * @param array $args The list of format arguments including source (php/jquery_ui) and type (date/time).
1148 *
1149 * @return string|mixed The converted MomentJS format.
1150 */
1151 public function convert_format_to_moment_js( $source_format, $args = array() ) {
1152 if ( ! is_string( $source_format ) || '' === trim( $source_format ) ) {
1153 return $source_format;
1154 }
1155
1156 $defaults = [
1157 'source' => 'php', // php or jquery_ui.
1158 'type' => 'date', // date or time.
1159 ];
1160
1161 $args = array_merge( $defaults, $args );
1162
1163 // For PHP symbols, see https://www.php.net/manual/en/datetime.format.php
1164 // For Moment.js symbols, see https://momentjs.com/docs/#/displaying/format/
1165 $php_replacements = [
1166 'A' => 'A', // for the sake of escaping below
1167 'a' => 'a', // for the sake of escaping below
1168 'B' => '', // Swatch internet time (.beats), no equivalent
1169 'c' => 'YYYY-MM-DD[T]HH:mm:ssZ', // ISO 8601
1170 'D' => 'ddd',
1171 'd' => 'DD',
1172 'e' => 'zz', // deprecated since version 1.6.0 of moment.js
1173 'F' => 'MMMM',
1174 'G' => 'H',
1175 'g' => 'h',
1176 'H' => 'HH',
1177 'h' => 'hh',
1178 'I' => '', // Daylight Saving Time? => moment().isDST();
1179 'i' => 'mm',
1180 'j' => 'D',
1181 'L' => '', // Leap year? => moment().isLeapYear();
1182 'l' => 'dddd',
1183 'M' => 'MMM',
1184 'm' => 'MM',
1185 'N' => 'E',
1186 'n' => 'M',
1187 'O' => 'ZZ',
1188 'o' => 'YYYY',
1189 'P' => 'Z',
1190 'r' => 'ddd, DD MMM YYYY HH:mm:ss ZZ', // RFC 2822
1191 'S' => 'o',
1192 's' => 'ss',
1193 'T' => 'z', // deprecated since version 1.6.0 of moment.js
1194 't' => '', // days in the month => moment().daysInMonth();
1195 'U' => 'X',
1196 'u' => 'SSSSSS', // microseconds
1197 'v' => 'SSS', // milliseconds (from PHP 7.0.0)
1198 'W' => 'W', // for the sake of escaping below
1199 'w' => 'e',
1200 'Y' => 'YYYY',
1201 'y' => 'YY',
1202 'Z' => '', // time zone offset in minutes => moment().zone();
1203 'z' => 'DDD',
1204 ];
1205
1206 // For jQuery symbols, see https://api.jqueryui.com/datepicker/#utility-formatDate
1207 // For Moment.js symbols, see https://momentjs.com/docs/#/displaying/format/
1208 $jquery_ui_date_replacements = [
1209 'dd' => 'DD', // day of month (two digit)
1210 'd' => 'D', // day of month (no leading zero)
1211 'oo' => 'DDDD', // day of the year (three digit)
1212 'o' => 'DDD', // day of the year (no leading zeros)
1213 'DD' => 'dddd', // day name long
1214 'D' => 'dd', // day name short
1215 'mm' => 'MM', // month of year (two digit)
1216 'm' => 'M', // month of year (no leading zero)
1217 'MM' => 'MMMM', // month name long
1218 'M' => 'MMM', // month name short
1219 'yy' => 'YYYY', // year (four digit)
1220 'y' => 'YY', // year (two digit)
1221 '@' => 'X', // Unix timestamp (ms since 01/01/1970)
1222 '!' => '', // Windows ticks (100ns since 01/01/0001), no equivalent
1223 ];
1224
1225 // For jQuery symbols, see http://trentrichardson.com/examples/timepicker/#tp-formatting
1226 // For Moment.js symbols, see https://momentjs.com/docs/#/displaying/format/
1227 $jquery_ui_time_replacements = [
1228 'HH' => 'HH', // Hour with leading 0 (24 hour)
1229 'H' => 'H', // Hour with no leading 0 (24 hour)
1230 'hh' => 'hh', // Hour with leading 0 (12 hour)
1231 'h' => 'h', // Hour with no leading 0 (12 hour)
1232 'mm' => 'mm', // Minute with leading 0
1233 'm' => 'm', // Minute with no leading 0
1234 'i' => 'mm', // In case they got confused with PHP time format
1235 'ss' => 'ss', // Second with leading 0
1236 's' => 's', // Second with no leading 0
1237 'l' => 'SSS', // Milliseconds always with leading 0
1238 'c' => 'SSSSSS', // Microseconds always with leading 0
1239 't' => 'a', // a or p for AM/PM, no equivalent, switches to am/pm
1240 'TT' => 'A', // AM or PM for AM/PM
1241 'tt' => 'a', // am or pm for AM/PM
1242 'T' => 'A', // A or P for AM/PM, no equivalent, switches to AM/PM
1243 'z' => '', // Timezone as defined by timezoneList => moment().zone();
1244 'Z' => '', // Timezone in Iso 8601 format (+04:45) => moment().zone();
1245 ];
1246
1247 // Handle PHP first since it only has one replacement logic.
1248 if ( 'php' === $args['source'] ) {
1249 return pods_replace_keys_to_values( $source_format, $php_replacements );
1250 }
1251
1252 // Handle jQuery UI replacements.
1253 if ( 'jquery_ui' === $args['source'] ) {
1254 if ( 'date' === $args['type'] ) {
1255 return pods_replace_keys_to_values( $source_format, $jquery_ui_date_replacements );
1256 } elseif ( 'time' === $args['type'] ) {
1257 return pods_replace_keys_to_values( $source_format, $jquery_ui_time_replacements );
1258 }
1259 }
1260
1261 return $source_format;
1262 }
1263
1264 /**
1265 * Enqueue the i18n files for jquery date/timepicker
1266 *
1267 * @deprecated since 2.8.0
1268 *
1269 * @since 2.7.0
1270 */
1271 public function enqueue_jquery_ui_i18n() {
1272 $done = (array) pods_static_cache_get( 'done', __METHOD__ );
1273
1274 $types = array();
1275
1276 switch ( static::$type ) {
1277 case 'time':
1278 $types['time'] = true;
1279
1280 break;
1281 case 'date':
1282 $types['date'] = true;
1283
1284 break;
1285 case 'datetime':
1286 $types['time'] = true;
1287 $types['date'] = true;
1288
1289 break;
1290 }
1291
1292 $locale = str_replace( '_', '-', get_locale() );
1293
1294 if ( isset( $types['date'] ) && ! isset( $done[ 'date-' . $locale ] ) ) {
1295 if ( function_exists( 'wp_localize_jquery_ui_datepicker' ) ) {
1296 wp_localize_jquery_ui_datepicker();
1297 }
1298
1299 $done['date'] = true;
1300 }
1301
1302 if ( isset( $types['time'] ) && ! isset( $done[ 'time-' . $locale ] ) ) {
1303 $locale_exists = file_exists( PODS_DIR . 'ui/js/timepicker/i18n/jquery-ui-timepicker-' . $locale . '.js' );
1304
1305 // Local files.
1306 if ( ! $locale_exists ) {
1307 // Fallback to the base language (non-region specific).
1308 $locale = substr( $locale, 0, strpos( $locale, '-' ) );
1309
1310 $locale_exists = file_exists( PODS_DIR . 'ui/js/timepicker/i18n/jquery-ui-timepicker-' . $locale . '.js' );
1311 }
1312
1313 if ( $locale_exists && ! wp_script_is( 'jquery-ui-timepicker-i18n-' . $locale ) ) {
1314 wp_enqueue_script( 'jquery-ui-timepicker-i18n-' . $locale, PODS_URL . 'ui/js/timepicker/i18n/jquery-ui-timepicker-' . $locale . '.js', [ 'jquery-ui-timepicker' ], '1.6.3' );
1315 }
1316
1317 $done[ 'time-' . $locale ] = true;
1318 }
1319 }
1320 }
1321