Issues (2873)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

classes/fields/datetime.php (15 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
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
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
404
			// Try field format.
405
			$date_local = $this->createFromFormat( $format, (string) $value );
406
407
			if ( $date instanceof DateTime ) {
408
				$value = $date->format( $format );
409
			} elseif ( $date_local instanceof DateTime ) {
410
				$value = $date_local->format( $format );
411
			} else {
412
				$value = date_i18n( $format, strtotime( (string) $value ) );
413
			}
414
		} elseif ( 0 === (int) pods_v( static::$type . '_allow_empty', $options, 1 ) ) {
415
			$value = date_i18n( $format );
416
		} else {
417
			$value = '';
418
		}
419
420
		return $value;
421
	}
422
423
	/**
424
	 * Build date and/or time format string based on options
425
	 *
426
	 * @since  2.7
427
	 *
428
	 * @param  array $options Field options.
429
	 * @param  bool  $js      Whether to return format for jQuery UI.
430
	 *
431
	 * @return string
432
	 */
433
	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...
434
435
		$format = $this->format_date( $options, $js );
436
437
		$type = pods_v( static::$type . '_type', $options, 'format' );
438
439
		if ( 'format' !== $type || 'c' !== pods_v( static::$type . '_format', $options, '' ) ) {
440
			$format .= ' ' . $this->format_time( $options, $js );
441
		}
442
443
		return $format;
444
	}
445
446
	/**
447
	 * Build date format string based on options
448
	 *
449
	 * @since  2.7
450
	 *
451
	 * @param  array $options Field options.
452
	 * @param  bool  $js      Whether to return format for jQuery UI.
453
	 *
454
	 * @return string
455
	 */
456
	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...
457
458
		switch ( (string) pods_v( static::$type . '_type', $options, 'format', true ) ) {
459
			case 'wp':
460
				$format = get_option( 'date_format' );
461
				if ( $js ) {
462
					$format = $this->convert_format( $format, array( 'source' => 'php' ) );
463
				}
464
				break;
465
			case 'custom':
466
				if ( ! $js ) {
467
					$format = pods_v( static::$type . '_format_custom', $options, '' );
468
				} else {
469
					$format = pods_v( static::$type . '_format_custom_js', $options, '' );
470
					if ( empty( $format ) ) {
471
						$format = pods_v( static::$type . '_format_custom', $options, '' );
472
						$format = $this->convert_format( $format, array( 'source' => 'php' ) );
473
					}
474
				}
475
				break;
476
			default:
477
				$date_format = $this->get_date_formats( $js );
478
				$format      = $date_format[ pods_v( static::$type . '_format', $options, 'ymd_dash', true ) ];
479
				break;
480
		}//end switch
481
482
		return $format;
483
	}
484
485
	/**
486
	 * Build time format string based on options
487
	 *
488
	 * @since  2.7
489
	 *
490
	 * @param  array $options Field options.
491
	 * @param  bool  $js      Whether to return format for jQuery UI.
492
	 *
493
	 * @return string
494
	 */
495
	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...
496
497
		switch ( (string) pods_v( static::$type . '_time_type', $options, '12', true ) ) {
498
			case '12':
499
				$time_format = $this->get_time_formats( $js );
500
501
				$format = $time_format[ pods_v( static::$type . '_time_format', $options, 'hh_mm', true ) ];
502
503
				break;
504
			case '24':
505
				$time_format_24 = $this->get_time_formats_24( $js );
506
507
				$format = $time_format_24[ pods_v( static::$type . '_time_format_24', $options, 'hh_mm', true ) ];
508
509
				break;
510
			case 'custom':
511
				if ( ! $js ) {
512
					$format = pods_v( static::$type . '_time_format_custom', $options, '' );
513
				} else {
514
					$format = pods_v( static::$type . '_time_format_custom_js', $options, '' );
515
516
					if ( empty( $format ) ) {
517
						$format = pods_v( static::$type . '_time_format_custom', $options, '' );
518
						$format = $this->convert_format( $format, array( 'source' => 'php' ) );
519
					}
520
				}
521
522
				break;
523
			default:
524
				$format = get_option( 'time_format' );
525
526
				if ( $js ) {
527
					$format = $this->convert_format( $format, array( 'source' => 'php' ) );
528
				}
529
530
				break;
531
		}//end switch
532
533
		return $format;
534
	}
535
536
	/**
537
	 * Get the date formats.
538
	 *
539
	 * @since  2.7
540
	 *
541
	 * @param  bool $js Whether to return format for jQuery UI.
542
	 *
543
	 * @return array
544
	 */
545
	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...
546
547
		$date_format = array(
548
			'mdy'       => 'm/d/Y',
549
			'mdy_dash'  => 'm-d-Y',
550
			'mdy_dot'   => 'm.d.Y',
551
			'dmy'       => 'd/m/Y',
552
			'dmy_dash'  => 'd-m-Y',
553
			'dmy_dot'   => 'd.m.Y',
554
			'ymd_slash' => 'Y/m/d',
555
			'ymd_dash'  => 'Y-m-d',
556
			'ymd_dot'   => 'Y.m.d',
557
			'dMy'       => 'd/M/Y',
558
			'dMy_dash'  => 'd-M-Y',
559
			'fjy'       => 'F j, Y',
560
			'fjsy'      => 'F jS, Y',
561
			'y'         => 'Y',
562
		);
563
564
		$filter = 'pods_form_ui_field_date_formats';
565
566
		if ( $js ) {
567
			// @todo Method parameters? (Not supported by array_map)
568
			$date_format = array_map( array( $this, 'convert_format' ), $date_format );
569
570
			$filter = 'pods_form_ui_field_date_js_formats';
571
		}
572
573
		return apply_filters( $filter, $date_format );
574
	}
575
576
	/**
577
	 * Get the time formats.
578
	 *
579
	 * @since  2.7
580
	 *
581
	 * @param  bool $js Whether to return format for jQuery UI.
582
	 *
583
	 * @return array
584
	 */
585
	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...
586
587
		$time_format = array(
588
			'h_mm_A'     => 'g:i A',
589
			'h_mm_ss_A'  => 'g:i:s A',
590
			'hh_mm_A'    => 'h:i A',
591
			'hh_mm_ss_A' => 'h:i:s A',
592
			'h_mma'      => 'g:ia',
593
			'hh_mma'     => 'h:ia',
594
			'h_mm'       => 'g:i',
595
			'h_mm_ss'    => 'g:i:s',
596
			'hh_mm'      => 'h:i',
597
			'hh_mm_ss'   => 'h:i:s',
598
		);
599
600
		$filter = 'pods_form_ui_field_time_formats';
601
602
		if ( $js ) {
603
			// @todo Method parameters? (Not supported by array_map)
604
			$time_format = array_map( array( $this, 'convert_format' ), $time_format );
605
606
			$filter = 'pods_form_ui_field_time_js_formats';
607
		}
608
609
		return apply_filters( $filter, $time_format );
610
	}
611
612
	/**
613
	 * Get the time formats.
614
	 *
615
	 * @since  2.7
616
	 *
617
	 * @param  bool $js Whether to return format for jQuery UI.
618
	 *
619
	 * @return array
620
	 */
621
	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...
622
623
		$time_format_24 = array(
624
			'hh_mm'    => 'H:i',
625
			'hh_mm_ss' => 'H:i:s',
626
		);
627
628
		$filter = 'pods_form_ui_field_time_formats_24';
629
630
		if ( $js ) {
631
			// @todo Method parameters? (Not supported by array_map)
632
			$time_format_24 = array_map( array( $this, 'convert_format' ), $time_format_24 );
633
634
			$filter         = 'pods_form_ui_field_time_js_formats_24';
635
		}
636
637
		return apply_filters( $filter, $time_format_24 );
638
	}
639
640
	/**
641
	 * PHP backwards compatibility for createFromFormat.
642
	 *
643
	 * @param string  $format           Format string.
644
	 * @param string  $date             Defaults to time() if empty.
645
	 * @param boolean $return_timestamp Whether to return the strtotime() or createFromFormat result or not.
646
	 *
647
	 * @return DateTime|null|int|false
648
	 */
649
	public function createFromFormat( $format, $date, $return_timestamp = false ) {
0 ignored issues
show
The function name createFromFormat is in camel caps, but expected create_from_format instead as per the coding standard.
Loading history...
650
651
		$datetime = null;
652
653
		try {
654
			if ( method_exists( 'DateTime', 'createFromFormat' ) ) {
655
				$timezone = get_option( 'timezone_string' );
656
657
				if ( empty( $timezone ) ) {
658
					$timezone = timezone_name_from_abbr( '', get_option( 'gmt_offset' ) * HOUR_IN_SECONDS, 0 );
659
				}
660
661
				if ( ! empty( $timezone ) ) {
662
					$datetimezone = new DateTimeZone( $timezone );
663
664
					$datetime = DateTime::createFromFormat( $format, (string) $date, $datetimezone );
665
666
					if ( false === $datetime ) {
667
						$datetime = DateTime::createFromFormat( static::$storage_format, (string) $date, $datetimezone );
668
					}
669
670
					if ( false !== $datetime && $return_timestamp ) {
671
						return $datetime;
672
					}
673
				}
674
			}//end if
675
676
			if ( in_array( $datetime, array( null, false ), true ) ) {
677
				if ( empty( $date ) ) {
678
					$timestamp = time();
679
				} else {
680
					$timestamp = strtotime( (string) $date );
681
682
					if ( $return_timestamp ) {
683
						return $timestamp;
684
					}
685
				}
686
687
				if ( $timestamp ) {
688
					$datetime = new DateTime( date_i18n( static::$storage_format, $timestamp ) );
689
				}
690
			}
691
		} catch ( Exception $e ) {
692
			// There is no saving this time value, it's an exception to the rule.
693
		}
694
695
		return apply_filters( 'pods_form_ui_field_datetime_formatter', $datetime, $format, $date );
696
	}
697
698
	/**
699
	 * Convert a date from one format to another.
700
	 *
701
	 * @param string  $value            Field value.
702
	 * @param string  $new_format       New format string.
703
	 * @param string  $original_format  Original format string (if known).
704
	 * @param boolean $return_timestamp Whether to return the strtotime() or createFromFormat result or not.
705
	 *
706
	 * @return string|int|boolean|DateTime
707
	 */
708
	public function convert_date( $value, $new_format, $original_format = '', $return_timestamp = false ) {
709
710
		if ( empty( $original_format ) ) {
711
			$original_format = static::$storage_format;
712
		}
713
714
		$date = '';
715
716
		if ( ! empty( $value ) && ! in_array( $value, array( '0000-00-00', '0000-00-00 00:00:00', '00:00:00' ), true ) ) {
717
			$date = $this->createFromFormat( $original_format, (string) $value, $return_timestamp );
718
719
			if ( $date instanceof DateTime ) {
720
				$value = $date->format( $new_format );
721
			} elseif ( false !== $date ) {
722
				$date = strtotime( (string) $value );
723
724
				$value = date_i18n( $new_format, $date );
725
			}
726
		} else {
727
			$value = date_i18n( $new_format );
728
		}
729
730
		// Return timestamp conversion result instead
731
		if ( $return_timestamp ) {
732
			return $date;
733
		}
734
735
		return $value;
736
	}
737
738
	/**
739
	 * Matches each symbol of PHP date format standard with jQuery equivalent codeword.
740
	 *
741
	 * @link   http://stackoverflow.com/questions/16702398/convert-a-php-date-format-to-a-jqueryui-datepicker-date-format
742
	 * @link   https://api.jqueryui.com/datepicker/
743
	 * @link   http://trentrichardson.com/examples/timepicker/
744
	 *
745
	 * @since  2.7
746
	 *
747
	 * @param  string $source_format Source format string.
748
	 * @param  array  $args          Format arguments.
749
	 *
750
	 * @return string
751
	 */
752
	public function convert_format( $source_format, $args = array() ) {
753
754
		// @todo Improve source/target logic.
755
		$args = array_merge(
756
			array(
757
				'source' => 'php',
758
			// 'jquery_ui' for reverse.
759
			), $args
760
		);
761
762
		// Keep keys and values sorted by string length.
763
		$symbols = array(
764
			// Day
765
			'd' => 'dd',
766
			'l' => 'DD',
767
			'D' => 'D',
768
			'j' => 'd',
769
			'N' => '',
770
			'S' => '',
771
			'w' => '',
772
			'z' => 'o',
773
			// Week
774
			'W' => '',
775
			// Month
776
			'F' => 'MM',
777
			'm' => 'mm',
778
			'M' => 'M',
779
			'n' => 'm',
780
			't' => '',
781
			// Year
782
			'L' => '',
783
			'o' => '',
784
			'Y' => 'yy',
785
			'y' => 'y',
786
			// AM/PM
787
			'a' => 'tt',
788
			'A' => 'TT',
789
			// Swatch internet time (not supported)
790
			'B' => '',
791
			// Hour
792
			'h' => 'hh',
793
			'H' => 'HH',
794
			'g' => 'h',
795
			'G' => 'H',
796
			// Minute
797
			'i' => 'mm',
798
			// Second
799
			's' => 'ss',
800
			// Microsecond
801
			'u' => 'c',
802
		);
803
804
		if ( version_compare( PHP_VERSION, '7.0.0' ) >= 0 ) {
805
			// Millisecond
806
			$symbols['v'] = 'l';
807
		}
808
809
		if ( 'jquery_ui' === $args['source'] ) {
810
			// Remove empty values.
811
			$symbols = array_filter( $symbols );
812
			$symbols = array_flip( $symbols );
813
		}
814
815
		$new_format = '';
816
		$escaping   = false;
817
818
		$source_format_length = strlen( $source_format );
819
820
		for ( $i = 0; $i < $source_format_length; $i ++ ) {
821
			$char = $source_format[ $i ];
822
823
			// PHP date format escaping character
824
			// @todo Do we want to support non-format characters?
825
			if ( '\\' === $char ) {
826
				$i ++;
827
828
				if ( $escaping ) {
829
					$new_format .= $source_format[ $i ];
830
				} else {
831
					$new_format .= '\'' . $source_format[ $i ];
832
				}
833
834
				$escaping = true;
835
			} else {
836
				if ( $escaping ) {
837
					$new_format .= "'";
838
					$escaping    = false;
839
				}
840
841
				$symbol_key = false;
842
843
				if ( isset( $source_format[ $i + 1 ] ) ) {
844
					$symbol_key = $char . $source_format[ $i + 1 ];
845
				}
846
847
				// Support 2 characters.
848
				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...
849
					$new_format .= $symbols[ $symbol_key ];
850
851
					$i ++;
852
				} elseif ( isset( $symbols[ $char ] ) ) {
853
					$new_format .= $symbols[ $char ];
854
				} else {
855
					$new_format .= $char;
856
				}
857
			}//end if
858
		}//end for
859
860
		return $new_format;
861
	}
862
863
	/**
864
	 * Enqueue the i18n files for jquery date/timepicker
865
	 *
866
	 * @since  2.7
867
	 */
868
	public function enqueue_jquery_ui_i18n() {
869
870
		static $done = array();
871
872
		$types = array();
873
874
		switch ( static::$type ) {
875
			case 'time':
876
				$types[] = 'time';
877
878
				break;
879
			case 'date':
880
				$types[] = 'date';
881
882
				break;
883
			case 'datetime':
884
				$types[] = 'time';
885
				$types[] = 'date';
886
887
				break;
888
		}
889
890
		if ( in_array( 'date', $types, true ) && ! in_array( 'date', $done, true ) ) {
891
			if ( function_exists( 'wp_localize_jquery_ui_datepicker' ) ) {
892
				wp_localize_jquery_ui_datepicker();
893
			}
894
895
			$done[] = 'date';
896
		}
897
898
		if ( in_array( 'time', $types, true ) && ! in_array( 'time', $done, true ) ) {
899
			$locale = str_replace( '_', '-', get_locale() );
900
901
			// Local files.
902
			if ( ! file_exists( PODS_DIR . 'ui/js/timepicker/i18n/jquery-ui-timepicker-' . $locale . '.js' ) ) {
903
				// Fallback to the base language (non-region specific).
904
				$locale = substr( $locale, 0, strpos( $locale, '-' ) );
905
			}
906
907
			if ( ! wp_script_is( 'jquery-ui-timepicker-i18n-' . $locale, 'registered' ) && file_exists( PODS_DIR . 'ui/js/timepicker/i18n/jquery-ui-timepicker-' . $locale . '.js' ) ) {
908
				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' );
909
			}
910
911
			$done[] = 'time';
912
		}
913
	}
914
}
915