Completed
Push — master ( 2a60ef...645aad )
by Zack
53:50 queued 48:17
created

GravityView_Field_Time::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 10
ccs 0
cts 6
cp 0
crap 2
rs 9.9332
c 0
b 0
f 0
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 $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 71
	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 71
		if( ! empty( $criteria['sorting']['key'] ) ) {
107
108 8
			$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 8
			if( ! empty( $pieces[1] ) ) {
115
116
				// Pass these to the _modify_query_sort_by_time_hack() method
117 1
				$this->_time_format = $pieces[1];
118 1
				$this->_date_format = $pieces[2];
119
120
				// Remove fake input IDs (5.1 doesn't exist. Use 5)
121 1
				$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 1
				$criteria['sorting']['is_numeric'] = true;
128
129
				// Modify the Gravity Forms WP Query
130 1
				add_filter('query', array( $this, '_modify_query_sort_by_time_hack' ) );
131
			}
132
		}
133
134 71
		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 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...
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 2
		if( strpos( $query, self::GF_SORTING_SQL ) > 0 ) {
154
155
			if( $this->_time_format === '24' ) {
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 2
		return $query;
193
	}
194
195
196
	public function field_options( $field_options, $template_id, $field_id, $context, $input_type, $form_id ) {
197
198
		// Set variables
199
		parent::field_options( $field_options, $template_id, $field_id, $context, $input_type, $form_id );
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' ) );
209
210
		$this->add_field_support('date_display', $field_options );
211
212
		remove_filter('gravityview_date_format', array( $this, '_filter_date_display_date_format' ) );
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;
226
227
		$current_form = isset( $_POST['form_id'] ) ? intval( $_POST['form_id'] ) : gravityview_get_form_id( $post->ID );
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 1
	static public function _get_time_format_for_field( $field_id, $form_id = 0 ) {
242
243
		// GF defaults to 12, so should we.
244 1
		$time_format = '12';
245
246 1
		if( $form_id ) {
247 1
			$form = GFAPI::get_form( $form_id );
248
249 1
			if ( $form ) {
250 1
				$field = GFFormsModel::get_field( $form, floor( $field_id ) );
251 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...
252 1
					$field->sanitize_settings(); // Make sure time is set
253 1
					$time_format = $field->timeFormat;
254
				}
255
			}
256
		}
257
258 1
		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 2
	static public function date_format( $time_format = '12', $field_id = 0 ) {
287
288 2
		$field_input_id = gravityview_get_input_id_from_id( $field_id );
289
290 2
		$default = 'h:i A';
291
292
		// This doesn't take into account 24-hour
293
		switch( $field_input_id ) {
294
			// Hours
295 2
			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 2
			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 2
			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 2
			case 0:
308 2
				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