Completed
Push — develop ( 107abd...28890b )
by Zack
07:51
created

GravityView_Field_Time   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 307
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 50.62%

Importance

Changes 0
Metric Value
dl 0
loc 307
ccs 41
cts 81
cp 0.5062
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
A _maybe_filter_gravity_forms_query() 0 35 3
B _modify_query_sort_by_time_hack() 0 48 8
A field_options() 0 20 2
A _get_time_format() 0 7 2
A _get_time_format_for_field() 0 19 5
A _filter_date_display_date_format() 0 7 1
B date_format() 0 28 7
1
<?php
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';
14
15
	var $is_searchable = true;
16
17
	var $search_operators = array( 'is', 'isnot', 'greater_than', 'less_than' );
18
19
	/** @see GF_Field_Time */
20
	var $_gf_field_class_name = 'GF_Field_Time';
21
22
	var $group = 'advanced';
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;
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 );
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 1
	public function modify_sort_id( $sort_field_id, $form_id ) {
80
81 1
		$time_format = self::_get_time_format_for_field( $sort_field_id, $form_id );
82
83 1
		$date_format = self::date_format( $time_format, $sort_field_id );
84
85
		// Should look something like `5|:time:|12|:time:|h:i A`
86 1
		$new_sort_field_id = implode( $this->_sort_divider, array( $sort_field_id, $time_format, $date_format ) );
87
88 1
		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 array $criteria If a match, the sorting will be updated to set `is_numeric` to true and make sure the field ID is an int
102
	 */
103 74
	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 not being sorted, return early
106 74
		if( empty( $criteria['sorting']['key'] ) ) {
107 74
			return $criteria;
108
		}
109
110 8
		$pieces = explode( $this->_sort_divider, $criteria['sorting']['key'] );
111
112
		/**
113
		 * If the sort key does not match the key set in modify_sort_id(), do not modify the Gravity Forms query SQL
114
		 * @see modify_sort_id()
115
		 */
116 8
		if( empty( $pieces[1] ) ) {
117 7
			return $criteria;
118
		}
119
120
		// Pass these to the _modify_query_sort_by_time_hack() method
121 1
		$this->_time_format = $pieces[1];
122 1
		$this->_date_format = $pieces[2];
123
124
		// Remove fake input IDs (5.1 doesn't exist. Use 5)
125 1
		$criteria['sorting']['key'] = floor( $pieces[0] );
126
127
		/**
128
		 * Make sure sorting is numeric (# of seconds). IMPORTANT.
129
		 * @see GVCommon::is_field_numeric() is_numeric should also be set here
130
		 */
131 1
		$criteria['sorting']['is_numeric'] = true;
132
133
		// Modify the Gravity Forms WP Query
134 1
		add_filter('query', array( $this, '_modify_query_sort_by_time_hack' ) );
135
136 1
		return $criteria;
137
	}
138
139
	/**
140
	 * Modify Gravity Forms query SQL to convert times to numbers
141
	 * Gravity Forms couldn't sort by time...until NOW
142
	 *
143
	 * @since 1.14
144
	 * @param string $query MySQL query
145
	 *
146
	 * @return string Modified query, if the query matches the expected Gravity Forms SQL string used for sorting time fields. Otherwise, original query.
147
	 */
148 2
	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...
149
150
		/**
151
		 * If this is a Gravity Forms entry selection sorting query, generated by sort_by_field_query(),
152
		 * then we want to modify the query.
153
		 * @see GFFormsModel::sort_by_field_query()
154
		 */
155 2
		if( strpos( $query, self::GF_SORTING_SQL ) > 0 ) {
156
157
			if( $this->_time_format === '24' ) {
158
				$sql_str_to_date = "STR_TO_DATE( `value`, '%H:%i' )";
159
			} else {
160
				$sql_str_to_date = "STR_TO_DATE( `value`, '%h:%i %p' )";
161
			}
162
163
			switch ( $this->_date_format ) {
164
				case 'h':
165
				case 'H':
166
					$modification = "TIME_FORMAT( {$sql_str_to_date}, '%H' )";
167
					break;
168
				case 'i':
169
					$modification = "TIME_FORMAT( {$sql_str_to_date}, '%i' )";
170
					break;
171
				case 'H:i':
172
				case 'h:i A':
173
				default:
174
					$modification = "TIME_TO_SEC( {$sql_str_to_date} )";
175
			}
176
177
			/**
178
			 * Convert the time (12:30 pm) to the MySQL `TIME_TO_SEC()` value for that time (45000)
179
			 * This way, Gravity Forms is able to sort numerically.
180
			 */
181
			$replacement_query = str_replace( 'value', "{$modification} as value", self::GF_SORTING_SQL );
182
183
			/**
184
			 * Replace it in the main query
185
			 */
186
			$query = str_replace( self::GF_SORTING_SQL, $replacement_query, $query );
187
188
			/**
189
			 * REMOVE the Gravity Forms WP Query modifications!
190
			 */
191
			remove_filter( 'query', array( $this, '_modify_query_sort_by_time_hack' ) );
192
		}
193
194 2
		return $query;
195
	}
196
197
198
	public function field_options( $field_options, $template_id, $field_id, $context, $input_type, $form_id ) {
199
200
		// Set variables
201
		parent::field_options( $field_options, $template_id, $field_id, $context, $input_type, $form_id );
202
203
		if( 'edit' === $context ) {
204
			return $field_options;
205
		}
206
207
		/**
208
		 * Set default date format based on field ID and Form ID
209
		 */
210
		add_filter('gravityview_date_format', array( $this, '_filter_date_display_date_format' ) );
211
212
		$this->add_field_support('date_display', $field_options );
213
214
		remove_filter('gravityview_date_format', array( $this, '_filter_date_display_date_format' ) );
215
216
		return $field_options;
217
	}
218
219
	/**
220
	 * Return the field's time format by fetching the form ID and checking the field settings
221
	 *
222
	 * @since 1.14
223
	 *
224
	 * @return string Either "12" or "24". "12" is default.
225
	 */
226
	private function _get_time_format() {
227
		global $post;
228
229
		$current_form = isset( $_POST['form_id'] ) ? intval( $_POST['form_id'] ) : gravityview_get_form_id( $post->ID );
230
231
		return self::_get_time_format_for_field( $this->_field_id, $current_form );
232
	}
233
234
	/**
235
	 * Return the field's time format by fetching the form ID and checking the field settings
236
	 *
237
	 * @since 1.14
238
	 *
239
	 * @param string $field_id ID for Gravity Forms time field
240
	 * @param int $form_id ID for Gravity Forms form
241
	 * @return string Either "12" or "24". "12" is default.
242
	 */
243 1
	static public function _get_time_format_for_field( $field_id, $form_id = 0 ) {
244
245
		// GF defaults to 12, so should we.
246 1
		$time_format = '12';
247
248 1
		if( $form_id ) {
249 1
			$form = GFAPI::get_form( $form_id );
250
251 1
			if ( $form ) {
252 1
				$field = GFFormsModel::get_field( $form, floor( $field_id ) );
253 1
				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...
254 1
					$field->sanitize_settings(); // Make sure time is set
255 1
					$time_format = $field->timeFormat;
256
				}
257
			}
258
		}
259
260 1
		return $time_format;
261
	}
262
263
	/**
264
	 * Modify the default PHP date formats used by the time field based on the field IDs and the field settings
265
	 *
266
	 * @since 1.14
267
	 *
268
	 * @return string PHP date() format text to to display the correctly formatted time value for the newly created field
269
	 */
270
	public function _filter_date_display_date_format() {
271
272
		$time_format = $this->_get_time_format();
273
		$field_id = $this->_field_id;
274
275
		return self::date_format( $time_format, $field_id );
276
	}
277
278
	/**
279
	 * Get the default date format for a field based on the field ID and the time format setting
280
	 *
281
	 * @since 1.14
282
283
	 * @param string $time_format The time format ("12" or "24"). Default: "12" {@since 1.14}
284
	 * @param int $field_id The ID of the field. Used to figure out full time/hours/minutes/am/pm {@since 1.14}
285
	 *
286
	 * @return string PHP date format for the time
287
	 */
288 2
	static public function date_format( $time_format = '12', $field_id = 0 ) {
289
290 2
		$field_input_id = gravityview_get_input_id_from_id( $field_id );
291
292 2
		$default = 'h:i A';
293
294
		// This doesn't take into account 24-hour
295
		switch( $field_input_id ) {
296
			// Hours
297 2
			case 1:
298 1
				return ( $time_format === '12' ) ? 'h' : 'H';
299
				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...
300
			// Minutes
301 2
			case 2:
302 1
				return 'i';
303
				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...
304
			// AM/PM
305 2
			case 3:
306 1
				return 'A';
307
				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...
308
			// Full time field
309 2
			case 0:
310 2
				return ( $time_format === '12' ) ? $default : 'H:i';
311
				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...
312
		}
313
314
		return $default;
315
	}
316
317
}
318
319
new GravityView_Field_Time;
320