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

PodsField_DateTime::format()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 6
nc 2
nop 2
dl 0
loc 12
rs 9.4285
c 0
b 0
f 0
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
		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;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $js. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
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 );
0 ignored issues
show
Bug Best Practice introduced by
The return type of return sprintf(esc_html_...ods'), $label, $value); (string) is incompatible with the return type of the parent method PodsField::validate of type boolean.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
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 ) {
0 ignored issues
show
Documentation introduced by
The return type could not be reliably inferred; please add a @return annotation.

Our type inference engine in quite powerful, but sometimes the code does not provide enough clues to go by. In these cases we request you to add a @return annotation as described here.

Loading history...
333
334
		$js = true;
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $js. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
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
		} 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 ) {
0 ignored issues
show
Coding Style introduced by
This method's name is shorter than the configured minimum length of 3 characters.

Even though PHP does not care about the name of your methods, it is generally a good practice to choose method names which can be easily understood by other human readers.

Loading history...
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;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The expression false; of type false adds false to the return on line 377 which is incompatible with the return type of the parent method PodsField::ui of type string. It seems like you forgot to handle an error condition.
Loading history...
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 ) {
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $js. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
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
			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
		} 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 ) {
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $js. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
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 ) {
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $js. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
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
	public function format_time( $options, $js = false ) {
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $js. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

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 ) {
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $js. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
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 ) {
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $js. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
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 ) {
0 ignored issues
show
Comprehensibility introduced by
Avoid variables with short names like $js. Configured minimum length is 3.

Short variable names may make your code harder to understand. Variable names should be self-descriptive. This check looks for variable names who are shorter than a configured minimum.

Loading history...
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 ) {
0 ignored issues
show
Coding Style introduced by
The function name createFromFormat is in camel caps, but expected create_from_format instead as per the coding standard.
Loading history...
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
			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