Completed
Push — master ( cd320e...b9ab02 )
by Zack
15s
created

GravityView_Field_Time   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 305
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 20.25%

Importance

Changes 0
Metric Value
dl 0
loc 305
ccs 16
cts 79
cp 0.2025
rs 10
c 0
b 0
f 0
wmc 30
lcom 1
cbo 1

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
A modify_sort_id() 0 11 1
C _modify_query_sort_by_time_hack() 0 48 8
A field_options() 0 20 2
A _get_time_format() 0 7 2
B _get_time_format_for_field() 0 19 5
A _filter_date_display_date_format() 0 7 1
C date_format() 0 28 7
B _maybe_filter_gravity_forms_query() 0 33 3
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 11 and the first side effect is on line 317.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * @file class-gravityview-field-time.php
4
 * @package GravityView
5
 * @subpackage includes\fields
6
 */
7
8
/**
9
 * Add custom options for date fields
10
 */
11
class GravityView_Field_Time extends GravityView_Field {
12
13
	var $name = 'time';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $name.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
14
15
	var $is_searchable = true;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $is_searchable.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
16
17
	var $search_operators = array( 'is', 'isnot', 'greater_than', 'less_than' );
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $search_operators.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
18
19
	/** @see GF_Field_Time */
20
	var $_gf_field_class_name = 'GF_Field_Time';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $_gf_field_class_name.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
21
22
	var $group = 'advanced';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $group.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
23
24
	/**
25
	 * @internal Do not define. This is overridden by the class using a filter.
26
	 * @todo Fix using variable for time field
27
	 */
28
	var $is_numeric;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $is_numeric.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
29
30
	/**
31
	 * @var string The part of the Gravity Forms query that's modified to enable sorting by time. `value` gets replaced.
32
	 * @since 1.14
33
	 */
34
	const GF_SORTING_SQL = 'SELECT 0 as query, lead_id as id, value';
35
36
	/**
37
	 * @var string Used to implode and explode the custom sort key for query modification.
38
	 * @since 1.14
39
	 */
40
	private $_sort_divider = '|:time:|';
41
42
	/**
43
	 * @var string Used to store the time format for the field ("12" or "24") so it can be used in the query filter
44
	 * @since 1.14
45
	 */
46
	private $_time_format = null;
47
48
	/**
49
	 * @var string Used to store the date format for the field, based on the input being displayed, so it can be used in the query filter
50
	 * @since 1.14
51
	 */
52
	private $_date_format = null;
53
54
	/**
55
	 * GravityView_Field_Time constructor.
56
	 */
57
	public function __construct() {
58
59
		$this->label = esc_html__( 'Time', 'gravityview' );
60
61
		parent::__construct();
62
63
		add_filter( 'gravityview/sorting/time', array( $this, 'modify_sort_id' ), 10, 2 );
64
65
		add_filter('gravityview_search_criteria', array( $this, '_maybe_filter_gravity_forms_query' ), 10, 4 );
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
66
	}
67
68
	/**
69
	 * Modify the sort key for the time field so it can be parsed by the query filter
70
	 *
71
	 * @see _modify_query_sort_by_time_hack
72
	 *
73
	 * @since 1.14
74
	 * @param string $sort_field_id Existing sort field ID (like "5")
75
	 * @param int $form_id Gravity Forms Form ID being sorted
76
	 *
77
	 * @return string Modified sort key imploded with $_sort_divider, like `5|:time:|12|:time:|h:i A`
78
	 */
79
	public function modify_sort_id( $sort_field_id, $form_id ) {
80
81
		$time_format = self::_get_time_format_for_field( $sort_field_id, $form_id );
82
83
		$date_format = self::date_format( $time_format, $sort_field_id );
84
85
		// Should look something like `5|:time:|12|:time:|h:i A`
86
		$new_sort_field_id = implode( $this->_sort_divider, array( $sort_field_id, $time_format, $date_format ) );
87
88
		return $new_sort_field_id;
89
	}
90
91
	/**
92
	 * If the sorting key matches the key set in modify_sort_id(), then modify the Gravity Forms query SQL
93
	 *
94
	 * @since 1.14
95
	 * @see modify_sort_id()
96
	 *
97
	 * @param array $criteria Search criteria used by GravityView
98
	 * @param array $form_ids Forms to search
99
	 * @param int $view_id ID of the view being used to search
100
	 *
101
	 * @return $criteria If a match, the sorting will be updated to set `is_numeric` to true and make sure the field ID is an int
0 ignored issues
show
Documentation introduced by
The doc-type $criteria could not be parsed: Unknown type name "$criteria" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
102
	 */
103 10
	public function _maybe_filter_gravity_forms_query( $criteria, $form_ids, $view_id ) {
0 ignored issues
show
Unused Code introduced by
The parameter $form_ids is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $view_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
104
105
		// If the search is being sorted
106 10
		if( ! empty( $criteria['sorting']['key'] ) ) {
107
108 2
			$pieces = explode( $this->_sort_divider, $criteria['sorting']['key'] );
109
110
			/**
111
			 * And the sort key matches the key set in modify_sort_id(), then modify the Gravity Forms query SQL
112
			 * @see modify_sort_id()
113
			 */
114 2
			if( ! empty( $pieces[1] ) ) {
115
116
				// Pass these to the _modify_query_sort_by_time_hack() method
117
				$this->_time_format = $pieces[1];
118
				$this->_date_format = $pieces[2];
119
120
				// Remove fake input IDs (5.1 doesn't exist. Use 5)
121
				$criteria['sorting']['key'] = floor( $pieces[0] );
122
123
				/**
124
				 * Make sure sorting is numeric (# of seconds). IMPORTANT.
125
				 * @see GVCommon::is_field_numeric() is_numeric should also be set here
126
				 */
127
				$criteria['sorting']['is_numeric'] = true;
128
129
				// Modify the Gravity Forms WP Query
130
				add_filter('query', array( $this, '_modify_query_sort_by_time_hack' ) );
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
131
			}
132
		}
133
134 10
		return $criteria;
135
	}
136
137
	/**
138
	 * Modify Gravity Forms query SQL to convert times to numbers
139
	 * Gravity Forms couldn't sort by time...until NOW
140
	 *
141
	 * @since 1.14
142
	 * @param string $query MySQL query
143
	 *
144
	 * @return string Modified query, if the query matches the expected Gravity Forms SQL string used for sorting time fields. Otherwise, original query.
145
	 */
146
	function _modify_query_sort_by_time_hack( $query ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
147
148
		/**
149
		 * If this is a Gravity Forms entry selection sorting query, generated by sort_by_field_query(),
150
		 * then we want to modify the query.
151
		 * @see GFFormsModel::sort_by_field_query()
152
		 */
153
		if( strpos( $query, self::GF_SORTING_SQL ) > 0 ) {
154
155
			if( $this->_time_format === '24' ) {
0 ignored issues
show
introduced by
Found "=== '". Use Yoda Condition checks, you must
Loading history...
156
				$sql_str_to_date = "STR_TO_DATE( `value`, '%H:%i' )";
157
			} else {
158
				$sql_str_to_date = "STR_TO_DATE( `value`, '%h:%i %p' )";
159
			}
160
161
			switch ( $this->_date_format ) {
162
				case 'h':
163
				case 'H':
164
					$modification = "TIME_FORMAT( {$sql_str_to_date}, '%H' )";
165
					break;
166
				case 'i':
167
					$modification = "TIME_FORMAT( {$sql_str_to_date}, '%i' )";
168
					break;
169
				case 'H:i':
170
				case 'h:i A':
171
				default:
172
					$modification = "TIME_TO_SEC( {$sql_str_to_date} )";
173
			}
174
175
			/**
176
			 * Convert the time (12:30 pm) to the MySQL `TIME_TO_SEC()` value for that time (45000)
177
			 * This way, Gravity Forms is able to sort numerically.
178
			 */
179
			$replacement_query = str_replace( 'value', "{$modification} as value", self::GF_SORTING_SQL );
180
181
			/**
182
			 * Replace it in the main query
183
			 */
184
			$query = str_replace( self::GF_SORTING_SQL, $replacement_query, $query );
185
186
			/**
187
			 * REMOVE the Gravity Forms WP Query modifications!
188
			 */
189
			remove_filter( 'query', array( $this, '_modify_query_sort_by_time_hack' ) );
190
		}
191
192
		return $query;
193
	}
194
195
196
	function field_options( $field_options, $template_id = '', $field_id = '', $context = '', $input_type = '' ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
197
198
		// Set variables
199
		parent::field_options( $field_options, $template_id, $field_id, $context, $input_type );
200
201
		if( 'edit' === $context ) {
202
			return $field_options;
203
		}
204
205
		/**
206
		 * Set default date format based on field ID and Form ID
207
		 */
208
		add_filter('gravityview_date_format', array( $this, '_filter_date_display_date_format' ) );
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
209
210
		$this->add_field_support('date_display', $field_options );
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
211
212
		remove_filter('gravityview_date_format', array( $this, '_filter_date_display_date_format' ) );
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
213
214
		return $field_options;
215
	}
216
217
	/**
218
	 * Return the field's time format by fetching the form ID and checking the field settings
219
	 *
220
	 * @since 1.14
221
	 *
222
	 * @return string Either "12" or "24". "12" is default.
223
	 */
224
	private function _get_time_format() {
225
		global $post;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
226
227
		$current_form = isset( $_POST['form_id'] ) ? intval( $_POST['form_id'] ) : gravityview_get_form_id( $post->ID );
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
228
229
		return self::_get_time_format_for_field( $this->_field_id, $current_form );
230
	}
231
232
	/**
233
	 * Return the field's time format by fetching the form ID and checking the field settings
234
	 *
235
	 * @since 1.14
236
	 *
237
	 * @param string $field_id ID for Gravity Forms time field
238
	 * @param int $form_id ID for Gravity Forms form
239
	 * @return string Either "12" or "24". "12" is default.
240
	 */
241
	static public function _get_time_format_for_field( $field_id, $form_id = 0 ) {
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
242
243
		// GF defaults to 12, so should we.
244
		$time_format = '12';
245
246
		if( $form_id ) {
247
			$form = GFAPI::get_form( $form_id );
248
249
			if ( $form ) {
250
				$field = GFFormsModel::get_field( $form, floor( $field_id ) );
251
				if ( $field && $field instanceof GF_Field_Time ) {
0 ignored issues
show
Bug introduced by
The class GF_Field_Time does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
252
					$field->sanitize_settings(); // Make sure time is set
253
					$time_format = $field->timeFormat;
254
				}
255
			}
256
		}
257
258
		return $time_format;
259
	}
260
261
	/**
262
	 * Modify the default PHP date formats used by the time field based on the field IDs and the field settings
263
	 *
264
	 * @since 1.14
265
	 *
266
	 * @return string PHP date() format text to to display the correctly formatted time value for the newly created field
267
	 */
268
	public function _filter_date_display_date_format() {
269
270
		$time_format = $this->_get_time_format();
271
		$field_id = $this->_field_id;
272
273
		return self::date_format( $time_format, $field_id );
274
	}
275
276
	/**
277
	 * Get the default date format for a field based on the field ID and the time format setting
278
	 *
279
	 * @since 1.14
280
281
	 * @param string $time_format The time format ("12" or "24"). Default: "12" {@since 1.14}
282
	 * @param int $field_id The ID of the field. Used to figure out full time/hours/minutes/am/pm {@since 1.14}
283
	 *
284
	 * @return string PHP date format for the time
285
	 */
286 1
	static public function date_format( $time_format = '12', $field_id = 0 ) {
0 ignored issues
show
Coding Style introduced by
As per PSR2, the static declaration should come after the visibility declaration.
Loading history...
287
288 1
		$field_input_id = gravityview_get_input_id_from_id( $field_id );
289
290 1
		$default = 'h:i A';
291
292
		// This doesn't take into account 24-hour
293
		switch( $field_input_id ) {
294
			// Hours
295 1
			case 1:
296 1
				return ( $time_format === '12' ) ? 'h' : 'H';
297
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
298
			// Minutes
299 1
			case 2:
300 1
				return 'i';
301
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
302
			// AM/PM
303 1
			case 3:
304 1
				return 'A';
305
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
306
			// Full time field
307 1
			case 0:
308 1
				return ( $time_format === '12' ) ? $default : 'H:i';
309
				break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
310
		}
311
312
		return $default;
313
	}
314
315
}
316
317
new GravityView_Field_Time;
318