Completed
Push — master ( af77e8...7d9d07 )
by Zack
11s
created

GravityView_Widget_Search::get_formatted_date()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 2
dl 0
loc 8
ccs 0
cts 6
cp 0
crap 6
rs 9.4285
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 GravityView_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
	public function __construct() {
31
32
		$this->widget_description = esc_html__( 'Search form for searching entries.', 'gravityview' );
33
34
		self::$instance = &$this;
35
36
		self::$file = plugin_dir_path( __FILE__ );
37
38
		$default_values = array( 'header' => 0, 'footer' => 0 );
39
40
		$settings = array(
41
			'search_layout' => array(
42
				'type' => 'radio',
43
				'full_width' => true,
44
				'label' => esc_html__( 'Search Layout', 'gravityview' ),
45
				'value' => 'horizontal',
46
				'options' => array(
47
					'horizontal' => esc_html__( 'Horizontal', 'gravityview' ),
48
					'vertical' => esc_html__( 'Vertical', 'gravityview' ),
49
				),
50
			),
51
			'search_clear' => array(
52
				'type' => 'checkbox',
53
				'label' => __( 'Show Clear button', 'gravityview' ),
54
				'value' => false,
55
			),
56
			'search_fields' => array(
57
				'type' => 'hidden',
58
				'label' => '',
59
				'class' => 'gv-search-fields-value',
60
				'value' => '[{"field":"search_all","input":"input_text"}]', // Default: Search Everything text box
61
			),
62
			'search_mode' => array(
63
				'type' => 'radio',
64
				'full_width' => true,
65
				'label' => esc_html__( 'Search Mode', 'gravityview' ),
66
				'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...
67
				'value' => 'any',
68
				'class' => 'hide-if-js',
69
				'options' => array(
70
					'any' => esc_html__( 'Match Any Fields', 'gravityview' ),
71
					'all' => esc_html__( 'Match All Fields', 'gravityview' ),
72
				),
73
			),
74
		);
75
		parent::__construct( esc_html__( 'Search Bar', 'gravityview' ), 'search_bar', $default_values, $settings );
76
77
		// frontend - filter entries
78
		add_filter( 'gravityview_fe_search_criteria', array( $this, 'filter_entries' ), 10, 1 );
79
80
		// frontend - add template path
81
		add_filter( 'gravityview_template_paths', array( $this, 'add_template_path' ) );
82
83
		// Add hidden fields for "Default" permalink structure
84
		add_filter( 'gravityview_widget_search_filters', array( $this, 'add_no_permalink_fields' ), 10, 3 );
85
86
		// admin - add scripts - run at 1100 to make sure GravityView_Admin_Views::add_scripts_and_styles() runs first at 999
87
		add_action( 'admin_enqueue_scripts', array( $this, 'add_scripts_and_styles' ), 1100 );
88
		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...
89
		add_filter( 'gravityview_noconflict_scripts', array( $this, 'register_no_conflict' ) );
90
91
		// ajax - get the searchable fields
92
		add_action( 'wp_ajax_gv_searchable_fields', array( 'GravityView_Widget_Search', 'get_searchable_fields' ) );
93
94
		// calculate the search method (POST / GET)
95
		$this->set_search_method();
96
97
	}
98
99
	/**
100
	 * @return GravityView_Widget_Search
101
	 */
102
	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...
103
		if ( empty( self::$instance ) ) {
104
			self::$instance = new GravityView_Widget_Search;
105
		}
106
		return self::$instance;
107
	}
108
109
	/**
110
	 * Sets the search method to GET (default) or POST
111
	 * @since 1.16.4
112
	 */
113
	private function set_search_method() {
114
		/**
115
		 * @filter `gravityview/search/method` Modify the search form method (GET / POST)
116
		 * @since 1.16.4
117
		 * @param string $search_method Assign an input type according to the form field type. Defaults: `boolean`, `multi`, `select`, `date`, `text`
118
		 * @param string $field_type Gravity Forms field type (also the `name` parameter of GravityView_Field classes)
119
		 */
120
		$method = apply_filters( 'gravityview/search/method', $this->search_method );
121
122
		$method = strtolower( $method );
123
124
		$this->search_method = in_array( $method, array( 'get', 'post' ) ) ? $method : 'get';
125
	}
126
127
	/**
128
	 * Returns the search method
129
	 * @since 1.16.4
130
	 * @return string
131
	 */
132
	public function get_search_method() {
133
		return $this->search_method;
134
	}
135
136
	/**
137
	 * Get the input types available for different field types
138
	 *
139
	 * @since 1.17.5
140
	 *
141
	 * @return array [field type name] => (array|string) search bar input types
142
	 */
143
	public static function get_input_types_by_field_type() {
144
		/**
145
		 * Input Type groups
146
		 * @see admin-search-widget.js (getSelectInput)
147
		 * @var array
148
		 */
149
		$input_types = array(
150
			'text' => array( 'input_text' ),
151
			'address' => array( 'input_text' ),
152
			'number' => array( 'input_text' ),
153
			'date' => array( 'date', 'date_range' ),
154
			'boolean' => array( 'single_checkbox' ),
155
			'select' => array( 'select', 'radio', 'link' ),
156
			'multi' => array( 'select', 'multiselect', 'radio', 'checkbox', 'link' ),
157
		);
158
159
		/**
160
		 * @filter `gravityview/search/input_types` Change the types of search fields available to a field type
161
		 * @see GravityView_Widget_Search::get_search_input_labels() for the available input types
162
		 * @param array $input_types Associative array: key is field `name`, value is array of GravityView input types (note: use `input_text` for `text`)
163
		 */
164
		$input_types = apply_filters( 'gravityview/search/input_types', $input_types );
165
166
		return $input_types;
167
	}
168
169
	/**
170
	 * Get labels for different types of search bar inputs
171
	 *
172
	 * @since 1.17.5
173
	 *
174
	 * @return array [input type] => input type label
175
	 */
176
	public static function get_search_input_labels() {
177
		/**
178
		 * Input Type labels l10n
179
		 * @see admin-search-widget.js (getSelectInput)
180
		 * @var array
181
		 */
182
		$input_labels = array(
183
			'input_text' => esc_html__( 'Text', 'gravityview' ),
184
			'date' => esc_html__( 'Date', 'gravityview' ),
185
			'select' => esc_html__( 'Select', 'gravityview' ),
186
			'multiselect' => esc_html__( 'Select (multiple values)', 'gravityview' ),
187
			'radio' => esc_html__( 'Radio', 'gravityview' ),
188
			'checkbox' => esc_html__( 'Checkbox', 'gravityview' ),
189
			'single_checkbox' => esc_html__( 'Checkbox', 'gravityview' ),
190
			'link' => esc_html__( 'Links', 'gravityview' ),
191
			'date_range' => esc_html__( 'Date range', 'gravityview' ),
192
		);
193
194
		/**
195
		 * @filter `gravityview/search/input_types` Change the label of search field input types
196
		 * @param array $input_types Associative array: key is input type name, value is label
197
		 */
198
		$input_labels = apply_filters( 'gravityview/search/input_labels', $input_labels );
199
200
		return $input_labels;
201
	}
202
203
	public static function get_search_input_label( $input_type ) {
204
		$labels = self::get_search_input_labels();
205
206
		return rgar( $labels, $input_type, false );
207
	}
208
209
	/**
210
	 * Add script to Views edit screen (admin)
211
	 * @param  mixed $hook
212
	 */
213
	public function add_scripts_and_styles( $hook ) {
214
		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...
215
216
		// Don't process any scripts below here if it's not a GravityView page or the widgets screen
217
		if ( ! gravityview_is_admin_page( $hook ) && ( 'widgets.php' !== $pagenow ) ) {
218
			return;
219
		}
220
221
		$script_min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
222
		$script_source = empty( $script_min ) ? '/source' : '';
223
224
		wp_enqueue_script( 'gravityview_searchwidget_admin', plugins_url( 'assets/js'.$script_source.'/admin-search-widget'.$script_min.'.js', __FILE__ ), array( 'jquery', 'gravityview_views_scripts' ), GravityView_Plugin::version );
225
226
		wp_localize_script( 'gravityview_searchwidget_admin', 'gvSearchVar', array(
227
			'nonce' => wp_create_nonce( 'gravityview_ajaxsearchwidget' ),
228
			'label_nofields' => esc_html__( 'No search fields configured yet.', 'gravityview' ),
229
			'label_addfield' => esc_html__( 'Add Search Field', 'gravityview' ),
230
			'label_label' => esc_html__( 'Label', 'gravityview' ),
231
			'label_searchfield' => esc_html__( 'Search Field', 'gravityview' ),
232
			'label_inputtype' => esc_html__( 'Input Type', 'gravityview' ),
233
			'label_ajaxerror' => esc_html__( 'There was an error loading searchable fields. Save the View or refresh the page to fix this issue.', 'gravityview' ),
234
			'input_labels' => json_encode( self::get_search_input_labels() ),
235
			'input_types' => json_encode( self::get_input_types_by_field_type() ),
236
		) );
237
238
	}
239
240
	/**
241
	 * Add admin script to the no-conflict scripts whitelist
242
	 * @param array $allowed Scripts allowed in no-conflict mode
243
	 * @return array Scripts allowed in no-conflict mode, plus the search widget script
244
	 */
245
	public function register_no_conflict( $allowed ) {
246
		$allowed[] = 'gravityview_searchwidget_admin';
247
		return $allowed;
248
	}
249
250
	/**
251
	 * Ajax
252
	 * Returns the form fields ( only the searchable ones )
253
	 *
254
	 * @access public
255
	 * @return void
256
	 */
257
	public static function get_searchable_fields() {
258
259
		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...
260
			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...
261
		}
262
263
		$form = '';
264
265
		// Fetch the form for the current View
266
		if ( ! empty( $_POST['view_id'] ) ) {
267
268
			$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...
269
270
		} elseif ( ! empty( $_POST['formid'] ) ) {
271
272
			$form = (int) $_POST['formid'];
0 ignored issues
show
introduced by
Detected access of super global var $_POST, probably need manual inspection.
Loading history...
273
274
		} elseif ( ! empty( $_POST['template_id'] ) && class_exists( 'GravityView_Ajax' ) ) {
275
276
			$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...
277
278
		}
279
280
		// fetch form id assigned to the view
281
		$response = self::render_searchable_fields( $form );
282
283
		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...
284
	}
285
286
	/**
287
	 * Generates html for the available Search Fields dropdown
288
	 * @param  int $form_id
289
	 * @param  string $current (for future use)
290
	 * @return string
291
	 */
292
	public static function render_searchable_fields( $form_id = null, $current = '' ) {
293
294
		if ( is_null( $form_id ) ) {
295
			return '';
296
		}
297
298
		// start building output
299
300
		$output = '<select class="gv-search-fields">';
301
302
		$custom_fields = array(
303
			'search_all' => array(
304
				'text' => esc_html__( 'Search Everything', 'gravityview' ),
305
				'type' => 'text',
306
			),
307
			'entry_date' => array(
308
				'text' => esc_html__( 'Entry Date', 'gravityview' ),
309
				'type' => 'date',
310
			),
311
			'entry_id' => array(
312
				'text' => esc_html__( 'Entry ID', 'gravityview' ),
313
				'type' => 'text',
314
			),
315
			'created_by' => array(
316
				'text' => esc_html__( 'Entry Creator', 'gravityview' ),
317
				'type' => 'select',
318
			)
319
		);
320
321
		foreach( $custom_fields as $custom_field_key => $custom_field ) {
322
			$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...
323
		}
324
325
		// Get fields with sub-inputs and no parent
326
		$fields = gravityview_get_form_fields( $form_id, true, true );
327
328
		/**
329
		 * @filter `gravityview/search/searchable_fields` Modify the fields that are displayed as searchable in the Search Bar dropdown\n
330
		 * @since 1.17
331
		 * @see gravityview_get_form_fields() Used to fetch the fields
332
		 * @see GravityView_Widget_Search::get_search_input_types See this method to modify the type of input types allowed for a field
333
		 * @param array $fields Array of searchable fields, as fetched by gravityview_get_form_fields()
334
		 * @param  int $form_id
335
		 */
336
		$fields = apply_filters( 'gravityview/search/searchable_fields', $fields, $form_id );
337
338
		if ( ! empty( $fields ) ) {
339
340
			$blacklist_field_types = apply_filters( 'gravityview_blacklist_field_types', array( 'fileupload', 'post_image', 'post_id', 'section' ), null );
341
342
			foreach ( $fields as $id => $field ) {
343
344
				if ( in_array( $field['type'], $blacklist_field_types ) ) {
345
					continue;
346
				}
347
348
				$types = self::get_search_input_types( $id, $field['type'] );
349
350
				$output .= '<option value="'. $id .'" '. selected( $id, $current, false ).'data-inputtypes="'. esc_attr( $types ) .'">'. esc_html( $field['label'] ) .'</option>';
351
			}
352
		}
353
354
		$output .= '</select>';
355
356
		return $output;
357
358
	}
359
360
	/**
361
	 * Assign an input type according to the form field type
362
	 *
363
	 * @see admin-search-widget.js
364
	 *
365
	 * @param string|int|float $field_id Gravity Forms field ID
366
	 * @param string $field_type Gravity Forms field type (also the `name` parameter of GravityView_Field classes)
367
	 *
368
	 * @return string GV field search input type ('multi', 'boolean', 'select', 'date', 'text')
369
	 */
370
	public static function get_search_input_types( $field_id = '', $field_type = null ) {
371
372
		// @todo - This needs to be improved - many fields have . including products and addresses
373
		if ( false !== strpos( (string) $field_id, '.' ) && in_array( $field_type, array( 'checkbox' ) ) || in_array( $field_id, array( 'is_fulfilled' ) ) ) {
374
			$input_type = 'boolean'; // on/off checkbox
375
		} elseif ( in_array( $field_type, array( 'checkbox', 'post_category', 'multiselect' ) ) ) {
376
			$input_type = 'multi'; //multiselect
377
		} elseif ( in_array( $field_type, array( 'select', 'radio' ) ) ) {
378
			$input_type = 'select';
379
		} elseif ( in_array( $field_type, array( 'date' ) ) || in_array( $field_id, array( 'payment_date' ) ) ) {
380
			$input_type = 'date';
381
		} elseif ( in_array( $field_type, array( 'number' ) ) || in_array( $field_id, array( 'payment_amount' ) ) ) {
382
			$input_type = 'number';
383
		} else {
384
			$input_type = 'text';
385
		}
386
387
		/**
388
		 * @filter `gravityview/extension/search/input_type` Modify the search form input type based on field type
389
		 * @since 1.2
390
		 * @since 1.19.2 Added $field_id parameter
391
		 * @param string $input_type Assign an input type according to the form field type. Defaults: `boolean`, `multi`, `select`, `date`, `text`
392
		 * @param string $field_type Gravity Forms field type (also the `name` parameter of GravityView_Field classes)
393
		 * @param string|int|float $field_id ID of the field being processed
394
		 */
395
		$input_type = apply_filters( 'gravityview/extension/search/input_type', $input_type, $field_type, $field_id );
396
397
		return $input_type;
398
	}
399
400
	/**
401
	 * Display hidden fields to add support for sites using Default permalink structure
402
	 *
403
	 * @since 1.8
404
	 * @return array Search fields, modified if not using permalinks
405
	 */
406
	public function add_no_permalink_fields( $search_fields, $object, $widget_args = array() ) {
407
		/** @global WP_Rewrite $wp_rewrite */
408
		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...
409
410
		// Support default permalink structure
411
		if ( false === $wp_rewrite->using_permalinks() ) {
412
413
			// By default, use current post.
414
			$post_id = 0;
415
416
			// We're in the WordPress Widget context, and an overriding post ID has been set.
417
			if ( ! empty( $widget_args['post_id'] ) ) {
418
				$post_id = absint( $widget_args['post_id'] );
419
			}
420
			// We're in the WordPress Widget context, and the base View ID should be used
421
			else if ( ! empty( $widget_args['view_id'] ) ) {
422
				$post_id = absint( $widget_args['view_id'] );
423
			}
424
425
			$args = gravityview_get_permalink_query_args( $post_id );
426
427
			// Add hidden fields to the search form
428
			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...
429
				$search_fields[] = array(
430
					'name'  => $key,
431
					'input' => 'hidden',
432
					'value' => $value,
433
				);
434
			}
435
		}
436
437
		return $search_fields;
438
	}
439
440
441
	/** --- Frontend --- */
442
443
	/**
444
	 * Calculate the search criteria to filter entries
445
	 * @param  array $search_criteria
446
	 * @return array
447
	 */
448 1
	public function filter_entries( $search_criteria ) {
449
450 1
		if( 'post' === $this->search_method ) {
451
			$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...
452
		} else {
453 1
			$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...
454
		}
455
456 1
		do_action( 'gravityview_log_debug', sprintf( '%s[filter_entries] Requested $_%s: ', get_class( $this ), $this->search_method ), $get );
457
458 1
		if ( empty( $get ) || ! is_array( $get ) ) {
459 1
			return $search_criteria;
460
		}
461
462 1
		$get = stripslashes_deep( $get );
463
464 1
		$get = gv_map_deep( $get, 'rawurldecode' );
465
466
		// Make sure array key is set up
467 1
		$search_criteria['field_filters'] = rgar( $search_criteria, 'field_filters', array() );
468
469
		// add free search
470 1
		if ( ! empty( $get['gv_search'] ) ) {
471
472 1
			$search_all_value = trim( $get['gv_search'] );
473
474
			/**
475
			 * @filter `gravityview/search-all-split-words` Search for each word separately or the whole phrase?
476
			 * @since 1.20.2
477
			 * @param bool $split_words True: split a phrase into words; False: search whole word only [Default: true]
478
			 */
479 1
			$split_words = apply_filters( 'gravityview/search-all-split-words', true );
480
481 1
			if( $split_words ) {
482
483
				// Search for a piece
484 1
				$words = explode( ' ', $search_all_value );
485
486 1
				$words = array_filter( $words );
487
488
			} else {
489
490
				// Replace multiple spaces with one space
491 1
				$search_all_value = preg_replace( '/\s+/ism', ' ', $search_all_value );
492
493 1
				$words = array( $search_all_value );
494
			}
495
496 1
			foreach ( $words as $word ) {
497 1
				$search_criteria['field_filters'][] = array(
498 1
					'key' => null, // The field ID to search
499 1
					'value' => $word, // The value to search
500 1
					'operator' => 'contains', // What to search in. Options: `is` or `contains`
501
				);
502
			}
503
		}
504
505
		//start date & end date
506 1
		$curr_start = !empty( $get['gv_start'] ) ? $get['gv_start'] : '';
0 ignored issues
show
introduced by
Expected 1 space after "!"; 0 found
Loading history...
507 1
		$curr_end = !empty( $get['gv_start'] ) ? $get['gv_end'] : '';
0 ignored issues
show
introduced by
Expected 1 space after "!"; 0 found
Loading history...
508
509
		/**
510
		 * @filter `gravityview_date_created_adjust_timezone` Whether to adjust the timezone for entries. \n
511
		 * date_created is stored in UTC format. Convert search date into UTC (also used on templates/fields/date_created.php)
512
		 * @since 1.12
513
		 * @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
514
		 * @param[in] string $context Where the filter is being called from. `search` in this case.
515
		 */
516 1
		$adjust_tz = apply_filters( 'gravityview_date_created_adjust_timezone', true, 'search' );
517
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
518
519
		/**
520
		 * Don't set $search_criteria['start_date'] if start_date is empty as it may lead to bad query results (GFAPI::get_entries)
521
		 */
522 1
		if( !empty( $curr_start ) ) {
0 ignored issues
show
introduced by
Expected 1 space after "!"; 0 found
Loading history...
523 1
			$search_criteria['start_date'] = $adjust_tz ? get_gmt_from_date( $curr_start ) : $curr_start;
524
		}
525 1
		if( !empty( $curr_end ) ) {
0 ignored issues
show
introduced by
Expected 1 space after "!"; 0 found
Loading history...
526 1
			$search_criteria['end_date'] = $adjust_tz ? get_gmt_from_date( $curr_end ) : $curr_end;
527
		}
528
529
		// search for a specific entry ID
530 1
		if ( ! empty( $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...
531 1
			$search_criteria['field_filters'][] = array(
532 1
				'key' => 'id',
533 1
				'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...
534 1
				'operator' => '=',
535
			);
536
		}
537
538
		// search for a specific Created_by ID
539 1
		if ( ! empty( $get[ 'gv_by' ] ) ) {
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...
540 1
			$search_criteria['field_filters'][] = array(
541 1
				'key' => 'created_by',
542 1
				'value' => absint( $get['gv_by'] ),
543 1
				'operator' => '=',
544
			);
545
		}
546
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
547
548
		// Get search mode passed in URL
549 1
		$mode = isset( $get['mode'] ) && in_array( $get['mode'], array( 'any', 'all' ) ) ?  $get['mode'] : 'any';
550
551
		// get the other search filters
552 1
		foreach ( $get as $key => $value ) {
553
554 1
			if ( 0 !== strpos( $key, 'filter_' ) || empty( $value ) || ( is_array( $value ) && count( $value ) === 1 && empty( $value[0] ) ) ) {
0 ignored issues
show
introduced by
Found "=== 1". Use Yoda Condition checks, you must
Loading history...
555 1
				continue;
556
			}
557
558
			// could return simple filter or multiple filters
559
			$filter = $this->prepare_field_filter( $key, $value );
560
561
			if ( isset( $filter[0]['value'] ) ) {
562
				$search_criteria['field_filters'] = array_merge( $search_criteria['field_filters'], $filter );
563
564
				// if date range type, set search mode to ALL
565
				if ( ! empty( $filter[0]['operator'] ) && in_array( $filter[0]['operator'], array( '>=', '<=', '>', '<' ) ) ) {
566
					$mode = 'all';
567
				}
568
			} elseif( !empty( $filter ) ) {
0 ignored issues
show
introduced by
Expected 1 space after "!"; 0 found
Loading history...
569
				$search_criteria['field_filters'][] = $filter;
570
			}
571
		}
572
573
		/**
574
		 * @filter `gravityview/search/mode` Set the Search Mode (`all` or `any`)
575
		 * @since 1.5.1
576
		 * @param[out,in] string $mode Search mode (`any` vs `all`)
577
		 */
578 1
		$search_criteria['field_filters']['mode'] = apply_filters( 'gravityview/search/mode', $mode );
579
580 1
		do_action( 'gravityview_log_debug', sprintf( '%s[filter_entries] Returned Search Criteria: ', get_class( $this ) ), $search_criteria );
581
582 1
		unset( $get );
583
584 1
		return $search_criteria;
585
	}
586
587
	/**
588
	 * Prepare the field filters to GFAPI
589
	 *
590
	 * 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.
591
	 *
592
	 * Format searched values
593
	 * @param  string $key   $_GET/$_POST search key
594
	 * @param  string $value $_GET/$_POST search value
595
	 * @return array        1 or 2 deph levels
596
	 */
597
	public function prepare_field_filter( $key, $value ) {
598
599
		$gravityview_view = GravityView_View::getInstance();
600
601
		$field_id = str_replace( 'filter_', '', $key );
602
603
		// calculates field_id, removing 'filter_' and for '_' for advanced fields ( like name or checkbox )
604
		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...
605
			$field_id = str_replace( '_', '.', $field_id );
606
		}
607
608
		// get form field array
609
		$form = $gravityview_view->getForm();
610
		$form_field = gravityview_get_field( $form, $field_id );
611
612
		// default filter array
613
		$filter = array(
614
			'key' => $field_id,
615
			'value' => $value,
616
		);
617
618
		switch ( $form_field['type'] ) {
619
620
			case 'select':
621
			case 'radio':
622
				$filter['operator'] = 'is';
623
				break;
624
625
			case 'post_category':
626
627
				if ( ! is_array( $value ) ) {
628
					$value = array( $value );
629
				}
630
631
				// Reset filter variable
632
				$filter = array();
633
634
				foreach ( $value as $val ) {
635
					$cat = get_term( $val, 'category' );
636
					$filter[] = array(
637
						'key' => $field_id,
638
						'value' => esc_attr( $cat->name ) . ':' . $val,
639
						'operator' => 'is',
640
					);
641
				}
642
643
				break;
644
645
			case 'multiselect':
646
647
				if ( ! is_array( $value ) ) {
648
					break;
649
				}
650
651
				// Reset filter variable
652
				$filter = array();
653
654
				foreach ( $value as $val ) {
655
					$filter[] = array( 'key' => $field_id, 'value' => $val );
656
				}
657
658
				break;
659
660
			case 'checkbox':
661
				// convert checkbox on/off into the correct search filter
662
				if ( false !== strpos( $field_id, '.' ) && ! empty( $form_field['inputs'] ) && ! empty( $form_field['choices'] ) ) {
663
					foreach ( $form_field['inputs'] as $k => $input ) {
664
						if ( $input['id'] == $field_id ) {
665
							$filter['value'] = $form_field['choices'][ $k ]['value'];
666
							$filter['operator'] = 'is';
667
							break;
668
						}
669
					}
670
				} elseif ( is_array( $value ) ) {
671
672
					// Reset filter variable
673
					$filter = array();
674
675
					foreach ( $value as $val ) {
676
						$filter[] = array(
677
							'key'   => $field_id,
678
							'value' => $val,
679
							'operator' => 'is',
680
						);
681
					}
682
				}
683
684
				break;
685
686
			case 'name':
687
			case 'address':
688
689
				if ( false === strpos( $field_id, '.' ) ) {
690
691
					$words = explode( ' ', $value );
692
693
					$filters = array();
694
					foreach ( $words as $word ) {
695
						if ( ! empty( $word ) && strlen( $word ) > 1 ) {
696
							// Keep the same key for each filter
697
							$filter['value'] = $word;
698
							// Add a search for the value
699
							$filters[] = $filter;
700
						}
701
					}
702
703
					$filter = $filters;
704
				}
705
706
				break;
707
708
			case 'date':
709
710
				if ( is_array( $value ) ) {
711
712
					// Reset filter variable
713
					$filter = array();
714
715
					foreach ( $value as $k => $date ) {
716
						if ( empty( $date ) ) {
717
							continue;
718
						}
719
						$operator = 'start' === $k ? '>=' : '<=';
720
721
						/**
722
						 * @hack
723
						 * @since 1.16.3
724
						 * Safeguard until GF implements '<=' operator
725
						 */
726
						if( !GFFormsModel::is_valid_operator( $operator ) && $operator === '<=' ) {
0 ignored issues
show
introduced by
Expected 1 space after "!"; 0 found
Loading history...
727
							$operator = '<';
728
							$date = date( 'Y-m-d', strtotime( $date . ' +1 day' ) );
729
						}
730
731
						$filter[] = array(
732
							'key' => $field_id,
733
							'value' => self::get_formatted_date( $date, 'Y-m-d' ),
734
							'operator' => $operator,
735
						);
736
					}
737
				} else {
738
					$filter['value'] = self::get_formatted_date( $value, 'Y-m-d' );
739
				}
740
741
				break;
742
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
743
744
		} // switch field type
745
746
		return $filter;
747
	}
748
749
	/**
750
	 * Get the Field Format form GravityForms
751
	 *
752
	 * @param GF_Field_Date $field The field object
753
	 * @since 1.10
754
	 *
755
	 * @return string Format of the date in the database
756
	 */
757
	public static function get_date_field_format( GF_Field_Date $field ) {
758
		$format = 'm/d/Y';
759
		$datepicker = array(
760
			'mdy' => 'm/d/Y',
761
			'dmy' => 'd/m/Y',
762
			'dmy_dash' => 'd-m-Y',
763
			'dmy_dot' => 'm.d.Y',
764
			'ymd_slash' => 'Y/m/d',
765
			'ymd_dash' => 'Y-m-d',
766
			'ymd_dot' => 'Y.m.d',
767
		);
768
769
		if ( ! empty( $field->dateFormat ) && isset( $datepicker[ $field->dateFormat ] ) ){
770
			$format = $datepicker[ $field->dateFormat ];
771
		}
772
773
		return $format;
774
	}
775
776
	/**
777
	 * Format a date value
778
	 *
779
	 * @param string $value Date value input
780
	 * @param string $format Wanted formatted date
781
	 * @return string
782
	 */
783
	public static function get_formatted_date( $value = '', $format = 'Y-m-d' ) {
784
		$date = date_create( $value );
785
		if ( empty( $date ) ) {
786
			do_action( 'gravityview_log_debug', sprintf( '%s[get_formatted_date] Date format not valid: ', get_class( self::$instance ) ), $value );
787
			return '';
788
		}
789
		return $date->format( $format );
790
	}
791
792
793
	/**
794
	 * Include this extension templates path
795
	 * @param array $file_paths List of template paths ordered
796
	 */
797
	public function add_template_path( $file_paths ) {
798
799
		// Index 100 is the default GravityView template path.
800
		$file_paths[102] = self::$file . 'templates/';
801
802
		return $file_paths;
803
	}
804
805
	/**
806
	 * Check whether the configured search fields have a date field
807
	 *
808
	 * @since 1.17.5
809
	 *
810
	 * @param array $search_fields
811
	 *
812
	 * @return bool True: has a `date` or `date_range` field
813
	 */
814
	private function has_date_field( $search_fields ) {
815
816
		$has_date = false;
817
818
		foreach ( $search_fields as $k => $field ) {
819
			if ( in_array( $field['input'], array( 'date', 'date_range', 'entry_date' ) ) ) {
820
				$has_date = true;
821
				break;
822
			}
823
		}
824
825
		return $has_date;
826
	}
827
828
	/**
829
	 * Renders the Search Widget
830
	 * @param array $widget_args
831
	 * @param string $content
832
	 * @param string $context
833
	 *
834
	 * @return void
835
	 */
836
	public function render_frontend( $widget_args, $content = '', $context = '' ) {
837
		/** @var GravityView_View $gravityview_view */
838
		$gravityview_view = GravityView_View::getInstance();
839
840
		if ( empty( $gravityview_view ) ) {
841
			do_action( 'gravityview_log_debug', sprintf( '%s[render_frontend]: $gravityview_view not instantiated yet.', get_class( $this ) ) );
842
			return;
843
		}
844
845
		// get configured search fields
846
		$search_fields = ! empty( $widget_args['search_fields'] ) ? json_decode( $widget_args['search_fields'], true ) : '';
847
848
		if ( empty( $search_fields ) || ! is_array( $search_fields ) ) {
849
			do_action( 'gravityview_log_debug', sprintf( '%s[render_frontend] No search fields configured for widget:', get_class( $this ) ), $widget_args );
850
			return;
851
		}
852
0 ignored issues
show
Coding Style introduced by
Functions must not contain multiple empty lines in a row; found 2 empty lines
Loading history...
853
854
		// prepare fields
855
		foreach ( $search_fields as $k => $field ) {
856
857
			$updated_field = $field;
858
859
			$updated_field = $this->get_search_filter_details( $updated_field );
860
861
			switch ( $field['field'] ) {
862
863
				case 'search_all':
864
					$updated_field['key'] = 'search_all';
865
					$updated_field['input'] = 'search_all';
866
					$updated_field['value'] = $this->rgget_or_rgpost( 'gv_search' );
867
					break;
868
869
				case 'entry_date':
870
					$updated_field['key'] = 'entry_date';
871
					$updated_field['input'] = 'entry_date';
872
					$updated_field['value'] = array(
873
						'start' => $this->rgget_or_rgpost( 'gv_start' ),
874
						'end' => $this->rgget_or_rgpost( 'gv_end' ),
875
					);
876
					break;
877
878
				case 'entry_id':
879
					$updated_field['key'] = 'entry_id';
880
					$updated_field['input'] = 'entry_id';
881
					$updated_field['value'] = $this->rgget_or_rgpost( 'gv_id' );
882
					break;
883
884
				case 'created_by':
885
					$updated_field['key'] = 'created_by';
886
					$updated_field['name'] = 'gv_by';
887
					$updated_field['value'] = $this->rgget_or_rgpost( 'gv_by' );
888
					$updated_field['choices'] = self::get_created_by_choices();
889
					break;
890
			}
891
892
			$search_fields[ $k ] = $updated_field;
893
		}
894
895
		do_action( 'gravityview_log_debug', sprintf( '%s[render_frontend] Calculated Search Fields: ', get_class( $this ) ), $search_fields );
896
897
		/**
898
		 * @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.
899
		 * @param array $search_fields Array of search filters with `key`, `label`, `value`, `type`, `choices` keys
900
		 * @param GravityView_Widget_Search $this Current widget object
901
		 * @param array $widget_args Args passed to this method. {@since 1.8}
902
		 * @var array
903
		 */
904
		$gravityview_view->search_fields = apply_filters( 'gravityview_widget_search_filters', $search_fields, $this, $widget_args );
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...
905
906
		$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...
907
908
		/** @since 1.14 */
909
		$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...
910
911
		$custom_class = ! empty( $widget_args['custom_class'] ) ? $widget_args['custom_class'] : '';
912
913
		$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...
914
915
		$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...
916
917
		if ( $this->has_date_field( $search_fields ) ) {
918
			// enqueue datepicker stuff only if needed!
919
			$this->enqueue_datepicker();
920
		}
921
922
		$this->maybe_enqueue_flexibility();
923
924
		$gravityview_view->render( 'widget', 'search', false );
925
	}
926
927
	/**
928
	 * Get the search class for a search form
929
	 *
930
	 * @since 1.5.4
931
	 *
932
	 * @return string Sanitized CSS class for the search form
933
	 */
934
	public static function get_search_class( $custom_class = '' ) {
935
		$gravityview_view = GravityView_View::getInstance();
936
937
		$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...
938
939
		if ( ! empty( $custom_class )  ) {
940
			$search_class .= ' '.$custom_class;
941
		}
942
943
		/**
944
		 * @filter `gravityview_search_class` Modify the CSS class for the search form
945
		 * @param string $search_class The CSS class for the search form
946
		 */
947
		$search_class = apply_filters( 'gravityview_search_class', $search_class );
948
949
		// Is there an active search being performed? Used by fe-views.js
950
		$search_class .= GravityView_frontend::getInstance()->isSearch() ? ' gv-is-search' : '';
951
952
		return gravityview_sanitize_html_class( $search_class );
953
	}
954
955
956
	/**
957
	 * Calculate the search form action
958
	 * @since 1.6
959
	 *
960
	 * @return string
961
	 */
962
	public static function get_search_form_action() {
963
		$gravityview_view = GravityView_View::getInstance();
964
965
		$post_id = $gravityview_view->getPostId() ? $gravityview_view->getPostId() : $gravityview_view->getViewId();
966
967
		$url = add_query_arg( array(), get_permalink( $post_id ) );
968
969
		return esc_url( $url );
970
	}
971
972
	/**
973
	 * Get the label for a search form field
974
	 * @param  array $field      Field setting as sent by the GV configuration - has `field`, `input` (input type), and `label` keys
975
	 * @param  array $form_field Form field data, as fetched by `gravityview_get_field()`
976
	 * @return string             Label for the search form
977
	 */
978
	private static function get_field_label( $field, $form_field = array() ) {
979
980
		$label = rgget( 'label', $field );
981
982
		if( '' === $label ) {
983
984
			$label = isset( $form_field['label'] ) ? $form_field['label'] : '';
985
986
			switch( $field['field'] ) {
987
				case 'search_all':
988
					$label = __( 'Search Entries:', 'gravityview' );
989
					break;
990
				case 'entry_date':
991
					$label = __( 'Filter by date:', 'gravityview' );
992
					break;
993
				case 'entry_id':
994
					$label = __( 'Entry ID:', 'gravityview' );
995
					break;
996
				default:
997
					// If this is a field input, not a field
998
					if ( strpos( $field['field'], '.' ) > 0 && ! empty( $form_field['inputs'] ) ) {
999
1000
						// Get the label for the field in question, which returns an array
1001
						$items = wp_list_filter( $form_field['inputs'], array( 'id' => $field['field'] ) );
1002
1003
						// Get the item with the `label` key
1004
						$values = wp_list_pluck( $items, 'label' );
1005
1006
						// There will only one item in the array, but this is easier
1007
						foreach ( $values as $value ) {
1008
							$label = $value;
1009
							break;
1010
						}
1011
					}
1012
			}
1013
		}
1014
1015
		/**
1016
		 * @filter `gravityview_search_field_label` Modify the label for a search field. Supports returning HTML
1017
		 * @since 1.17.3 Added $field parameter
1018
		 * @param[in,out] string $label Existing label text, sanitized.
1019
		 * @param[in] array $form_field Gravity Forms field array, as returned by `GFFormsModel::get_field()`
1020
		 * @param[in] array $field Field setting as sent by the GV configuration - has `field`, `input` (input type), and `label` keys
1021
		 */
1022
		$label = apply_filters( 'gravityview_search_field_label', esc_attr( $label ), $form_field, $field );
1023
1024
		return $label;
1025
	}
1026
1027
	/**
1028
	 * Prepare search fields to frontend render with other details (label, field type, searched values)
1029
	 *
1030
	 * @param array $field
1031
	 * @return array
1032
	 */
1033
	private function get_search_filter_details( $field ) {
1034
1035
		$gravityview_view = GravityView_View::getInstance();
1036
1037
		$form = $gravityview_view->getForm();
1038
1039
		// for advanced field ids (eg, first name / last name )
1040
		$name = 'filter_' . str_replace( '.', '_', $field['field'] );
1041
1042
		// get searched value from $_GET/$_POST (string or array)
1043
		$value = $this->rgget_or_rgpost( $name );
1044
1045
		// get form field details
1046
		$form_field = gravityview_get_field( $form, $field['field'] );
1047
1048
		$filter = array(
1049
			'key' => $field['field'],
1050
			'name' => $name,
1051
			'label' => self::get_field_label( $field, $form_field ),
1052
			'input' => $field['input'],
1053
			'value' => $value,
1054
			'type' => $form_field['type'],
1055
		);
1056
1057
		// collect choices
1058
		if ( 'post_category' === $form_field['type'] && ! empty( $form_field['displayAllCategories'] ) && empty( $form_field['choices'] ) ) {
1059
			$filter['choices'] = gravityview_get_terms_choices();
1060
		} elseif ( ! empty( $form_field['choices'] ) ) {
1061
			$filter['choices'] = $form_field['choices'];
1062
		}
1063
1064
		if ( 'date_range' === $field['input'] && empty( $value ) ) {
1065
			$filter['value'] = array( 'start' => '', 'end' => '' );
1066
		}
1067
1068
		return $filter;
1069
1070
	}
1071
1072
	/**
1073
	 * Calculate the search choices for the users
1074
	 *
1075
	 * @since 1.8
1076
	 *
1077
	 * @return array Array of user choices (value = ID, text = display name)
1078
	 */
1079
	private static function get_created_by_choices() {
1080
1081
		/**
1082
		 * filter gravityview/get_users/search_widget
1083
		 * @see \GVCommon::get_users
1084
		 */
1085
		$users = GVCommon::get_users( 'search_widget', array( 'fields' => array( 'ID', 'display_name' ) ) );
1086
1087
		$choices = array();
1088
		foreach ( $users as $user ) {
1089
			$choices[] = array(
1090
				'value' => $user->ID,
1091
				'text' => $user->display_name,
1092
			);
1093
		}
1094
1095
		return $choices;
1096
	}
1097
1098
1099
	/**
1100
	 * Output the Clear Search Results button
1101
	 * @since 1.5.4
1102
	 */
1103
	public static function the_clear_search_button() {
1104
		$gravityview_view = GravityView_View::getInstance();
1105
1106
		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...
1107
1108
			$url = strtok( add_query_arg( array() ), '?' );
1109
1110
			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...
1111
1112
		}
1113
	}
1114
1115
	/**
1116
	 * Based on the search method, fetch the value for a specific key
1117
	 *
1118
	 * @since 1.16.4
1119
	 *
1120
	 * @param string $name Name of the request key to fetch the value for
1121
	 *
1122
	 * @return mixed|string Value of request at $name key. Empty string if empty.
1123
	 */
1124
	private function rgget_or_rgpost( $name ) {
1125
		$value = 'get' === $this->search_method ? rgget( $name ) : rgpost( $name );
1126
1127
		$value = stripslashes_deep( $value );
1128
1129
		$value = gv_map_deep( $value, 'rawurldecode' );
1130
1131
		$value = gv_map_deep( $value, '_wp_specialchars' );
1132
1133
		return $value;
1134
	}
1135
1136
1137
	/**
1138
	 * Require the datepicker script for the frontend GV script
1139
	 * @param array $js_dependencies Array of existing required scripts for the fe-views.js script
1140
	 * @return array Array required scripts, with `jquery-ui-datepicker` added
1141
	 */
1142
	public function add_datepicker_js_dependency( $js_dependencies ) {
1143
1144
		$js_dependencies[] = 'jquery-ui-datepicker';
1145
1146
		return $js_dependencies;
1147
	}
1148
1149
	/**
1150
	 * Modify the array passed to wp_localize_script()
1151
	 *
1152
	 * @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...
1153
	 * @param array $view_data View data array with View settings
1154
	 *
1155
	 * @return array
1156
	 */
1157
	public function add_datepicker_localization( $localizations = array(), $view_data = array() ) {
1158
		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...
1159
1160
		/**
1161
		 * @filter `gravityview_datepicker_settings` Modify the datepicker settings
1162
		 * @see http://api.jqueryui.com/datepicker/ Learn what settings are available
1163
		 * @see http://www.renegadetechconsulting.com/tutorials/jquery-datepicker-and-wordpress-i18n Thanks for the helpful information on $wp_locale
1164
		 * @param array $js_localization The data padded to the Javascript file
1165
		 * @param array $view_data View data array with View settings
1166
		 */
1167
		$datepicker_settings = apply_filters( 'gravityview_datepicker_settings', array(
1168
			'yearRange' => '-5:+5',
1169
			'changeMonth' => true,
1170
			'changeYear' => true,
1171
			'closeText' => esc_attr_x( 'Close', 'Close calendar', 'gravityview' ),
1172
			'prevText' => esc_attr_x( 'Prev', 'Previous month in calendar', 'gravityview' ),
1173
			'nextText' => esc_attr_x( 'Next', 'Next month in calendar', 'gravityview' ),
1174
			'currentText' => esc_attr_x( 'Today', 'Today in calendar', 'gravityview' ),
1175
			'weekHeader' => esc_attr_x( 'Week', 'Week in calendar', 'gravityview' ),
1176
			'monthStatus'       => __( 'Show a different month', 'gravityview' ),
1177
			'monthNames'        => array_values( $wp_locale->month ),
1178
			'monthNamesShort'   => array_values( $wp_locale->month_abbrev ),
1179
			'dayNames'          => array_values( $wp_locale->weekday ),
1180
			'dayNamesShort'     => array_values( $wp_locale->weekday_abbrev ),
1181
			'dayNamesMin'       => array_values( $wp_locale->weekday_initial ),
1182
			// get the start of week from WP general setting
1183
			'firstDay'          => get_option( 'start_of_week' ),
1184
			// is Right to left language? default is false
1185
			'isRTL'             => is_rtl(),
1186
		), $view_data );
1187
1188
		$localizations['datepicker'] = $datepicker_settings;
1189
1190
		return $localizations;
1191
1192
	}
1193
1194
	/**
1195
	 * Register search widget scripts, including Flexibility
1196
	 *
1197
	 * @see https://github.com/10up/flexibility
1198
	 *
1199
	 * @since 1.17
1200
	 *
1201
	 * @return void
1202
	 */
1203
	public function register_scripts() {
1204
1205
		wp_register_script( 'gv-flexibility', plugins_url( 'assets/lib/flexibility/flexibility.js', GRAVITYVIEW_FILE ), array(), GravityView_Plugin::version, true );
1206
1207
	}
1208
1209
	/**
1210
	 * If the current visitor is running IE 8 or 9, enqueue Flexibility
1211
	 *
1212
	 * @since 1.17
1213
	 *
1214
	 * @return void
1215
	 */
1216
	private function maybe_enqueue_flexibility() {
1217
		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...
1218
			wp_enqueue_script( 'gv-flexibility' );
1219
		}
1220
	}
1221
1222
	/**
1223
	 * Enqueue the datepicker script
1224
	 *
1225
	 * It sets the $gravityview->datepicker_class parameter
1226
	 *
1227
	 * @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.
1228
	 * @return void
1229
	 */
1230
	public function enqueue_datepicker() {
1231
		$gravityview_view = GravityView_View::getInstance();
1232
1233
		wp_enqueue_script( 'jquery-ui-datepicker' );
1234
1235
		add_filter( 'gravityview_js_dependencies', array( $this, 'add_datepicker_js_dependency' ) );
1236
		add_filter( 'gravityview_js_localization', array( $this, 'add_datepicker_localization' ), 10, 2 );
1237
1238
		$scheme = is_ssl() ? 'https://' : 'http://';
1239
		wp_enqueue_style( 'jquery-ui-datepicker', $scheme.'ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/smoothness/jquery-ui.css' );
1240
1241
		/**
1242
		 * @filter `gravityview_search_datepicker_class`
1243
		 * 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.
1244
		 * @param string $css_class CSS class to use. Default: `gv-datepicker datepicker mdy` \n
1245
		 * Options are:
1246
		 * - `mdy` (mm/dd/yyyy)
1247
		 * - `dmy` (dd/mm/yyyy)
1248
		 * - `dmy_dash` (dd-mm-yyyy)
1249
		 * - `dmy_dot` (dd.mm.yyyy)
1250
		 * - `ymp_slash` (yyyy/mm/dd)
1251
		 * - `ymd_dash` (yyyy-mm-dd)
1252
		 * - `ymp_dot` (yyyy.mm.dd)
1253
		 */
1254
		$datepicker_class = apply_filters( 'gravityview_search_datepicker_class', 'gv-datepicker datepicker mdy' );
1255
1256
		$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...
1257
1258
	}
1259
1260
1261
} // end class
1262
1263
new GravityView_Widget_Search;