Completed
Push — develop ( 613d21...7b2f6e )
by Gennady
17:07
created

add_scripts_and_styles()   B

Complexity

Conditions 6
Paths 9

Size

Total Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 0
Metric Value
cc 6
nc 9
nop 1
dl 0
loc 26
ccs 0
cts 18
cp 0
crap 42
rs 8.8817
c 0
b 0
f 0
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 16 and the first side effect is on line 13.

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
 * The GravityView New Search widget
4
 *
5
 * @package   GravityView-DataTables-Ext
6
 * @license   GPL2+
7
 * @author    Katz Web Services, Inc.
8
 * @link      http://gravityview.co
9
 * @copyright Copyright 2014, Katz Web Services, Inc.
10
 */
11
12
if ( ! defined( 'WPINC' ) ) {
13
	die;
14
}
15
16
class GravityView_Widget_Search extends \GV\Widget {
17
18
	public static $file;
19
	public static $instance;
20
21
	private $search_filters = array();
0 ignored issues
show
Unused Code introduced by
The property $search_filters is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
22
23
	/**
24
	 * whether search method is GET or POST ( default: GET )
25
	 * @since 1.16.4
26
	 * @var string
27
	 */
28
	private $search_method = 'get';
29
30 27
	public function __construct() {
31
32 27
		$this->widget_id = 'search_bar';
33 27
		$this->widget_description = esc_html__( 'Search form for searching entries.', 'gravityview' );
34
35 27
		self::$instance = &$this;
36
37 27
		self::$file = plugin_dir_path( __FILE__ );
38
39 27
		$default_values = array( 'header' => 0, 'footer' => 0 );
40
41
		$settings = array(
42 27
			'search_layout' => array(
43 27
				'type' => 'radio',
44
				'full_width' => true,
45 27
				'label' => esc_html__( 'Search Layout', 'gravityview' ),
46 27
				'value' => 'horizontal',
47
				'options' => array(
48 27
					'horizontal' => esc_html__( 'Horizontal', 'gravityview' ),
49 27
					'vertical' => esc_html__( 'Vertical', 'gravityview' ),
50
				),
51
			),
52
			'search_clear' => array(
53 27
				'type' => 'checkbox',
54 27
				'label' => __( 'Show Clear button', 'gravityview' ),
55
				'value' => false,
56
			),
57
			'search_fields' => array(
58
				'type' => 'hidden',
59
				'label' => '',
60
				'class' => 'gv-search-fields-value',
61
				'value' => '[{"field":"search_all","input":"input_text"}]', // Default: Search Everything text box
62
			),
63
			'search_mode' => array(
64 27
				'type' => 'radio',
65
				'full_width' => true,
66 27
				'label' => esc_html__( 'Search Mode', 'gravityview' ),
67 27
				'desc' => __('Should search results match all search fields, or any?', 'gravityview'),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
68 27
				'value' => 'any',
69 27
				'class' => 'hide-if-js',
70
				'options' => array(
71 27
					'any' => esc_html__( 'Match Any Fields', 'gravityview' ),
72 27
					'all' => esc_html__( 'Match All Fields', 'gravityview' ),
73
				),
74
			),
75
		);
76
77 27
		if ( ! $this->is_registered() ) {
78
			// frontend - filter entries
79
			add_filter( 'gravityview_fe_search_criteria', array( $this, 'filter_entries' ), 10, 3 );
80
81
			// frontend - add template path
82
			add_filter( 'gravityview_template_paths', array( $this, 'add_template_path' ) );
83
84
			// Add hidden fields for "Default" permalink structure
85
			add_filter( 'gravityview_widget_search_filters', array( $this, 'add_no_permalink_fields' ), 10, 3 );
86
87
			// admin - add scripts - run at 1100 to make sure GravityView_Admin_Views::add_scripts_and_styles() runs first at 999
88
			add_action( 'admin_enqueue_scripts', array( $this, 'add_scripts_and_styles' ), 1100 );
89
			add_action( 'wp_enqueue_scripts', array( $this, 'register_scripts') );
0 ignored issues
show
introduced by
No space before closing parenthesis of array is bad style
Loading history...
90
			add_filter( 'gravityview_noconflict_scripts', array( $this, 'register_no_conflict' ) );
91
92
			// ajax - get the searchable fields
93
			add_action( 'wp_ajax_gv_searchable_fields', array( 'GravityView_Widget_Search', 'get_searchable_fields' ) );
94
		}
95
96 27
		parent::__construct( esc_html__( 'Search Bar', 'gravityview' ), null, $default_values, $settings );
97
98
		// calculate the search method (POST / GET)
99 27
		$this->set_search_method();
100 27
	}
101
102
	/**
103
	 * @return GravityView_Widget_Search
104
	 */
105 5
	public static function getInstance() {
0 ignored issues
show
Coding Style introduced by
The function name getInstance is in camel caps, but expected get_instance instead as per the coding standard.
Loading history...
106 5
		if ( empty( self::$instance ) ) {
107
			self::$instance = new GravityView_Widget_Search;
108
		}
109 5
		return self::$instance;
110
	}
111
112
	/**
113
	 * Sets the search method to GET (default) or POST
114
	 * @since 1.16.4
115
	 */
116 27
	private function set_search_method() {
117
		/**
118
		 * @filter `gravityview/search/method` Modify the search form method (GET / POST)
119
		 * @since 1.16.4
120
		 * @param string $search_method Assign an input type according to the form field type. Defaults: `boolean`, `multi`, `select`, `date`, `text`
121
		 * @param string $field_type Gravity Forms field type (also the `name` parameter of GravityView_Field classes)
122
		 */
123 27
		$method = apply_filters( 'gravityview/search/method', $this->search_method );
124
125 27
		$method = strtolower( $method );
126
127 27
		$this->search_method = in_array( $method, array( 'get', 'post' ) ) ? $method : 'get';
128 27
	}
129
130
	/**
131
	 * Returns the search method
132
	 * @since 1.16.4
133
	 * @return string
134
	 */
135 5
	public function get_search_method() {
136 5
		return $this->search_method;
137
	}
138
139
	/**
140
	 * Get the input types available for different field types
141
	 *
142
	 * @since 1.17.5
143
	 *
144
	 * @return array [field type name] => (array|string) search bar input types
145
	 */
146
	public static function get_input_types_by_field_type() {
147
		/**
148
		 * Input Type groups
149
		 * @see admin-search-widget.js (getSelectInput)
150
		 * @var array
151
		 */
152
		$input_types = array(
153
			'text' => array( 'input_text' ),
154
			'address' => array( 'input_text' ),
155
			'number' => array( 'input_text' ),
156
			'date' => array( 'date', 'date_range' ),
157
			'boolean' => array( 'single_checkbox' ),
158
			'select' => array( 'select', 'radio', 'link' ),
159
			'multi' => array( 'select', 'multiselect', 'radio', 'checkbox', 'link' ),
160
		);
161
162
		/**
163
		 * @filter `gravityview/search/input_types` Change the types of search fields available to a field type
164
		 * @see GravityView_Widget_Search::get_search_input_labels() for the available input types
165
		 * @param array $input_types Associative array: key is field `name`, value is array of GravityView input types (note: use `input_text` for `text`)
166
		 */
167
		$input_types = apply_filters( 'gravityview/search/input_types', $input_types );
168
169
		return $input_types;
170
	}
171
172
	/**
173
	 * Get labels for different types of search bar inputs
174
	 *
175
	 * @since 1.17.5
176
	 *
177
	 * @return array [input type] => input type label
178
	 */
179
	public static function get_search_input_labels() {
180
		/**
181
		 * Input Type labels l10n
182
		 * @see admin-search-widget.js (getSelectInput)
183
		 * @var array
184
		 */
185
		$input_labels = array(
186
			'input_text' => esc_html__( 'Text', 'gravityview' ),
187
			'date' => esc_html__( 'Date', 'gravityview' ),
188
			'select' => esc_html__( 'Select', 'gravityview' ),
189
			'multiselect' => esc_html__( 'Select (multiple values)', 'gravityview' ),
190
			'radio' => esc_html__( 'Radio', 'gravityview' ),
191
			'checkbox' => esc_html__( 'Checkbox', 'gravityview' ),
192
			'single_checkbox' => esc_html__( 'Checkbox', 'gravityview' ),
193
			'link' => esc_html__( 'Links', 'gravityview' ),
194
			'date_range' => esc_html__( 'Date range', 'gravityview' ),
195
		);
196
197
		/**
198
		 * @filter `gravityview/search/input_types` Change the label of search field input types
199
		 * @param array $input_types Associative array: key is input type name, value is label
200
		 */
201
		$input_labels = apply_filters( 'gravityview/search/input_labels', $input_labels );
202
203
		return $input_labels;
204
	}
205
206
	public static function get_search_input_label( $input_type ) {
207
		$labels = self::get_search_input_labels();
208
209
		return \GV\Utils::get( $labels, $input_type, false );
210
	}
211
212
	/**
213
	 * Add script to Views edit screen (admin)
214
	 * @param  mixed $hook
215
	 */
216
	public function add_scripts_and_styles( $hook ) {
217
		global $pagenow;
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...
218
219
		// Don't process any scripts below here if it's not a GravityView page or the widgets screen
220
		if ( ! gravityview()->request->is_admin( $hook, 'single' ) && ( 'widgets.php' !== $pagenow ) ) {
0 ignored issues
show
Unused Code introduced by
The call to Request::is_admin() has too many arguments starting with $hook.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
221
			return;
222
		}
223
224
		$script_min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
225
		$script_source = empty( $script_min ) ? '/source' : '';
226
227
		wp_enqueue_script( 'gravityview_searchwidget_admin', plugins_url( 'assets/js'.$script_source.'/admin-search-widget'.$script_min.'.js', __FILE__ ), array( 'jquery', 'gravityview_views_scripts' ), \GV\Plugin::$version );
228
229
		wp_localize_script( 'gravityview_searchwidget_admin', 'gvSearchVar', array(
230
			'nonce' => wp_create_nonce( 'gravityview_ajaxsearchwidget' ),
231
			'label_nofields' => esc_html__( 'No search fields configured yet.', 'gravityview' ),
232
			'label_addfield' => esc_html__( 'Add Search Field', 'gravityview' ),
233
			'label_label' => esc_html__( 'Label', 'gravityview' ),
234
			'label_searchfield' => esc_html__( 'Search Field', 'gravityview' ),
235
			'label_inputtype' => esc_html__( 'Input Type', 'gravityview' ),
236
			'label_ajaxerror' => esc_html__( 'There was an error loading searchable fields. Save the View or refresh the page to fix this issue.', 'gravityview' ),
237
			'input_labels' => json_encode( self::get_search_input_labels() ),
238
			'input_types' => json_encode( self::get_input_types_by_field_type() ),
239
		) );
240
241
	}
242
243
	/**
244
	 * Add admin script to the no-conflict scripts whitelist
245
	 * @param array $allowed Scripts allowed in no-conflict mode
246
	 * @return array Scripts allowed in no-conflict mode, plus the search widget script
247
	 */
248
	public function register_no_conflict( $allowed ) {
249
		$allowed[] = 'gravityview_searchwidget_admin';
250
		return $allowed;
251
	}
252
253
	/**
254
	 * Ajax
255
	 * Returns the form fields ( only the searchable ones )
256
	 *
257
	 * @access public
258
	 * @return void
259
	 */
260
	public static function get_searchable_fields() {
261
262
		if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'gravityview_ajaxsearchwidget' ) ) {
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_POST
Loading history...
263
			exit( '0' );
0 ignored issues
show
Coding Style Compatibility introduced by
The method get_searchable_fields() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
264
		}
265
266
		$form = '';
267
268
		// Fetch the form for the current View
269
		if ( ! empty( $_POST['view_id'] ) ) {
270
271
			$form = gravityview_get_form_id( $_POST['view_id'] );
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_POST
Loading history...
272
273
		} elseif ( ! empty( $_POST['formid'] ) ) {
274
275
			$form = (int) $_POST['formid'];
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
276
277
		} elseif ( ! empty( $_POST['template_id'] ) && class_exists( 'GravityView_Ajax' ) ) {
278
279
			$form = GravityView_Ajax::pre_get_form_fields( $_POST['template_id'] );
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_POST
Loading history...
280
281
		}
282
283
		// fetch form id assigned to the view
284
		$response = self::render_searchable_fields( $form );
285
286
		exit( $response );
0 ignored issues
show
Coding Style Compatibility introduced by
The method get_searchable_fields() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
287
	}
288
289
	/**
290
	 * Generates html for the available Search Fields dropdown
291
	 * @param  int $form_id
292
	 * @param  string $current (for future use)
293
	 * @return string
294
	 */
295
	public static function render_searchable_fields( $form_id = null, $current = '' ) {
296
297
		if ( is_null( $form_id ) ) {
298
			return '';
299
		}
300
301
		// start building output
302
303
		$output = '<select class="gv-search-fields">';
304
305
		$custom_fields = array(
306
			'search_all' => array(
307
				'text' => esc_html__( 'Search Everything', 'gravityview' ),
308
				'type' => 'text',
309
			),
310
			'entry_date' => array(
311
				'text' => esc_html__( 'Entry Date', 'gravityview' ),
312
				'type' => 'date',
313
			),
314
			'entry_id' => array(
315
				'text' => esc_html__( 'Entry ID', 'gravityview' ),
316
				'type' => 'text',
317
			),
318
			'created_by' => array(
319
				'text' => esc_html__( 'Entry Creator', 'gravityview' ),
320
				'type' => 'select',
321
			),
322
			'is_starred' => array(
323
				'text' => esc_html__( 'Is Starred', 'gravityview' ),
324
				'type' => 'boolean',
325
			),
326
		);
327
328
		if ( gravityview()->plugin->supports( \GV\Plugin::FEATURE_GFQUERY ) ) {
329
			$custom_fields['is_approved'] = array(
330
				'text' => esc_html__( 'Is Approved', 'gravityview' ),
331
				'type' => 'boolean',
332
			);
333
		}
334
335
		foreach( $custom_fields as $custom_field_key => $custom_field ) {
336
			$output .= sprintf( '<option value="%s" %s data-inputtypes="%s" data-placeholder="%s">%s</option>', $custom_field_key, selected( $custom_field_key, $current, false ), $custom_field['type'], self::get_field_label( array('field' => $custom_field_key ) ), $custom_field['text'] );
0 ignored issues
show
introduced by
No space after opening parenthesis of array is bad style
Loading history...
337
		}
338
339
		// Get fields with sub-inputs and no parent
340
		$fields = gravityview_get_form_fields( $form_id, true, true );
341
342
		/**
343
		 * @filter `gravityview/search/searchable_fields` Modify the fields that are displayed as searchable in the Search Bar dropdown\n
344
		 * @since 1.17
345
		 * @see gravityview_get_form_fields() Used to fetch the fields
346
		 * @see GravityView_Widget_Search::get_search_input_types See this method to modify the type of input types allowed for a field
347
		 * @param array $fields Array of searchable fields, as fetched by gravityview_get_form_fields()
348
		 * @param  int $form_id
349
		 */
350
		$fields = apply_filters( 'gravityview/search/searchable_fields', $fields, $form_id );
351
352
		if ( ! empty( $fields ) ) {
353
354
			$blacklist_field_types = apply_filters( 'gravityview_blacklist_field_types', array( 'fileupload', 'post_image', 'post_id', 'section' ), null );
355
356
			foreach ( $fields as $id => $field ) {
357
358
				if ( in_array( $field['type'], $blacklist_field_types ) ) {
359
					continue;
360
				}
361
362
				$types = self::get_search_input_types( $id, $field['type'] );
363
364
				$output .= '<option value="'. $id .'" '. selected( $id, $current, false ).'data-inputtypes="'. esc_attr( $types ) .'">'. esc_html( $field['label'] ) .'</option>';
365
			}
366
		}
367
368
		$output .= '</select>';
369
370
		return $output;
371
372
	}
373
374
	/**
375
	 * Assign an input type according to the form field type
376
	 *
377
	 * @see admin-search-widget.js
378
	 *
379
	 * @param string|int|float $field_id Gravity Forms field ID
380
	 * @param string $field_type Gravity Forms field type (also the `name` parameter of GravityView_Field classes)
381
	 *
382
	 * @return string GV field search input type ('multi', 'boolean', 'select', 'date', 'text')
383
	 */
384
	public static function get_search_input_types( $field_id = '', $field_type = null ) {
385
386
		// @todo - This needs to be improved - many fields have . including products and addresses
387
		if ( false !== strpos( (string) $field_id, '.' ) && in_array( $field_type, array( 'checkbox' ) ) || in_array( $field_id, array( 'is_fulfilled' ) ) ) {
388
			$input_type = 'boolean'; // on/off checkbox
389
		} elseif ( in_array( $field_type, array( 'checkbox', 'post_category', 'multiselect' ) ) ) {
390
			$input_type = 'multi'; //multiselect
391
		} elseif ( in_array( $field_type, array( 'select', 'radio' ) ) ) {
392
			$input_type = 'select';
393
		} elseif ( in_array( $field_type, array( 'date' ) ) || in_array( $field_id, array( 'payment_date' ) ) ) {
394
			$input_type = 'date';
395
		} elseif ( in_array( $field_type, array( 'number' ) ) || in_array( $field_id, array( 'payment_amount' ) ) ) {
396
			$input_type = 'number';
397
		} else {
398
			$input_type = 'text';
399
		}
400
401
		/**
402
		 * @filter `gravityview/extension/search/input_type` Modify the search form input type based on field type
403
		 * @since 1.2
404
		 * @since 1.19.2 Added $field_id parameter
405
		 * @param string $input_type Assign an input type according to the form field type. Defaults: `boolean`, `multi`, `select`, `date`, `text`
406
		 * @param string $field_type Gravity Forms field type (also the `name` parameter of GravityView_Field classes)
407
		 * @param string|int|float $field_id ID of the field being processed
408
		 */
409
		$input_type = apply_filters( 'gravityview/extension/search/input_type', $input_type, $field_type, $field_id );
410
411
		return $input_type;
412
	}
413
414
	/**
415
	 * Display hidden fields to add support for sites using Default permalink structure
416
	 *
417
	 * @since 1.8
418
	 * @return array Search fields, modified if not using permalinks
419
	 */
420 4
	public function add_no_permalink_fields( $search_fields, $object, $widget_args = array() ) {
421
		/** @global WP_Rewrite $wp_rewrite */
422 4
		global $wp_rewrite;
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...
423
424
		// Support default permalink structure
425 4
		if ( false === $wp_rewrite->using_permalinks() ) {
426
427
			// By default, use current post.
428 4
			$post_id = 0;
429
430
			// We're in the WordPress Widget context, and an overriding post ID has been set.
431 4
			if ( ! empty( $widget_args['post_id'] ) ) {
432
				$post_id = absint( $widget_args['post_id'] );
433
			}
434
			// We're in the WordPress Widget context, and the base View ID should be used
435 4
			else if ( ! empty( $widget_args['view_id'] ) ) {
436
				$post_id = absint( $widget_args['view_id'] );
437
			}
438
439 4
			$args = gravityview_get_permalink_query_args( $post_id );
440
441
			// Add hidden fields to the search form
442 4
			foreach ( $args as $key => $value ) {
0 ignored issues
show
Bug introduced by
The expression $args of type array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
443 4
				$search_fields[] = array(
444 4
					'name'  => $key,
445 4
					'input' => 'hidden',
446 4
					'value' => $value,
447
				);
448
			}
449
		}
450
451 4
		return $search_fields;
452
	}
453
454
	/**
455
	 * Get the fields that are searchable for a View
456
	 *
457
	 * @since 2.0
458
	 * @since 2.0.9 Added $with_full_field parameter
459
	 *
460
	 * @param \GV\View|null $view
461
	 * @param bool $with_full_field Return full field array, or just field ID? Default: false (just field ID)
462
	 *
463
	 * TODO: Move to \GV\View, perhaps? And return a Field_Collection
464
	 * TODO: Use in gravityview()->request->is_search() to calculate whether a valid search
465
	 *
466
	 * @return array If no View, returns empty array. Otherwise, returns array of fields configured in widgets and Search Bar for a View
467
	 */
468 22
	private function get_view_searchable_fields( $view, $with_full_field = false ) {
469
470
		/**
471
		 * Find all search widgets on the view and get the searchable fields settings.
472
		 */
473 22
		$searchable_fields = array();
474
475 22
		if ( ! $view ) {
476
			return $searchable_fields;
477
		}
478
479
		/**
480
		 * Include the sidebar Widgets.
481
		 */
482 22
		$widgets = (array) get_option( 'widget_gravityview_search', array() );
483
484 22
		foreach ( $widgets as $widget ) {
485 22
			if ( ! empty( $widget['view_id'] ) && $widget['view_id'] == $view->ID ) {
486
				if( $_fields = json_decode( $widget['search_fields'], true ) ) {
487
					foreach ( $_fields as $field ) {
488 22
						$searchable_fields [] = $with_full_field ? $field : $field['field'];
489
					}
490
				}
491
			}
492
		}
493
494 22
		foreach ( $view->widgets->by_id( $this->get_widget_id() )->all() as $widget ) {
495 22
			if( $_fields = json_decode( $widget->configuration->get( 'search_fields' ), true ) ) {
496 22
				foreach ( $_fields as $field ) {
497 22
					$searchable_fields [] = $with_full_field ? $field : $field['field'];
498
				}
499
			}
500
		}
501
502 22
		return $searchable_fields;
503
	}
504
505
	/** --- Frontend --- */
506
507
	/**
508
	 * Calculate the search criteria to filter entries
509
	 * @param array $search_criteria The search criteria
510
	 * @param int $form_id The form ID
511
	 * @param array $args Some args
512
	 *
513
	 * @param bool $force_search_criteria Whether to suppress GF_Query filter, internally used in self::gf_query_filter
514
	 *
515
	 * @return array
516
	 */
517 51
	public function filter_entries( $search_criteria, $form_id = null, $args = array(), $force_search_criteria = false ) {
518 51
		if ( ! $force_search_criteria && gravityview()->plugin->supports( \GV\Plugin::FEATURE_GFQUERY ) ) {
519
			/**
520
			 * If GF_Query is available, we can construct custom conditions with nested
521
			 * booleans on the query, giving up the old ways of flat search_criteria field_filters.
522
			 */
523 32
			add_action( 'gravityview/view/query', array( $this, 'gf_query_filter' ), 10, 3 );
524 32
			return $search_criteria; // Return the original criteria, GF_Query modification kicks in later
525
		}
526
527 50
		if( 'post' === $this->search_method ) {
528
			$get = $_POST;
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_POST
Loading history...
529
		} else {
530 50
			$get = $_GET;
0 ignored issues
show
introduced by
Detected access of super global var $_GET, probably need manual inspection.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_GET
Loading history...
531
		}
532
533 50
		$view = \GV\View::by_id( \GV\Utils::get( $args, 'id' ) );
534
535 50
		gravityview()->log->debug( 'Requested $_{method}: ', array( 'method' => $this->search_method, 'data' => $get ) );
536
537 50
		if ( empty( $get ) || ! is_array( $get ) ) {
538 28
			return $search_criteria;
539
		}
540
541 23
		$get = stripslashes_deep( $get );
542
543 23
		$get = gv_map_deep( $get, 'rawurldecode' );
544
545
		// Make sure array key is set up
546 23
		$search_criteria['field_filters'] = \GV\Utils::get( $search_criteria, 'field_filters', array() );
547
548 23
		$searchable_fields = $this->get_view_searchable_fields( $view );
549
550
		// add free search
551 23
		if ( isset( $get['gv_search'] ) && '' !== $get['gv_search'] && in_array( 'search_all', $searchable_fields ) ) {
552
553 1
			$search_all_value = trim( $get['gv_search'] );
554
555
			/**
556
			 * @filter `gravityview/search-all-split-words` Search for each word separately or the whole phrase?
557
			 * @since 1.20.2
558
			 * @param bool $split_words True: split a phrase into words; False: search whole word only [Default: true]
559
			 */
560 1
			$split_words = apply_filters( 'gravityview/search-all-split-words', true );
561
562 1
			if ( $split_words ) {
563
564
				// Search for a piece
565 1
				$words = explode( ' ', $search_all_value );
566
567 1
				$words = array_filter( $words );
568
569
			} else {
570
571
				// Replace multiple spaces with one space
572 1
				$search_all_value = preg_replace( '/\s+/ism', ' ', $search_all_value );
573
574 1
				$words = array( $search_all_value );
575
			}
576
577 1
			foreach ( $words as $word ) {
578 1
				$search_criteria['field_filters'][] = array(
579 1
					'key' => null, // The field ID to search
580 1
					'value' => $word, // The value to search
581 1
					'operator' => 'contains', // What to search in. Options: `is` or `contains`
582
				);
583
			}
584
		}
585
586
		// start date & end date
587 23
		if ( in_array( 'entry_date', $searchable_fields ) ) {
588
			/**
589
			 * Get and normalize the dates according to the input format.
590
			 */
591 11
			if ( $curr_start = ! empty( $get['gv_start'] ) ? $get['gv_start'] : '' ) {
592 11
				if( $curr_start_date = date_create_from_format( $this->get_datepicker_format( true ), $curr_start ) ) {
593 11
					$curr_start = $curr_start_date->format( 'Y-m-d' );
594
				}
595
			}
596
597 11
			if ( $curr_end = ! empty( $get['gv_start'] ) ? ( ! empty( $get['gv_end'] ) ? $get['gv_end'] : '' ) : '' ) {
598 11
				if( $curr_end_date = date_create_from_format( $this->get_datepicker_format( true ), $curr_end ) ) {
599 11
					$curr_end = $curr_end_date->format( 'Y-m-d' );
600
				}
601
			}
602
603 11
			if ( $view ) {
604
				/**
605
				 * Override start and end dates if View is limited to some already.
606
				 */
607 11
				if ( $start_date = $view->settings->get( 'start_date' ) ) {
608 1
					if ( $start_timestamp = strtotime( $curr_start ) ) {
609 1
						$curr_start = $start_timestamp < strtotime( $start_date ) ? $start_date : $curr_start;
610
					}
611
				}
612 11
				if ( $end_date = $view->settings->get( 'end_date' ) ) {
613
					if ( $end_timestamp = strtotime( $curr_end ) ) {
614
						$curr_end = $end_timestamp > strtotime( $end_date ) ? $end_date : $curr_end;
615
					}
616
				}
617
			}
618
619
			/**
620
			 * @filter `gravityview_date_created_adjust_timezone` Whether to adjust the timezone for entries. \n
621
			 * date_created is stored in UTC format. Convert search date into UTC (also used on templates/fields/date_created.php)
622
			 * @since 1.12
623
			 * @param[out,in] boolean $adjust_tz  Use timezone-adjusted datetime? If true, adjusts date based on blog's timezone setting. If false, uses UTC setting. Default: true
624
			 * @param[in] string $context Where the filter is being called from. `search` in this case.
625
			 */
626 11
			$adjust_tz = apply_filters( 'gravityview_date_created_adjust_timezone', true, 'search' );
627
628
			/**
629
			 * Don't set $search_criteria['start_date'] if start_date is empty as it may lead to bad query results (GFAPI::get_entries)
630
			 */
631 11
			if ( ! empty( $curr_start ) ) {
632 11
				$curr_start = date( 'Y-m-d H:i:s', strtotime( $curr_start ) );
633 11
				$search_criteria['start_date'] = $adjust_tz ? get_gmt_from_date( $curr_start ) : $curr_start;
634
			}
635
636 11
			if ( ! empty( $curr_end ) ) {
637
				// Fast-forward 24 hour on the end time
638 11
				$curr_end = date( 'Y-m-d H:i:s', strtotime( $curr_end ) + DAY_IN_SECONDS );
639 11
				$search_criteria['end_date'] = $adjust_tz ? get_gmt_from_date( $curr_end ) : $curr_end;
640 11
				if ( strpos( $search_criteria['end_date'], '00:00:00' ) ) { // See https://github.com/gravityview/GravityView/issues/1056
641 11
					$search_criteria['end_date'] = date( 'Y-m-d H:i:s', strtotime( $search_criteria['end_date'] ) - 1 );
642
				}
643
			}
644
		}
645
646
		// search for a specific entry ID
647 23
		if ( ! empty( $get[ 'gv_id' ] ) && in_array( 'entry_id', $searchable_fields ) ) {
0 ignored issues
show
introduced by
Array keys should NOT be surrounded by spaces if they only contain a string or an integer.
Loading history...
648 2
			$search_criteria['field_filters'][] = array(
649 2
				'key' => 'id',
650 2
				'value' => absint( $get[ 'gv_id' ] ),
0 ignored issues
show
introduced by
Array keys should NOT be surrounded by spaces if they only contain a string or an integer.
Loading history...
651 2
				'operator' => '=',
652
			);
653
		}
654
655
		// search for a specific Created_by ID
656 23
		if ( ! empty( $get[ 'gv_by' ] ) && in_array( 'created_by', $searchable_fields ) ) {
0 ignored issues
show
introduced by
Array keys should NOT be surrounded by spaces if they only contain a string or an integer.
Loading history...
657 2
			$search_criteria['field_filters'][] = array(
658 2
				'key' => 'created_by',
659 2
				'value' => absint( $get['gv_by'] ),
660 2
				'operator' => '=',
661
			);
662
		}
663
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
664
665
		// Get search mode passed in URL
666 23
		$mode = isset( $get['mode'] ) && in_array( $get['mode'], array( 'any', 'all' ) ) ?  $get['mode'] : 'any';
667
668
		// get the other search filters
669 23
		foreach ( $get as $key => $value ) {
670
671 23
			if ( 0 !== strpos( $key, 'filter_' ) || gv_empty( $value, false, false ) || ( is_array( $value ) && count( $value ) === 1 && gv_empty( $value[0], false, false ) ) ) {
0 ignored issues
show
introduced by
Found "=== 1". Use Yoda Condition checks, you must
Loading history...
672 13
				continue;
673
			}
674
675 13
			$filter_key = $this->convert_request_key_to_filter_key( $key );
676
677
			// could return simple filter or multiple filters
678 13
			if ( ! in_array( 'search_all', $searchable_fields ) && ! in_array( $filter_key , $searchable_fields ) ) {
679 1
				continue;
680
			}
681
682 12
			$filter = $this->prepare_field_filter( $filter_key, $value, $view );
683
684 12
			if ( isset( $filter[0]['value'] ) ) {
685
				$search_criteria['field_filters'] = array_merge( $search_criteria['field_filters'], $filter );
686
687
				// if date range type, set search mode to ALL
688
				if ( ! empty( $filter[0]['operator'] ) && in_array( $filter[0]['operator'], array( '>=', '<=', '>', '<' ) ) ) {
689
					$mode = 'all';
690
				}
691 12
			} elseif( !empty( $filter ) ) {
0 ignored issues
show
introduced by
Expected 1 space after "!"; 0 found
Loading history...
692 12
				$search_criteria['field_filters'][] = $filter;
693
			}
694
		}
695
696
		/**
697
		 * @filter `gravityview/search/mode` Set the Search Mode (`all` or `any`)
698
		 * @since 1.5.1
699
		 * @param[out,in] string $mode Search mode (`any` vs `all`)
700
		 */
701 23
		$search_criteria['field_filters']['mode'] = apply_filters( 'gravityview/search/mode', $mode );
702
703 23
		gravityview()->log->debug( 'Returned Search Criteria: ', array( 'data' => $search_criteria ) );
704
705 23
		unset( $get );
706
707 23
		return $search_criteria;
708
	}
709
710
	/**
711
	 * Filters the \GF_Query with advanced logic.
712
	 *
713
	 * Dropin for the legacy flat filters when \GF_Query is available.
714
	 *
715
	 * @param \GF_Query $query The current query object reference
716
	 * @param \GV\View $this The current view object
0 ignored issues
show
Bug introduced by
There is no parameter named $this. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
717
	 * @param \GV\Request $request The request object
718
	 */
719 31
	public function gf_query_filter( &$query, $view, $request ) {
0 ignored issues
show
Unused Code introduced by
The parameter $request 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...
720
		/**
721
		 * This is a shortcut to get all the needed search criteria.
722
		 * We feed these into an new GF_Query and tack them onto the current object.
723
		 */
724 31
		$search_criteria = $this->filter_entries( array(), null, array( 'id' => $view->ID ), true /** force search_criteria */ );
725
726 31
		if ( empty( $search_criteria['field_filters'] ) ) {
727 27
			return;
728
		}
729
730 4
		$extra_conditions = array();
731
732 4
		foreach ( $search_criteria['field_filters'] as &$filter ) {
733 4
			if ( ! is_array( $filter ) ) {
734 4
				continue;
735
			}
736
737
			// Construct a manual query for unapproved statuses
738 4
			if ( 'is_approved' === $filter['key'] && in_array( \GravityView_Entry_Approval_Status::UNAPPROVED, $filter['value'] ) ) {
739 2
				$_tmp_query       = new GF_Query( $view->form->ID, array(
740 2
					'field_filters' => array(
741
						array(
742 2
							'operator' => 'in',
743 2
							'key'      => 'is_approved',
744 2
							'value'    => $filter['value'],
745
						),
746
						array(
747
							'operator' => 'is',
748
							'key'      => 'is_approved',
749
							'value'    => '',
750
						),
751 2
						'mode' => 'any'
0 ignored issues
show
introduced by
Key specified for array entry; first entry has no key
Loading history...
752
					),
753
				) );
754 2
				$_tmp_query_parts = $_tmp_query->_introspect();
755
756 2
				$extra_conditions[] = $_tmp_query_parts['where'];
757
758 2
				$filter = false;
759
760 2
				continue;
761
			}
762
763
			// By default, we want searches to be wildcard for each field.
764 4
			$filter['operator'] = empty( $filter['operator'] ) ? 'contains' : $filter['operator'];
765
766
			// For multichoice, let's have an in (OR) search.
767 4
			if ( is_array( $filter['value'] ) ) {
768 2
				$filter['operator'] = 'in';
769
			}
770
771
			/**
772
			 * @filter `gravityview_search_operator` Modify the search operator for the field (contains, is, isnot, etc)
773
			 * @param string $operator Existing search operator
774
			 * @param array $filter array with `key`, `value`, `operator`, `type` keys
775
			 */
776 4
			$filter['operator'] = apply_filters( 'gravityview_search_operator', $filter['operator'], $filter );
777
		}
778
779 4
		$search_criteria['field_filters'] = array_filter( $search_criteria['field_filters'] );
780
781
		/**
782
		 * Parse the filter criteria to generate the needed
783
		 * WHERE clauses. This is a trick to not write our own generation
784
		 * code by reusing what's inside GF_Query already.
785
		 */
786 4
		$_tmp_query       = new GF_Query( $view->form->ID, $search_criteria );
787 4
		$_tmp_query_parts = $_tmp_query->_introspect();
788
789
		/**
790
		 * Grab the current clauses. We'll be combining them shortly.
791
		 */
792 4
		$query_parts      = $query->_introspect();
793
794
		/**
795
		 * Combine the parts as a new WHERE clause.
796
		 */
797 4
		$where = call_user_func_array( '\GF_Query_Condition::_and', array_merge( array( $query_parts['where'], $_tmp_query_parts['where'] ), $extra_conditions ) );
798 4
		$query->where( $where );
799 4
	}
800
801
	/**
802
	 * Convert $_GET/$_POST key to the field/meta ID
803
	 *
804
	 * Examples:
805
	 * - `filter_is_starred` => `is_starred`
806
	 * - `filter_1_2` => `1.2`
807
	 * - `filter_5` => `5`
808
	 *
809
	 * @since 2.0
810
	 *
811
	 * @param string $key $_GET/_$_POST search key
812
	 *
813
	 * @return string
814
	 */
815 13
	private function convert_request_key_to_filter_key( $key ) {
816
817 13
		$field_id = str_replace( 'filter_', '', $key );
818
819
		// calculates field_id, removing 'filter_' and for '_' for advanced fields ( like name or checkbox )
820 13
		if ( preg_match('/^[0-9_]+$/ism', $field_id ) ) {
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces after opening bracket; 0 found
Loading history...
821 11
			$field_id = str_replace( '_', '.', $field_id );
822
		}
823
824 13
		return $field_id;
825
	}
826
827
	/**
828
	 * Prepare the field filters to GFAPI
829
	 *
830
	 * The type post_category, multiselect and checkbox support multi-select search - each value needs to be separated in an independent filter so we could apply the ANY search mode.
831
	 *
832
	 * Format searched values
833
	 *
834
	 * @param  string $filter_key ID of the field, or entry meta key
835
	 * @param  string $value $_GET/$_POST search value
836
	 * @param  \GV\View $view The view we're looking at
837
	 *
838
	 * @return array        1 or 2 deph levels
839
	 */
840 12
	public function prepare_field_filter( $filter_key, $value, $view ) {
841
842
		// get form field array
843 12
		$form_field = is_numeric( $filter_key ) ? \GV\GF_Field::by_id( $view->form, $filter_key ) : \GV\Internal_Field::by_id( $filter_key );
844
845
		// default filter array
846
		$filter = array(
847 12
			'key'   => $filter_key,
848 12
			'value' => $value,
849
		);
850
851 12
		switch ( $form_field->type ) {
852
853 12
			case 'select':
854 12
			case 'radio':
855 1
				$filter['operator'] = 'is';
856 1
				break;
857
858 11
			case 'post_category':
859
860
				if ( ! is_array( $value ) ) {
861
					$value = array( $value );
862
				}
863
864
				// Reset filter variable
865
				$filter = array();
866
867
				foreach ( $value as $val ) {
868
					$cat = get_term( $val, 'category' );
869
					$filter[] = array(
870
						'key'      => $filter_key,
871
						'value'    => esc_attr( $cat->name ) . ':' . $val,
872
						'operator' => 'is',
873
					);
874
				}
875
876
				break;
877
878 11
			case 'multiselect':
879
880
				if ( ! is_array( $value ) ) {
881
					break;
882
				}
883
884
				// Reset filter variable
885
				$filter = array();
886
887
				foreach ( $value as $val ) {
888
					$filter[] = array( 'key' => $filter_key, 'value' => $val );
889
				}
890
891
				break;
892
893 11
			case 'checkbox':
894
				// convert checkbox on/off into the correct search filter
895
				if ( false !== strpos( $filter_key, '.' ) && ! empty( $form_field->inputs ) && ! empty( $form_field->choices ) ) {
896
					foreach ( $form_field->inputs as $k => $input ) {
897
						if ( $input['id'] == $filter_key ) {
898
							$filter['value'] = $form_field->choices[ $k ]['value'];
899
							$filter['operator'] = 'is';
900
							break;
901
						}
902
					}
903
				} elseif ( is_array( $value ) ) {
904
905
					// Reset filter variable
906
					$filter = array();
907
908
					foreach ( $value as $val ) {
909
						$filter[] = array(
910
							'key'      => $filter_key,
911
							'value'    => $val,
912
							'operator' => 'is',
913
						);
914
					}
915
				}
916
917
				break;
918
919 11
			case 'name':
920 11
			case 'address':
921
922
				if ( false === strpos( $filter_key, '.' ) ) {
923
924
					$words = explode( ' ', $value );
925
926
					$filters = array();
927
					foreach ( $words as $word ) {
928
						if ( ! empty( $word ) && strlen( $word ) > 1 ) {
929
							// Keep the same key for each filter
930
							$filter['value'] = $word;
931
							// Add a search for the value
932
							$filters[] = $filter;
933
						}
934
					}
935
936
					$filter = $filters;
937
				}
938
939
				// State/Province should be exact matches
940
				if ( 'address' === $form_field->field->type ) {
941
942
					$searchable_fields = $this->get_view_searchable_fields( $view, true );
943
944
					foreach ( $searchable_fields as $searchable_field ) {
945
946
						if( $form_field->ID !== $searchable_field['field'] ) {
947
							continue;
948
						}
949
950
						// Only exact-match dropdowns, not text search
951
						if( in_array( $searchable_field['input'], array( 'text', 'search' ), true ) ) {
952
							continue;
953
						}
954
955
						$input_id = gravityview_get_input_id_from_id( $form_field->ID );
956
957
						if ( 4 === $input_id ) {
958
							$filter['operator'] = 'is';
959
						};
960
					}
961
				}
962
963
				break;
964
965 11
			case 'date':
966
967 8
				$date_format = $this->get_datepicker_format( true );
968
969 8
				if ( is_array( $value ) ) {
970
971
					// Reset filter variable
972
					$filter = array();
973
974
					foreach ( $value as $k => $date ) {
975
						if ( empty( $date ) ) {
976
							continue;
977
						}
978
						$operator = 'start' === $k ? '>=' : '<=';
979
980
						/**
981
						 * @hack
982
						 * @since 1.16.3
983
						 * Safeguard until GF implements '<=' operator
984
						 */
985
						if( !GFFormsModel::is_valid_operator( $operator ) && $operator === '<=' ) {
0 ignored issues
show
introduced by
Expected 1 space after "!"; 0 found
Loading history...
986
							$operator = '<';
987
							$date = date( 'Y-m-d', strtotime( self::get_formatted_date( $date, 'Y-m-d', $date_format ) . ' +1 day' ) );
988
						}
989
990
						$filter[] = array(
991
							'key'      => $filter_key,
992
							'value'    => self::get_formatted_date( $date, 'Y-m-d', $date_format ),
993
							'operator' => $operator,
994
						);
995
					}
996
				} else {
997 8
					$date = $value;
998 8
					$filter['value'] = self::get_formatted_date( $date, 'Y-m-d', $date_format );
999
				}
1000
1001 8
				break;
1002
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
1003
1004
		} // switch field type
1005
1006 12
		return $filter;
1007
	}
1008
1009
	/**
1010
	 * Get the Field Format form GravityForms
1011
	 *
1012
	 * @param GF_Field_Date $field The field object
1013
	 * @since 1.10
1014
	 *
1015
	 * @return string Format of the date in the database
1016
	 */
1017
	public static function get_date_field_format( GF_Field_Date $field ) {
1018
		$format = 'm/d/Y';
1019
		$datepicker = array(
1020
			'mdy' => 'm/d/Y',
1021
			'dmy' => 'd/m/Y',
1022
			'dmy_dash' => 'd-m-Y',
1023
			'dmy_dot' => 'd.m.Y',
1024
			'ymd_slash' => 'Y/m/d',
1025
			'ymd_dash' => 'Y-m-d',
1026
			'ymd_dot' => 'Y.m.d',
1027
		);
1028
1029
		if ( ! empty( $field->dateFormat ) && isset( $datepicker[ $field->dateFormat ] ) ){
1030
			$format = $datepicker[ $field->dateFormat ];
1031
		}
1032
1033
		return $format;
1034
	}
1035
1036
	/**
1037
	 * Format a date value
1038
	 *
1039
	 * @param string $value Date value input
1040
	 * @param string $format Wanted formatted date
1041
	 *
1042
	 * @since 2.1.2
1043
	 * @param string $value_format The value format. Default: Y-m-d
1044
	 *
1045
	 * @return string
1046
	 */
1047 8
	public static function get_formatted_date( $value = '', $format = 'Y-m-d', $value_format = 'Y-m-d' ) {
1048
1049 8
		$date = date_create_from_format( $value_format, $value );
1050
1051 8
		if ( empty( $date ) ) {
1052
			gravityview()->log->debug( 'Date format not valid: {value}', array( 'value' => $value ) );
1053
			return '';
1054
		}
1055 8
		return $date->format( $format );
1056
	}
1057
1058
1059
	/**
1060
	 * Include this extension templates path
1061
	 * @param array $file_paths List of template paths ordered
1062
	 */
1063 1
	public function add_template_path( $file_paths ) {
1064
1065
		// Index 100 is the default GravityView template path.
1066 1
		$file_paths[102] = self::$file . 'templates/';
1067
1068 1
		return $file_paths;
1069
	}
1070
1071
	/**
1072
	 * Check whether the configured search fields have a date field
1073
	 *
1074
	 * @since 1.17.5
1075
	 *
1076
	 * @param array $search_fields
1077
	 *
1078
	 * @return bool True: has a `date` or `date_range` field
1079
	 */
1080 4
	private function has_date_field( $search_fields ) {
1081
1082 4
		$has_date = false;
1083
1084 4
		foreach ( $search_fields as $k => $field ) {
1085 4
			if ( in_array( $field['input'], array( 'date', 'date_range', 'entry_date' ) ) ) {
1086
				$has_date = true;
1087 4
				break;
1088
			}
1089
		}
1090
1091 4
		return $has_date;
1092
	}
1093
1094
	/**
1095
	 * Renders the Search Widget
1096
	 * @param array $widget_args
1097
	 * @param string $content
1098
	 * @param string $context
1099
	 *
1100
	 * @return void
1101
	 */
1102 4
	public function render_frontend( $widget_args, $content = '', $context = '' ) {
1103
		/** @var GravityView_View $gravityview_view */
1104 4
		$gravityview_view = GravityView_View::getInstance();
1105
1106 4
		if ( empty( $gravityview_view ) ) {
1107
			gravityview()->log->debug( '$gravityview_view not instantiated yet.' );
1108
			return;
1109
		}
1110
1111
		// get configured search fields
1112 4
		$search_fields = ! empty( $widget_args['search_fields'] ) ? json_decode( $widget_args['search_fields'], true ) : '';
1113
1114 4
		if ( empty( $search_fields ) || ! is_array( $search_fields ) ) {
1115
			gravityview()->log->debug( 'No search fields configured for widget:', array( 'data' => $widget_args ) );
1116
			return;
1117
		}
1118
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
1119
1120
		// prepare fields
1121 4
		foreach ( $search_fields as $k => $field ) {
1122
1123 4
			$updated_field = $field;
1124
1125 4
			$updated_field = $this->get_search_filter_details( $updated_field );
1126
1127 4
			switch ( $field['field'] ) {
1128
1129 4
				case 'search_all':
1130 4
					$updated_field['key'] = 'search_all';
1131 4
					$updated_field['input'] = 'search_all';
1132 4
					$updated_field['value'] = $this->rgget_or_rgpost( 'gv_search' );
1133 4
					break;
1134
1135
				case 'entry_date':
1136
					$updated_field['key'] = 'entry_date';
1137
					$updated_field['input'] = 'entry_date';
1138
					$updated_field['value'] = array(
1139
						'start' => $this->rgget_or_rgpost( 'gv_start' ),
1140
						'end' => $this->rgget_or_rgpost( 'gv_end' ),
1141
					);
1142
					break;
1143
1144
				case 'entry_id':
1145
					$updated_field['key'] = 'entry_id';
1146
					$updated_field['input'] = 'entry_id';
1147
					$updated_field['value'] = $this->rgget_or_rgpost( 'gv_id' );
1148
					break;
1149
1150
				case 'created_by':
1151
					$updated_field['key'] = 'created_by';
1152
					$updated_field['name'] = 'gv_by';
1153
					$updated_field['value'] = $this->rgget_or_rgpost( 'gv_by' );
1154
					$updated_field['choices'] = self::get_created_by_choices();
1155
					break;
1156
				
1157
				case 'is_approved':
1158
					$updated_field['key'] = 'is_approved';
1159
					$updated_field['input'] = 'checkbox';
1160
					$updated_field['value'] = $this->rgget_or_rgpost( 'filter_is_approved' );
1161
					$updated_field['choices'] = self::get_is_approved_choices();
1162
					break;
1163
			}
1164
1165 4
			$search_fields[ $k ] = $updated_field;
1166
		}
1167
1168 4
		gravityview()->log->debug( 'Calculated Search Fields: ', array( 'data' => $search_fields ) );
1169
1170
		/**
1171
		 * @filter `gravityview_widget_search_filters` Modify what fields are shown. The order of the fields in the $search_filters array controls the order as displayed in the search bar widget.
1172
		 * @param array $search_fields Array of search filters with `key`, `label`, `value`, `type`, `choices` keys
1173
		 * @param GravityView_Widget_Search $this Current widget object
1174
		 * @param array $widget_args Args passed to this method. {@since 1.8}
1175
		 * @param \GV\Template_Context $context {@since 2.0}
1176
		 * @var array
1177
		 */
1178 4
		$gravityview_view->search_fields = apply_filters( 'gravityview_widget_search_filters', $search_fields, $this, $widget_args, $context );
0 ignored issues
show
Bug introduced by
The property search_fields does not seem to exist. Did you mean fields?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
1179
1180 4
		$gravityview_view->search_layout = ! empty( $widget_args['search_layout'] ) ? $widget_args['search_layout'] : 'horizontal';
0 ignored issues
show
Bug introduced by
The property search_layout does not seem to exist in GravityView_View.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
1181
1182
		/** @since 1.14 */
1183 4
		$gravityview_view->search_mode = ! empty( $widget_args['search_mode'] ) ? $widget_args['search_mode'] : 'any';
0 ignored issues
show
Bug introduced by
The property search_mode does not seem to exist in GravityView_View.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
1184
1185 4
		$custom_class = ! empty( $widget_args['custom_class'] ) ? $widget_args['custom_class'] : '';
1186
1187 4
		$gravityview_view->search_class = self::get_search_class( $custom_class );
0 ignored issues
show
Bug introduced by
The property search_class does not seem to exist in GravityView_View.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
1188
1189 4
		$gravityview_view->search_clear = ! empty( $widget_args['search_clear'] ) ? $widget_args['search_clear'] : false;
0 ignored issues
show
Bug introduced by
The property search_clear does not seem to exist in GravityView_View.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
1190
1191 4
		if ( $this->has_date_field( $search_fields ) ) {
1192
			// enqueue datepicker stuff only if needed!
1193
			$this->enqueue_datepicker();
1194
		}
1195
1196 4
		$this->maybe_enqueue_flexibility();
1197
1198 4
		$gravityview_view->render( 'widget', 'search', false );
1199 4
	}
1200
1201
	/**
1202
	 * Get the search class for a search form
1203
	 *
1204
	 * @since 1.5.4
1205
	 *
1206
	 * @return string Sanitized CSS class for the search form
1207
	 */
1208 4
	public static function get_search_class( $custom_class = '' ) {
1209 4
		$gravityview_view = GravityView_View::getInstance();
1210
1211 4
		$search_class = 'gv-search-'.$gravityview_view->search_layout;
0 ignored issues
show
Documentation introduced by
The property search_layout does not exist on object<GravityView_View>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1212
1213 4
		if ( ! empty( $custom_class )  ) {
1214
			$search_class .= ' '.$custom_class;
1215
		}
1216
1217
		/**
1218
		 * @filter `gravityview_search_class` Modify the CSS class for the search form
1219
		 * @param string $search_class The CSS class for the search form
1220
		 */
1221 4
		$search_class = apply_filters( 'gravityview_search_class', $search_class );
1222
1223
		// Is there an active search being performed? Used by fe-views.js
1224 4
		$search_class .= GravityView_frontend::getInstance()->isSearch() ? ' gv-is-search' : '';
1225
1226 4
		return gravityview_sanitize_html_class( $search_class );
1227
	}
1228
1229
1230
	/**
1231
	 * Calculate the search form action
1232
	 * @since 1.6
1233
	 *
1234
	 * @return string
1235
	 */
1236 4
	public static function get_search_form_action() {
1237 4
		$gravityview_view = GravityView_View::getInstance();
1238
1239 4
		$post_id = $gravityview_view->getPostId() ? $gravityview_view->getPostId() : $gravityview_view->getViewId();
1240
1241 4
		$url = add_query_arg( array(), get_permalink( $post_id ) );
1242
1243 4
		return esc_url( $url );
1244
	}
1245
1246
	/**
1247
	 * Get the label for a search form field
1248
	 * @param  array $field      Field setting as sent by the GV configuration - has `field`, `input` (input type), and `label` keys
1249
	 * @param  array $form_field Form field data, as fetched by `gravityview_get_field()`
1250
	 * @return string             Label for the search form
1251
	 */
1252 4
	private static function get_field_label( $field, $form_field = array() ) {
1253
1254 4
		$label = \GV\Utils::_GET( 'label', \GV\Utils::get( $field, 'label' ) );
1255
1256 4
		if ( ! $label ) {
1257
1258 4
			$label = isset( $form_field['label'] ) ? $form_field['label'] : '';
1259
1260 4
			switch( $field['field'] ) {
1261 4
				case 'search_all':
1262 4
					$label = __( 'Search Entries:', 'gravityview' );
1263 4
					break;
1264
				case 'entry_date':
1265
					$label = __( 'Filter by date:', 'gravityview' );
1266
					break;
1267
				case 'entry_id':
1268
					$label = __( 'Entry ID:', 'gravityview' );
1269
					break;
1270
				default:
1271
					// If this is a field input, not a field
1272
					if ( strpos( $field['field'], '.' ) > 0 && ! empty( $form_field['inputs'] ) ) {
1273
1274
						// Get the label for the field in question, which returns an array
1275
						$items = wp_list_filter( $form_field['inputs'], array( 'id' => $field['field'] ) );
1276
1277
						// Get the item with the `label` key
1278
						$values = wp_list_pluck( $items, 'label' );
1279
1280
						// There will only one item in the array, but this is easier
1281
						foreach ( $values as $value ) {
1282
							$label = $value;
1283
							break;
1284
						}
1285
					}
1286
			}
1287
		}
1288
1289
		/**
1290
		 * @filter `gravityview_search_field_label` Modify the label for a search field. Supports returning HTML
1291
		 * @since 1.17.3 Added $field parameter
1292
		 * @param[in,out] string $label Existing label text, sanitized.
1293
		 * @param[in] array $form_field Gravity Forms field array, as returned by `GFFormsModel::get_field()`
1294
		 * @param[in] array $field Field setting as sent by the GV configuration - has `field`, `input` (input type), and `label` keys
1295
		 */
1296 4
		$label = apply_filters( 'gravityview_search_field_label', esc_attr( $label ), $form_field, $field );
1297
1298 4
		return $label;
1299
	}
1300
1301
	/**
1302
	 * Prepare search fields to frontend render with other details (label, field type, searched values)
1303
	 *
1304
	 * @param array $field
1305
	 * @return array
1306
	 */
1307 4
	private function get_search_filter_details( $field ) {
1308
1309 4
		$gravityview_view = GravityView_View::getInstance();
1310
1311 4
		$form = $gravityview_view->getForm();
1312
1313
		// for advanced field ids (eg, first name / last name )
1314 4
		$name = 'filter_' . str_replace( '.', '_', $field['field'] );
1315
1316
		// get searched value from $_GET/$_POST (string or array)
1317 4
		$value = $this->rgget_or_rgpost( $name );
1318
1319
		// get form field details
1320 4
		$form_field = gravityview_get_field( $form, $field['field'] );
1321
1322
		$filter = array(
1323 4
			'key' => $field['field'],
1324 4
			'name' => $name,
1325 4
			'label' => self::get_field_label( $field, $form_field ),
1326 4
			'input' => $field['input'],
1327 4
			'value' => $value,
1328 4
			'type' => $form_field['type'],
1329
		);
1330
1331
		// collect choices
1332 4
		if ( 'post_category' === $form_field['type'] && ! empty( $form_field['displayAllCategories'] ) && empty( $form_field['choices'] ) ) {
1333
			$filter['choices'] = gravityview_get_terms_choices();
1334 4
		} elseif ( ! empty( $form_field['choices'] ) ) {
1335
			$filter['choices'] = $form_field['choices'];
1336
		}
1337
1338 4
		if ( 'date_range' === $field['input'] && empty( $value ) ) {
1339
			$filter['value'] = array( 'start' => '', 'end' => '' );
1340
		}
1341
1342 4
		return $filter;
1343
1344
	}
1345
1346
	/**
1347
	 * Calculate the search choices for the users
1348
	 *
1349
	 * @since 1.8
1350
	 *
1351
	 * @return array Array of user choices (value = ID, text = display name)
1352
	 */
1353
	private static function get_created_by_choices() {
1354
1355
		/**
1356
		 * filter gravityview/get_users/search_widget
1357
		 * @see \GVCommon::get_users
1358
		 */
1359
		$users = GVCommon::get_users( 'search_widget', array( 'fields' => array( 'ID', 'display_name' ) ) );
1360
1361
		$choices = array();
1362
		foreach ( $users as $user ) {
1363
			$choices[] = array(
1364
				'value' => $user->ID,
1365
				'text' => $user->display_name,
1366
			);
1367
		}
1368
1369
		return $choices;
1370
	}
1371
1372
	/**
1373
	 * Calculate the search checkbox choices for approval status
1374
	 *
1375
	 * @since develop
1376
	 *
1377
	 * @return array Array of approval status choices (value = status, text = display name)
1378
	 */
1379
	private static function get_is_approved_choices() {
1380
1381
		$choices = array();
1382
		foreach ( GravityView_Entry_Approval_Status::get_all() as $status ) {
1383
			$choices[] = array(
1384
				'value' => $status['value'],
1385
				'text' => $status['label'],
1386
			);
1387
		}
1388
1389
		return $choices;
1390
	}
1391
1392
	/**
1393
	 * Output the Clear Search Results button
1394
	 * @since 1.5.4
1395
	 */
1396 4
	public static function the_clear_search_button() {
1397 4
		$gravityview_view = GravityView_View::getInstance();
1398
1399 4
		if ( $gravityview_view->search_clear ) {
0 ignored issues
show
Documentation introduced by
The property search_clear does not exist on object<GravityView_View>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1400
1401
			$url = strtok( add_query_arg( array() ), '?' );
1402
1403
			echo gravityview_get_link( $url, esc_html__( 'Clear', 'gravityview' ), 'class=button gv-search-clear' );
0 ignored issues
show
introduced by
Expected a sanitizing function (see Codex for 'Data Validation'), but instead saw 'gravityview_get_link'
Loading history...
1404
1405
		}
1406 4
	}
1407
1408
	/**
1409
	 * Based on the search method, fetch the value for a specific key
1410
	 *
1411
	 * @since 1.16.4
1412
	 *
1413
	 * @param string $name Name of the request key to fetch the value for
1414
	 *
1415
	 * @return mixed|string Value of request at $name key. Empty string if empty.
1416
	 */
1417 4
	private function rgget_or_rgpost( $name ) {
1418 4
		$value = \GV\Utils::_REQUEST( $name );
1419
1420 4
		$value = stripslashes_deep( $value );
1421
1422 4
		$value = gv_map_deep( $value, 'rawurldecode' );
1423
1424 4
		$value = gv_map_deep( $value, '_wp_specialchars' );
1425
1426 4
		return $value;
1427
	}
1428
1429
1430
	/**
1431
	 * Require the datepicker script for the frontend GV script
1432
	 * @param array $js_dependencies Array of existing required scripts for the fe-views.js script
1433
	 * @return array Array required scripts, with `jquery-ui-datepicker` added
1434
	 */
1435
	public function add_datepicker_js_dependency( $js_dependencies ) {
1436
1437
		$js_dependencies[] = 'jquery-ui-datepicker';
1438
1439
		return $js_dependencies;
1440
	}
1441
1442
	/**
1443
	 * Modify the array passed to wp_localize_script()
1444
	 *
1445
	 * @param array $js_localization The data padded to the Javascript file
0 ignored issues
show
Bug introduced by
There is no parameter named $js_localization. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1446
	 * @param array $view_data View data array with View settings
1447
	 *
1448
	 * @return array
1449
	 */
1450
	public function add_datepicker_localization( $localizations = array(), $view_data = array() ) {
1451
		global $wp_locale;
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...
1452
1453
		/**
1454
		 * @filter `gravityview_datepicker_settings` Modify the datepicker settings
1455
		 * @see http://api.jqueryui.com/datepicker/ Learn what settings are available
1456
		 * @see http://www.renegadetechconsulting.com/tutorials/jquery-datepicker-and-wordpress-i18n Thanks for the helpful information on $wp_locale
1457
		 * @param array $js_localization The data padded to the Javascript file
1458
		 * @param array $view_data View data array with View settings
1459
		 */
1460
		$datepicker_settings = apply_filters( 'gravityview_datepicker_settings', array(
1461
			'yearRange' => '-5:+5',
1462
			'changeMonth' => true,
1463
			'changeYear' => true,
1464
			'closeText' => esc_attr_x( 'Close', 'Close calendar', 'gravityview' ),
1465
			'prevText' => esc_attr_x( 'Prev', 'Previous month in calendar', 'gravityview' ),
1466
			'nextText' => esc_attr_x( 'Next', 'Next month in calendar', 'gravityview' ),
1467
			'currentText' => esc_attr_x( 'Today', 'Today in calendar', 'gravityview' ),
1468
			'weekHeader' => esc_attr_x( 'Week', 'Week in calendar', 'gravityview' ),
1469
			'monthStatus'       => __( 'Show a different month', 'gravityview' ),
1470
			'monthNames'        => array_values( $wp_locale->month ),
1471
			'monthNamesShort'   => array_values( $wp_locale->month_abbrev ),
1472
			'dayNames'          => array_values( $wp_locale->weekday ),
1473
			'dayNamesShort'     => array_values( $wp_locale->weekday_abbrev ),
1474
			'dayNamesMin'       => array_values( $wp_locale->weekday_initial ),
1475
			// get the start of week from WP general setting
1476
			'firstDay'          => get_option( 'start_of_week' ),
1477
			// is Right to left language? default is false
1478
			'isRTL'             => is_rtl(),
1479
		), $view_data );
1480
1481
		$localizations['datepicker'] = $datepicker_settings;
1482
1483
		return $localizations;
1484
1485
	}
1486
1487
	/**
1488
	 * Register search widget scripts, including Flexibility
1489
	 *
1490
	 * @see https://github.com/10up/flexibility
1491
	 *
1492
	 * @since 1.17
1493
	 *
1494
	 * @return void
1495
	 */
1496
	public function register_scripts() {
1497
		wp_register_script( 'gv-flexibility', plugins_url( 'assets/lib/flexibility/flexibility.js', GRAVITYVIEW_FILE ), array(), \GV\Plugin::$version, true );
1498
	}
1499
1500
	/**
1501
	 * If the current visitor is running IE 8 or 9, enqueue Flexibility
1502
	 *
1503
	 * @since 1.17
1504
	 *
1505
	 * @return void
1506
	 */
1507 4
	private function maybe_enqueue_flexibility() {
1508 4
		if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && preg_match( '/MSIE [8-9]/', $_SERVER['HTTP_USER_AGENT'] ) ) {
0 ignored issues
show
introduced by
Due to using Batcache, server side based client related logic will not work, use JS instead.
Loading history...
introduced by
Detected usage of a non-sanitized input variable: $_SERVER
Loading history...
1509
			wp_enqueue_script( 'gv-flexibility' );
1510
		}
1511 4
	}
1512
1513
	/**
1514
	 * Enqueue the datepicker script
1515
	 *
1516
	 * It sets the $gravityview->datepicker_class parameter
1517
	 *
1518
	 * @todo Use own datepicker javascript instead of GF datepicker.js - that way, we can localize the settings and not require the changeMonth and changeYear pickers.
1519
	 * @return void
1520
	 */
1521
	public function enqueue_datepicker() {
1522
		$gravityview_view = GravityView_View::getInstance();
1523
1524
		wp_enqueue_script( 'jquery-ui-datepicker' );
1525
1526
		add_filter( 'gravityview_js_dependencies', array( $this, 'add_datepicker_js_dependency' ) );
1527
		add_filter( 'gravityview_js_localization', array( $this, 'add_datepicker_localization' ), 10, 2 );
1528
1529
		$scheme = is_ssl() ? 'https://' : 'http://';
1530
		wp_enqueue_style( 'jquery-ui-datepicker', $scheme.'ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/smoothness/jquery-ui.css' );
1531
1532
		/**
1533
		 * @filter `gravityview_search_datepicker_class`
1534
		 * Modify the CSS class for the datepicker, used by the CSS class is used by Gravity Forms' javascript to determine the format for the date picker. The `gv-datepicker` class is required by the GravityView datepicker javascript.
1535
		 * @param string $css_class CSS class to use. Default: `gv-datepicker datepicker mdy` \n
1536
		 * Options are:
1537
		 * - `mdy` (mm/dd/yyyy)
1538
		 * - `dmy` (dd/mm/yyyy)
1539
		 * - `dmy_dash` (dd-mm-yyyy)
1540
		 * - `dmy_dot` (dd.mm.yyyy)
1541
		 * - `ymd_slash` (yyyy/mm/dd)
1542
		 * - `ymd_dash` (yyyy-mm-dd)
1543
		 * - `ymd_dot` (yyyy.mm.dd)
1544
		 */
1545
		$datepicker_class = apply_filters( 'gravityview_search_datepicker_class', "gv-datepicker datepicker " . $this->get_datepicker_format() );
0 ignored issues
show
Coding Style Comprehensibility introduced by
The string literal gv-datepicker datepicker does not require double quotes, as per coding-style, please use single quotes.

PHP provides two ways to mark string literals. Either with single quotes 'literal' or with double quotes "literal". The difference between these is that string literals in double quotes may contain variables with are evaluated at run-time as well as escape sequences.

String literals in single quotes on the other hand are evaluated very literally and the only two characters that needs escaping in the literal are the single quote itself (\') and the backslash (\\). Every other character is displayed as is.

Double quoted string literals may contain other variables or more complex escape sequences.

<?php

$singleQuoted = 'Value';
$doubleQuoted = "\tSingle is $singleQuoted";

print $doubleQuoted;

will print an indented: Single is Value

If your string literal does not contain variables or escape sequences, it should be defined using single quotes to make that fact clear.

For more information on PHP string literals and available escape sequences see the PHP core documentation.

Loading history...
1546
1547
		$gravityview_view->datepicker_class = $datepicker_class;
0 ignored issues
show
Bug introduced by
The property datepicker_class does not seem to exist in GravityView_View.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
1548
	}
1549
1550
	/**
1551
	 * Retrieve the datepicker format.
1552
	 *
1553
	 * @param bool $date_format Whether to return the PHP date format or the datpicker class name. Default: false.
1554
	 *
1555
	 * @see https://docs.gravityview.co/article/115-changing-the-format-of-the-search-widgets-date-picker
1556
	 *
1557
	 * @return string The datepicker format placeholder, or the PHP date format.
1558
	 */
1559 18
	private function get_datepicker_format( $date_format = false ) {
1560
1561 18
		$default_format = 'mdy';
1562
1563
		/**
1564
		 * @filter `gravityview/widgets/search/datepicker/format`
1565
		 * @since 2.1.1
1566
		 * @param string           $format Default: mdy
1567
		 * Options are:
1568
		 * - `mdy` (mm/dd/yyyy)
1569
		 * - `dmy` (dd/mm/yyyy)
1570
		 * - `dmy_dash` (dd-mm-yyyy)
1571
		 * - `dmy_dot` (dd.mm.yyyy)
1572
		 * - `ymd_slash` (yyyy/mm/dd)
1573
		 * - `ymd_dash` (yyyy-mm-dd)
1574
		 * - `ymd_dot` (yyyy.mm.dd)
1575
		 */
1576 18
		$format = apply_filters( 'gravityview/widgets/search/datepicker/format', $default_format );
1577
1578
		$gf_date_formats = array(
1579 18
			'mdy' => 'm/d/Y',
1580
1581
			'dmy_dash' => 'd-m-Y',
1582
			'dmy_dot' => 'd.m.Y',
1583
			'dmy' => 'd/m/Y',
1584
1585
			'ymd_slash' => 'Y/m/d',
1586
			'ymd_dash' => 'Y-m-d',
1587
			'ymd_dot' => 'Y.m.d',
1588
		);
1589
1590 18
		if ( ! $date_format ) {
1591
			// If the format key isn't valid, return default format key
1592
			return isset( $gf_date_formats[ $format ] ) ? $format : $default_format;
1593
		}
1594
1595
		// If the format key isn't valid, return default format value
1596 18
		return \GV\Utils::get( $gf_date_formats, $format, $gf_date_formats[ $default_format ] );
1597
	}
1598
1599
1600
} // end class
1601
1602
new GravityView_Widget_Search;
1603