Completed
Pull Request — 2.x (#4569)
by Scott Kingsley
04:42
created

PodsField_DateTime   D

Complexity

Total Complexity 105

Size/Duplication

Total Lines 886
Duplicated Lines 7.11 %

Coupling/Cohesion

Components 2
Dependencies 2

Importance

Changes 0
Metric Value
dl 63
loc 886
rs 4
c 0
b 0
f 0
wmc 105
lcom 2
cbo 2

20 Methods

Rating   Name   Duplication   Size   Complexity  
B options() 12 165 3
A schema() 0 6 1
A is_empty() 0 13 3
A display() 0 6 1
C input() 0 30 7
C validate() 0 33 7
C pre_save() 3 27 7
A ui() 0 16 4
C format_value_display() 9 31 8
A setup() 0 4 1
A format_datetime() 0 12 3
B format_date() 0 28 6
C format_time() 32 32 7
B get_date_formats() 0 27 2
A get_time_formats() 0 23 2
A get_time_formats_24() 0 15 2
C createFromFormat() 0 43 11
C convert_date() 7 29 7
C convert_format() 0 110 11
C enqueue_jquery_ui_i18n() 0 46 12

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like PodsField_DateTime often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PodsField_DateTime, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * @package Pods\Fields
5
 */
6
class PodsField_DateTime extends PodsField {
7
8
	/**
9
	 * {@inheritdoc}
10
	 */
11
	public static $group = 'Date / Time';
12
13
	/**
14
	 * {@inheritdoc}
15
	 */
16
	public static $type = 'datetime';
17
18
	/**
19
	 * {@inheritdoc}
20
	 */
21
	public static $label = 'Date / Time';
22
23
	/**
24
	 * {@inheritdoc}
25
	 */
26
	public static $prepare = '%s';
27
28
	/**
29
	 * Storage format.
30
	 *
31
	 * @var string
32
	 * @since 2.7
33
	 */
34
	public static $storage_format = 'Y-m-d H:i:s';
35
36
	/**
37
	 * The default empty value (database)
38
	 *
39
	 * @var string
40
	 * @since 2.7
41
	 */
42
	public static $empty_value = '0000-00-00 00:00:00';
43
44
	/**
45
	 * {@inheritdoc}
46
	 */
47
	public function setup() {
48
49
		static::$label = __( 'Date / Time', 'pods' );
50
	}
51
52
	/**
53
	 * {@inheritdoc}
54
	 */
55
	public function options() {
56
57
		$options = array(
58
			static::$type . '_repeatable'            => array(
59
				'label'             => __( 'Repeatable Field', 'pods' ),
60
				'default'           => 0,
61
				'type'              => 'boolean',
62
				'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' ),
63
				'boolean_yes_label' => '',
64
				'dependency'        => true,
65
				'developer_mode'    => true,
66
			),
67
			static::$type . '_type'                  => array(
68
				'label'                        => __( 'Date Format Type', 'pods' ),
69
				'default'                      => 'format',
70
				// Backwards compatibility
71
										'type' => 'pick',
72
				'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' ),
73
				'data'                         => array(
74
					'wp'     => __( 'WordPress default', 'pods' ) . ': ' . date_i18n( get_option( 'date_format' ) ),
75
					'format' => __( 'Predefined format', 'pods' ),
76
					'custom' => __( 'Custom format', 'pods' ),
77
				),
78
				'dependency'                   => true,
79
			),
80
			static::$type . '_format_custom'         => array(
81
				'label'      => __( 'Date format for display', 'pods' ),
82
				'depends-on' => array( static::$type . '_type' => 'custom' ),
83
				'default'    => '',
84
				'type'       => 'text',
85
				'help'       => sprintf(
86
					'<a href="http://php.net/manual/function.date.php" target="_blank">%s</a>',
87
					esc_html__( 'PHP date documentation', 'pods' )
88
				),
89
			),
90
			static::$type . '_format_custom_js'      => array(
91
				'label'      => __( 'Date format for input', 'pods' ),
92
				'depends-on' => array( static::$type . '_type' => 'custom' ),
93
				'default'    => '',
94
				'type'       => 'text',
95
				'help'       => sprintf(
96
					'<a href="https://api.jqueryui.com/datepicker/" target="_blank">%1$s</a><br />%2$s',
97
					esc_html__( 'jQuery UI datepicker documentation', 'pods' ),
98
					esc_html__( 'Leave empty to auto-generate from PHP format.', 'pods' )
99
				),
100
			),
101
			static::$type . '_format'                => array(
102
				'label'      => __( 'Date Format', 'pods' ),
103
				'depends-on' => array( static::$type . '_type' => 'format' ),
104
				'default'    => 'mdy',
105
				'type'       => 'pick',
106
				'data'       => array(
107
					'mdy'       => date_i18n( 'm/d/Y' ),
108
					'mdy_dash'  => date_i18n( 'm-d-Y' ),
109
					'mdy_dot'   => date_i18n( 'm.d.Y' ),
110
					'ymd_slash' => date_i18n( 'Y/m/d' ),
111
					'ymd_dash'  => date_i18n( 'Y-m-d' ),
112
					'ymd_dot'   => date_i18n( 'Y.m.d' ),
113
					'fjy'       => date_i18n( 'F j, Y' ),
114
					'fjsy'      => date_i18n( 'F jS, Y' ),
115
					'c'         => date_i18n( 'c' ),
116
				),
117
				'dependency' => true,
118
			),
119
			static::$type . '_time_type'             => array(
120
				'label'                        => __( 'Time Format Type', 'pods' ),
121
				'excludes-on'                  => array( static::$type . '_format' => 'c' ),
122
				'default'                      => '12',
123
				// Backwards compatibility
124
										'type' => 'pick',
125
				'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' ),
126
				'data'                         => array(
127
					'wp'     => __( 'WordPress default', 'pods' ) . ': ' . date_i18n( get_option( 'time_format' ) ),
128
					'12'     => __( '12 hour', 'pods' ),
129
					'24'     => __( '24 hour', 'pods' ),
130
					'custom' => __( 'Custom', 'pods' ),
131
				),
132
				'dependency'                   => true,
133
			),
134
			static::$type . '_time_format_custom'    => array(
135
				'label'       => __( 'Time format', 'pods' ),
136
				'depends-on'  => array( static::$type . '_time_type' => 'custom' ),
137
				'excludes-on' => array( static::$type . '_format' => 'c' ),
138
				'default'     => '',
139
				'type'        => 'text',
140
				'help'        => '<a href="http://php.net/manual/function.date.php" target="_blank">' . __( 'PHP date documentation', 'pods' ) . '</a>',
141
			),
142
			static::$type . '_time_format_custom_js' => array(
143
				'label'       => __( 'Time format field input', 'pods' ),
144
				'depends-on'  => array( static::$type . '_time_type' => 'custom' ),
145
				'excludes-on' => array( static::$type . '_format' => 'c' ),
146
				'default'     => '',
147
				'type'        => 'text',
148
				'help'        => sprintf(
149
					'<a href="http://trentrichardson.com/examples/timepicker/#tp-formatting" target="_blank">%1$s</a><br />%2$s',
150
					esc_html__( 'jQuery UI timepicker documentation', 'pods' ),
151
					esc_html__( 'Leave empty to auto-generate from PHP format.', 'pods' )
152
				),
153
			),
154
			static::$type . '_time_format'           => array(
155
				'label'       => __( 'Time Format', 'pods' ),
156
				'depends-on'  => array( static::$type . '_time_type' => '12' ),
157
				'excludes-on' => array( static::$type . '_format' => 'c' ),
158
				'default'     => 'h_mma',
159
				'type'        => 'pick',
160
				'data'        => array(
161
					'h_mm_A'     => date_i18n( 'g:i A' ),
162
					'h_mm_ss_A'  => date_i18n( 'g:i:s A' ),
163
					'hh_mm_A'    => date_i18n( 'h:i A' ),
164
					'hh_mm_ss_A' => date_i18n( 'h:i:s A' ),
165
					'h_mma'      => date_i18n( 'g:ia' ),
166
					'hh_mma'     => date_i18n( 'h:ia' ),
167
					'h_mm'       => date_i18n( 'g:i' ),
168
					'h_mm_ss'    => date_i18n( 'g:i:s' ),
169
					'hh_mm'      => date_i18n( 'h:i' ),
170
					'hh_mm_ss'   => date_i18n( 'h:i:s' ),
171
				),
172
			),
173
			static::$type . '_time_format_24'        => array(
174
				'label'       => __( 'Time Format', 'pods' ),
175
				'depends-on'  => array( static::$type . '_time_type' => '24' ),
176
				'excludes-on' => array( static::$type . '_format' => 'c' ),
177
				'default'     => 'hh_mm',
178
				'type'        => 'pick',
179
				'data'        => array(
180
					'hh_mm'    => date_i18n( 'H:i' ),
181
					'hh_mm_ss' => date_i18n( 'H:i:s' ),
182
				),
183
			),
184
			static::$type . '_allow_empty'           => array(
185
				'label'   => __( 'Allow empty value?', 'pods' ),
186
				'default' => 1,
187
				'type'    => 'boolean',
188
			),
189
			static::$type . '_html5'                 => array(
190
				'label'   => __( 'Enable HTML5 Input Field?', 'pods' ),
191
				'default' => apply_filters( 'pods_form_ui_field_html5', 0, static::$type ),
192
				'type'    => 'boolean',
193
			),
194
		);
195
196
		// Check if PHP DateTime::createFromFormat exists for additional supported formats
197 View Code Duplication
		if ( method_exists( 'DateTime', 'createFromFormat' ) || apply_filters( 'pods_form_ui_field_datetime_custom_formatter', false ) ) {
198
			$options[ static::$type . '_format' ]['data'] = array_merge(
199
				$options[ static::$type . '_format' ]['data'], array(
200
					'dmy'      => date_i18n( 'd/m/Y' ),
201
					'dmy_dash' => date_i18n( 'd-m-Y' ),
202
					'dmy_dot'  => date_i18n( 'd.m.Y' ),
203
					'dMy'      => date_i18n( 'd/M/Y' ),
204
					'dMy_dash' => date_i18n( 'd-M-Y' ),
205
				)
206
			);
207
		}
208
209
		$options[ static::$type . '_format' ]['data']    = apply_filters( 'pods_form_ui_field_date_format_options', $options[ static::$type . '_format' ]['data'] );
210
		$options[ static::$type . '_format' ]['default'] = apply_filters( 'pods_form_ui_field_date_format_default', $options[ static::$type . '_format' ]['default'] );
211
212
		$options[ static::$type . '_time_type' ]['default']      = apply_filters( 'pods_form_ui_field_time_format_type_default', $options[ static::$type . '_time_type' ]['default'] );
213
		$options[ static::$type . '_time_format' ]['data']       = apply_filters( 'pods_form_ui_field_time_format_options', $options[ static::$type . '_time_format' ]['data'] );
214
		$options[ static::$type . '_time_format' ]['default']    = apply_filters( 'pods_form_ui_field_time_format_default', $options[ static::$type . '_time_format' ]['default'] );
215
		$options[ static::$type . '_time_format_24' ]['data']    = apply_filters( 'pods_form_ui_field_time_format_24_options', $options[ static::$type . '_time_format_24' ]['data'] );
216
		$options[ static::$type . '_time_format_24' ]['default'] = apply_filters( 'pods_form_ui_field_time_format_24_default', $options[ static::$type . '_time_format_24' ]['default'] );
217
218
		return $options;
219
	}
220
221
	/**
222
	 * {@inheritdoc}
223
	 */
224
	public function schema( $options = null ) {
225
226
		$schema = 'DATETIME NOT NULL default "0000-00-00 00:00:00"';
227
228
		return $schema;
229
	}
230
231
	/**
232
	 * {@inheritdoc}
233
	 */
234
	public function is_empty( $value = null ) {
235
236
		$is_empty = false;
237
238
		$value = trim( $value );
239
240
		if ( empty( $value ) || in_array( $value, array( '0000-00-00', '0000-00-00 00:00:00', '00:00:00' ), true ) ) {
241
			$is_empty = true;
242
		}
243
244
		return $is_empty;
245
246
	}
247
248
	/**
249
	 * {@inheritdoc}
250
	 */
251
	public function display( $value = null, $name = null, $options = null, $pod = null, $id = null ) {
252
253
		$value = $this->format_value_display( $value, $options, false );
254
255
		return $value;
256
	}
257
258
	/**
259
	 * {@inheritdoc}
260
	 */
261
	public function input( $name, $value = null, $options = null, $pod = null, $id = null ) {
262
263
		$options         = (array) $options;
264
		$form_field_type = PodsForm::$field_type;
265
266
		if ( is_array( $value ) ) {
267
			$value = implode( ' ', $value );
268
		}
269
270
		// Format Value
271
		$value = $this->format_value_display( $value, $options, true );
272
273
		$field_type = static::$type;
274
275
		if ( isset( $options['name'] ) && false === PodsForm::permission( static::$type, $options['name'], $options, null, $pod, $id ) ) {
276
			if ( pods_v( 'read_only', $options, false ) ) {
277
				$options['readonly'] = true;
278
279
				$field_type = 'text';
280
			} else {
281
				return;
282
			}
283
		} elseif ( ! pods_has_permissions( $options ) && pods_v( 'read_only', $options, false ) ) {
284
			$options['readonly'] = true;
285
286
			$field_type = 'text';
287
		}
288
289
		pods_view( PODS_DIR . 'ui/fields/' . $field_type . '.php', compact( array_keys( get_defined_vars() ) ) );
290
	}
291
292
	/**
293
	 * {@inheritdoc}
294
	 */
295
	public function validate( $value, $name = null, $options = null, $fields = null, $pod = null, $id = null, $params = null ) {
296
297
		if ( ! empty( $value ) && ( 0 === (int) pods_v( static::$type . '_allow_empty', $options, 1 ) || ! in_array(
298
			$value, array(
299
				'0000-00-00',
300
				'0000-00-00 00:00:00',
301
				'00:00:00',
302
			), true
303
		) ) ) {
304
			$js = true;
305
306
			if ( 'custom' !== pods_v( static::$type . '_type', $options, 'format' ) ) {
307
				$js = false;
308
			}
309
310
			$format = $this->format_datetime( $options, $js );
311
312
			if ( $js ) {
313
				$format = $this->convert_format( $format, array( 'source' => 'jquery_ui' ) );
314
			}
315
316
			$check = $this->convert_date( $value, static::$storage_format, $format, true );
317
318
			if ( false === $check ) {
319
				$label = pods_v( 'label', $options, ucwords( str_replace( '_', ' ', $name ) ) );
320
321
				return sprintf( esc_html__( '%1$s was not provided in a recognizable format: "%2$s"', 'pods' ), $label, $value );
322
			}
323
		}//end if
324
325
		return true;
326
327
	}
328
329
	/**
330
	 * {@inheritdoc}
331
	 */
332
	public function pre_save( $value, $id = null, $name = null, $options = null, $fields = null, $pod = null, $params = null ) {
333
334
		$js = true;
335
		if ( 'custom' !== pods_v( static::$type . '_type', $options, 'format' ) ) {
336
			$js = false;
337
		}
338
		$format = $this->format_datetime( $options, $js );
339
		if ( $js ) {
340
			$format = $this->convert_format( $format, array( 'source' => 'jquery_ui' ) );
341
		}
342
343
		if ( ! empty( $value ) && ( 0 === (int) pods_v( static::$type . '_allow_empty', $options, 1 ) || ! in_array(
344
			$value, array(
345
				'0000-00-00',
346
				'0000-00-00 00:00:00',
347
				'00:00:00',
348
			), true
349
		) ) ) {
350
			$value = $this->convert_date( $value, static::$storage_format, $format );
351 View Code Duplication
		} elseif ( 1 === (int) pods_v( static::$type . '_allow_empty', $options, 1 ) ) {
352
			$value = static::$empty_value;
353
		} else {
354
			$value = date_i18n( static::$storage_format );
355
		}
356
357
		return $value;
358
	}
359
360
	/**
361
	 * {@inheritdoc}
362
	 */
363
	public function ui( $id, $value, $name = null, $options = null, $fields = null, $pod = null ) {
364
365
		$value = $this->display( $value, $name, $options, $pod, $id );
366
367
		if ( 1 === (int) pods_v( static::$type . '_allow_empty', $options, 1 ) && ( empty( $value ) || in_array(
368
			$value, array(
369
				'0000-00-00',
370
				'0000-00-00 00:00:00',
371
				'00:00:00',
372
			), true
373
		) ) ) {
374
			$value = false;
375
		}
376
377
		return $value;
378
	}
379
380
	/**
381
	 * Convert value to the correct format for display.
382
	 *
383
	 * @param string $value   Field value.
384
	 * @param array  $options Field options.
385
	 * @param bool   $js      Return formatted from jQuery UI format? (only for custom formats).
386
	 *
387
	 * @return string
388
	 * @since 2.7
389
	 */
390
	public function format_value_display( $value, $options, $js = false ) {
391
392
		if ( 'custom' !== pods_v( static::$type . '_type', $options, 'format' ) ) {
393
			$js = false;
394
		}
395
		$format = $this->format_datetime( $options, $js );
396
		if ( $js ) {
397
			$format = $this->convert_format( $format, array( 'source' => 'jquery_ui' ) );
398
		}
399
400
		if ( ! empty( $value ) && ! in_array( $value, array( '0000-00-00', '0000-00-00 00:00:00', '00:00:00' ), true ) ) {
401
			// Try default storage format.
402
			$date = $this->createFromFormat( static::$storage_format, (string) $value );
403
			// Try field format.
404
			$date_local = $this->createFromFormat( $format, (string) $value );
405
406 View Code Duplication
			if ( $date instanceof DateTime ) {
407
				$value = $date->format( $format );
408
			} elseif ( $date_local instanceof DateTime ) {
409
				$value = $date_local->format( $format );
410
			} else {
411
				$value = date_i18n( $format, strtotime( (string) $value ) );
412
			}
413 View Code Duplication
		} elseif ( 0 === (int) pods_v( static::$type . '_allow_empty', $options, 1 ) ) {
414
			$value = date_i18n( $format );
415
		} else {
416
			$value = '';
417
		}
418
419
		return $value;
420
	}
421
422
	/**
423
	 * Build date and/or time format string based on options
424
	 *
425
	 * @since  2.7
426
	 *
427
	 * @param  array $options Field options.
428
	 * @param  bool  $js      Whether to return format for jQuery UI.
429
	 *
430
	 * @return string
431
	 */
432
	public function format_datetime( $options, $js = false ) {
433
434
		$format = $this->format_date( $options, $js );
435
436
		$type = pods_v( static::$type . '_type', $options, 'format' );
437
438
		if ( 'format' !== $type || 'c' !== pods_v( static::$type . '_format', $options, '' ) ) {
439
			$format .= ' ' . $this->format_time( $options, $js );
440
		}
441
442
		return $format;
443
	}
444
445
	/**
446
	 * Build date format string based on options
447
	 *
448
	 * @since  2.7
449
	 *
450
	 * @param  array $options Field options.
451
	 * @param  bool  $js      Whether to return format for jQuery UI.
452
	 *
453
	 * @return string
454
	 */
455
	public function format_date( $options, $js = false ) {
456
457
		switch ( (string) pods_v( static::$type . '_type', $options, 'format', true ) ) {
458
			case 'wp':
459
				$format = get_option( 'date_format' );
460
				if ( $js ) {
461
					$format = $this->convert_format( $format, array( 'source' => 'php' ) );
462
				}
463
				break;
464
			case 'custom':
465
				if ( ! $js ) {
466
					$format = pods_v( static::$type . '_format_custom', $options, '' );
467
				} else {
468
					$format = pods_v( static::$type . '_format_custom_js', $options, '' );
469
					if ( empty( $format ) ) {
470
						$format = pods_v( static::$type . '_format_custom', $options, '' );
471
						$format = $this->convert_format( $format, array( 'source' => 'php' ) );
472
					}
473
				}
474
				break;
475
			default:
476
				$date_format = $this->get_date_formats( $js );
477
				$format      = $date_format[ pods_v( static::$type . '_format', $options, 'ymd_dash', true ) ];
478
				break;
479
		}//end switch
480
481
		return $format;
482
	}
483
484
	/**
485
	 * Build time format string based on options
486
	 *
487
	 * @since  2.7
488
	 *
489
	 * @param  array $options Field options.
490
	 * @param  bool  $js      Whether to return format for jQuery UI.
491
	 *
492
	 * @return string
493
	 */
494 View Code Duplication
	public function format_time( $options, $js = false ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
495
496
		switch ( (string) pods_v( static::$type . '_time_type', $options, '12', true ) ) {
497
			case '12':
498
				$time_format = $this->get_time_formats( $js );
499
				$format      = $time_format[ pods_v( static::$type . '_time_format', $options, 'hh_mm', true ) ];
500
				break;
501
			case '24':
502
				$time_format_24 = $this->get_time_formats_24( $js );
503
				$format         = $time_format_24[ pods_v( static::$type . '_time_format_24', $options, 'hh_mm', true ) ];
504
				break;
505
			case 'custom':
506
				if ( ! $js ) {
507
					$format = pods_v( static::$type . '_time_format_custom', $options, '' );
508
				} else {
509
					$format = pods_v( static::$type . '_time_format_custom_js', $options, '' );
510
					if ( empty( $format ) ) {
511
						$format = pods_v( static::$type . '_time_format_custom', $options, '' );
512
						$format = $this->convert_format( $format, array( 'source' => 'php' ) );
513
					}
514
				}
515
				break;
516
			default:
517
				$format = get_option( 'time_format' );
518
				if ( $js ) {
519
					$format = $this->convert_format( $format, array( 'source' => 'php' ) );
520
				}
521
				break;
522
		}//end switch
523
524
		return $format;
525
	}
526
527
	/**
528
	 * Get the date formats.
529
	 *
530
	 * @since  2.7
531
	 *
532
	 * @param  bool $js Whether to return format for jQuery UI.
533
	 *
534
	 * @return array
535
	 */
536
	public function get_date_formats( $js = false ) {
537
538
		$date_format = array(
539
			'mdy'       => 'm/d/Y',
540
			'mdy_dash'  => 'm-d-Y',
541
			'mdy_dot'   => 'm.d.Y',
542
			'dmy'       => 'd/m/Y',
543
			'dmy_dash'  => 'd-m-Y',
544
			'dmy_dot'   => 'd.m.Y',
545
			'ymd_slash' => 'Y/m/d',
546
			'ymd_dash'  => 'Y-m-d',
547
			'ymd_dot'   => 'Y.m.d',
548
			'dMy'       => 'd/M/Y',
549
			'dMy_dash'  => 'd-M-Y',
550
			'fjy'       => 'F j, Y',
551
			'fjsy'      => 'F jS, Y',
552
			'y'         => 'Y',
553
		);
554
		$filter      = 'pods_form_ui_field_date_formats';
555
		if ( $js ) {
556
			// @todo Method parameters? (Not supported by array_map)
557
			$date_format = array_map( array( $this, 'convert_format' ), $date_format );
558
			$filter      = 'pods_form_ui_field_date_js_formats';
559
		}
560
561
		return apply_filters( $filter, $date_format );
562
	}
563
564
	/**
565
	 * Get the time formats.
566
	 *
567
	 * @since  2.7
568
	 *
569
	 * @param  bool $js Whether to return format for jQuery UI.
570
	 *
571
	 * @return array
572
	 */
573
	public function get_time_formats( $js = false ) {
574
575
		$time_format = array(
576
			'h_mm_A'     => 'g:i A',
577
			'h_mm_ss_A'  => 'g:i:s A',
578
			'hh_mm_A'    => 'h:i A',
579
			'hh_mm_ss_A' => 'h:i:s A',
580
			'h_mma'      => 'g:ia',
581
			'hh_mma'     => 'h:ia',
582
			'h_mm'       => 'g:i',
583
			'h_mm_ss'    => 'g:i:s',
584
			'hh_mm'      => 'h:i',
585
			'hh_mm_ss'   => 'h:i:s',
586
		);
587
		$filter      = 'pods_form_ui_field_time_formats';
588
		if ( $js ) {
589
			// @todo Method parameters? (Not supported by array_map)
590
			$time_format = array_map( array( $this, 'convert_format' ), $time_format );
591
			$filter      = 'pods_form_ui_field_time_js_formats';
592
		}
593
594
		return apply_filters( $filter, $time_format );
595
	}
596
597
	/**
598
	 * Get the time formats.
599
	 *
600
	 * @since  2.7
601
	 *
602
	 * @param  bool $js Whether to return format for jQuery UI.
603
	 *
604
	 * @return array
605
	 */
606
	public function get_time_formats_24( $js = false ) {
607
608
		$time_format_24 = array(
609
			'hh_mm'    => 'H:i',
610
			'hh_mm_ss' => 'H:i:s',
611
		);
612
		$filter         = 'pods_form_ui_field_time_formats_24';
613
		if ( $js ) {
614
			// @todo Method parameters? (Not supported by array_map)
615
			$time_format_24 = array_map( array( $this, 'convert_format' ), $time_format_24 );
616
			$filter         = 'pods_form_ui_field_time_js_formats_24';
617
		}
618
619
		return apply_filters( $filter, $time_format_24 );
620
	}
621
622
	/**
623
	 * PHP backwards compatibility for createFromFormat.
624
	 *
625
	 * @param string  $format           Format string.
626
	 * @param string  $date             Defaults to time() if empty.
627
	 * @param boolean $return_timestamp Whether to return the strtotime() or createFromFormat result or not.
628
	 *
629
	 * @return DateTime|null|int|false
630
	 */
631
	public function createFromFormat( $format, $date, $return_timestamp = false ) {
632
633
		$datetime = null;
634
635
		if ( method_exists( 'DateTime', 'createFromFormat' ) ) {
636
			$timezone = get_option( 'timezone_string' );
637
638
			if ( empty( $timezone ) ) {
639
				$timezone = timezone_name_from_abbr( '', get_option( 'gmt_offset' ) * HOUR_IN_SECONDS, 0 );
640
			}
641
642
			if ( ! empty( $timezone ) ) {
643
				$datetimezone = new DateTimeZone( $timezone );
644
645
				$datetime = DateTime::createFromFormat( $format, (string) $date, $datetimezone );
0 ignored issues
show
Bug introduced by
The method createFromFormat() does not exist on DateTime. Did you maybe mean format()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
646
647
				if ( false === $datetime ) {
648
					$datetime = DateTime::createFromFormat( static::$storage_format, (string) $date, $datetimezone );
0 ignored issues
show
Bug introduced by
The method createFromFormat() does not exist on DateTime. Did you maybe mean format()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
649
				}
650
651
				if ( false !== $datetime && $return_timestamp ) {
652
					return $datetime;
653
				}
654
			}
655
		}//end if
656
657
		if ( in_array( $datetime, array( null, false ), true ) ) {
658
			if ( empty( $date ) ) {
659
				$timestamp = time();
660
			} else {
661
				$timestamp = strtotime( (string) $date );
662
663
				if ( $return_timestamp ) {
664
					return $timestamp;
665
				}
666
			}
667
			if ( $timestamp ) {
668
				$datetime = new DateTime( date_i18n( static::$storage_format, $timestamp ) );
669
			}
670
		}
671
672
		return apply_filters( 'pods_form_ui_field_datetime_formatter', $datetime, $format, $date );
673
	}
674
675
	/**
676
	 * Convert a date from one format to another.
677
	 *
678
	 * @param string  $value            Field value.
679
	 * @param string  $new_format       New format string.
680
	 * @param string  $original_format  Original format string (if known).
681
	 * @param boolean $return_timestamp Whether to return the strtotime() or createFromFormat result or not.
682
	 *
683
	 * @return string|int|boolean|DateTime
684
	 */
685
	public function convert_date( $value, $new_format, $original_format = '', $return_timestamp = false ) {
686
687
		if ( empty( $original_format ) ) {
688
			$original_format = static::$storage_format;
689
		}
690
691
		$date = '';
692
693
		if ( ! empty( $value ) && ! in_array( $value, array( '0000-00-00', '0000-00-00 00:00:00', '00:00:00' ), true ) ) {
694
			$date = $this->createFromFormat( $original_format, (string) $value, $return_timestamp );
695
696 View Code Duplication
			if ( $date instanceof DateTime ) {
697
				$value = $date->format( $new_format );
698
			} elseif ( false !== $date ) {
699
				$date = strtotime( (string) $value );
700
701
				$value = date_i18n( $new_format, $date );
702
			}
703
		} else {
704
			$value = date_i18n( $new_format );
705
		}
706
707
		// Return timestamp conversion result instead
708
		if ( $return_timestamp ) {
709
			return $date;
710
		}
711
712
		return $value;
713
	}
714
715
	/**
716
	 * Matches each symbol of PHP date format standard with jQuery equivalent codeword.
717
	 *
718
	 * @link   http://stackoverflow.com/questions/16702398/convert-a-php-date-format-to-a-jqueryui-datepicker-date-format
719
	 * @link   https://api.jqueryui.com/datepicker/
720
	 * @link   http://trentrichardson.com/examples/timepicker/
721
	 *
722
	 * @since  2.7
723
	 *
724
	 * @param  string $source_format Source format string.
725
	 * @param  array  $args          Format arguments.
726
	 *
727
	 * @return string
728
	 */
729
	public function convert_format( $source_format, $args = array() ) {
730
731
		// @todo Improve source/target logic.
732
		$args = array_merge(
733
			array(
734
				'source' => 'php',
735
			// 'jquery_ui' for reverse.
736
			), $args
737
		);
738
739
		// Keep keys and values sorted by string length.
740
		$symbols = array(
741
			// Day
742
			'd' => 'dd',
743
			'l' => 'DD',
744
			'D' => 'D',
745
			'j' => 'd',
746
			'N' => '',
747
			'S' => '',
748
			'w' => '',
749
			'z' => 'o',
750
			// Week
751
			'W' => '',
752
			// Month
753
			'F' => 'MM',
754
			'm' => 'mm',
755
			'M' => 'M',
756
			'n' => 'm',
757
			't' => '',
758
			// Year
759
			'L' => '',
760
			'o' => '',
761
			'Y' => 'yy',
762
			'y' => 'y',
763
			// AM/PM
764
			'a' => 'tt',
765
			'A' => 'TT',
766
			// Swatch internet time (not supported)
767
			'B' => '',
768
			// Hour
769
			'h' => 'hh',
770
			'H' => 'HH',
771
			'g' => 'h',
772
			'G' => 'H',
773
			// Minute
774
			'i' => 'mm',
775
			// Second
776
			's' => 'ss',
777
			// Microsecond
778
			'u' => 'c',
779
		);
780
781
		if ( version_compare( PHP_VERSION, '7.0.0' ) >= 0 ) {
782
			// Millisecond
783
			$symbols['v'] = 'l';
784
		}
785
786
		if ( 'jquery_ui' === $args['source'] ) {
787
			// Remove empty values.
788
			$symbols = array_filter( $symbols );
789
			$symbols = array_flip( $symbols );
790
		}
791
792
		$new_format = '';
793
		$escaping   = false;
794
795
		$source_format_length = strlen( $source_format );
796
797
		for ( $i = 0; $i < $source_format_length; $i ++ ) {
798
			$char = $source_format[ $i ];
799
800
			// PHP date format escaping character
801
			// @todo Do we want to support non-format characters?
802
			if ( '\\' === $char ) {
803
				$i ++;
804
805
				if ( $escaping ) {
806
					$new_format .= $source_format[ $i ];
807
				} else {
808
					$new_format .= '\'' . $source_format[ $i ];
809
				}
810
811
				$escaping = true;
812
			} else {
813
				if ( $escaping ) {
814
					$new_format .= "'";
815
					$escaping    = false;
816
				}
817
818
				$symbol_key = false;
819
820
				if ( isset( $source_format[ $i + 1 ] ) ) {
821
					$symbol_key = $char . $source_format[ $i + 1 ];
822
				}
823
824
				// Support 2 characters.
825
				if ( $symbol_key && isset( $symbols[ $symbol_key ] ) ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $symbol_key of type string|false is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== false instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
826
					$new_format .= $symbols[ $symbol_key ];
827
828
					$i ++;
829
				} elseif ( isset( $symbols[ $char ] ) ) {
830
					$new_format .= $symbols[ $char ];
831
				} else {
832
					$new_format .= $char;
833
				}
834
			}//end if
835
		}//end for
836
837
		return $new_format;
838
	}
839
840
	/**
841
	 * Enqueue the i18n files for jquery date/timepicker
842
	 *
843
	 * @since  2.7
844
	 */
845
	public function enqueue_jquery_ui_i18n() {
846
847
		static $done = array();
848
849
		$types = array();
850
851
		switch ( static::$type ) {
852
			case 'time':
853
				$types[] = 'time';
854
855
				break;
856
			case 'date':
857
				$types[] = 'date';
858
859
				break;
860
			case 'datetime':
861
				$types[] = 'time';
862
				$types[] = 'date';
863
864
				break;
865
		}
866
867
		if ( in_array( 'date', $types, true ) && ! in_array( 'date', $done, true ) ) {
868
			if ( function_exists( 'wp_localize_jquery_ui_datepicker' ) ) {
869
				wp_localize_jquery_ui_datepicker();
870
			}
871
872
			$done[] = 'date';
873
		}
874
875
		if ( in_array( 'time', $types, true ) && ! in_array( 'time', $done, true ) ) {
876
			$locale = str_replace( '_', '-', get_locale() );
877
878
			// Local files.
879
			if ( ! file_exists( PODS_DIR . 'ui/js/timepicker/i18n/jquery-ui-timepicker-' . $locale . '.js' ) ) {
880
				// Fallback to the base language (non-region specific).
881
				$locale = substr( $locale, 0, strpos( $locale, '-' ) );
882
			}
883
884
			if ( ! wp_script_is( 'jquery-ui-timepicker-i18n-' . $locale, 'registered' ) && file_exists( PODS_DIR . 'ui/js/timepicker/i18n/jquery-ui-timepicker-' . $locale . '.js' ) ) {
885
				wp_enqueue_script( 'jquery-ui-timepicker-i18n-' . $locale, PODS_URL . 'ui/js/timepicker/i18n/jquery-ui-timepicker-' . $locale . '.js', array( 'jquery-ui-timepicker' ), '1.6.3' );
886
			}
887
888
			$done[] = 'time';
889
		}
890
	}
891
}
892