Completed
Push — develop ( d6e54c...488d60 )
by Zack
17:10
created

GravityView_Widget_Search::render_frontend()   F

Complexity

Conditions 15
Paths 387

Size

Total Lines 90
Code Lines 48

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 240

Importance

Changes 0
Metric Value
cc 15
eloc 48
nc 387
nop 3
dl 0
loc 90
ccs 0
cts 49
cp 0
crap 240
rs 3.7313
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 30 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 {
0 ignored issues
show
Coding Style introduced by
Possible parse error: class missing opening or closing brace
Loading history...
17
18
	public static $file;
19
	public static $instance;
20
21
	private $search_filters = array();
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 1
	public function __construct() {
31
32 1
		$this->widget_id = 'search_bar';
33
		$this->widget_description = esc_html__( 'Search form for searching entries.', 'gravityview' );
34 1
35
		self::$instance = &$this;
36 1
37
		self::$file = plugin_dir_path( __FILE__ );
38 1
39
		$default_values = array( 'header' => 0, 'footer' => 0 );
40
41 1
		$settings = array(
42 1
			'search_layout' => array(
43
				'type' => 'radio',
44 1
				'full_width' => true,
45 1
				'label' => esc_html__( 'Search Layout', 'gravityview' ),
46
				'value' => 'horizontal',
47 1
				'options' => array(
48 1
					'horizontal' => esc_html__( 'Horizontal', 'gravityview' ),
49
					'vertical' => esc_html__( 'Vertical', 'gravityview' ),
50
				),
51
			),
52 1
			'search_clear' => array(
53 1
				'type' => 'checkbox',
54
				'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 1
			'search_mode' => array(
64
				'type' => 'radio',
65 1
				'full_width' => true,
66 1
				'label' => esc_html__( 'Search Mode', 'gravityview' ),
67 1
				'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 1
				'value' => 'any',
69
				'class' => 'hide-if-js',
70 1
				'options' => array(
71 1
					'any' => esc_html__( 'Match Any Fields', 'gravityview' ),
72
					'all' => esc_html__( 'Match All Fields', 'gravityview' ),
73
				),
74
			),
75 1
		);
76
77
		if ( ! $this->is_registered() ) {
78 1
			// frontend - filter entries
79
			add_filter( 'gravityview_fe_search_criteria', array( $this, 'filter_entries' ), 10, 3 );
80
81 1
			// frontend - add template path
82
			add_filter( 'gravityview_template_paths', array( $this, 'add_template_path' ) );
83
84 1
			// Add hidden fields for "Default" permalink structure
85
			add_filter( 'gravityview_widget_search_filters', array( $this, 'add_no_permalink_fields' ), 10, 3 );
86
87 1
			// admin - add scripts - run at 1100 to make sure GravityView_Admin_Views::add_scripts_and_styles() runs first at 999
88 1
			add_action( 'admin_enqueue_scripts', array( $this, 'add_scripts_and_styles' ), 1100 );
89 1
			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 1
			// ajax - get the searchable fields
93
			add_action( 'wp_ajax_gv_searchable_fields', array( 'GravityView_Widget_Search', 'get_searchable_fields' ) );
94
		}
95 1
96
		parent::__construct( esc_html__( 'Search Bar', 'gravityview' ), null, $default_values, $settings );
97 1
98
		// calculate the search method (POST / GET)
99
		$this->set_search_method();
100
	}
101
102
	/**
103
	 * @return GravityView_Widget_Search
104
	 */
105
	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
		if ( empty( self::$instance ) ) {
107
			self::$instance = new GravityView_Widget_Search;
108
		}
109
		return self::$instance;
110
	}
111
112
	/**
113 1
	 * Sets the search method to GET (default) or POST
114
	 * @since 1.16.4
115
	 */
116
	private function set_search_method() {
117
		/**
118
		 * @filter `gravityview/search/method` Modify the search form method (GET / POST)
119
		 * @since 1.16.4
120 1
		 * @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 1
		 */
123
		$method = apply_filters( 'gravityview/search/method', $this->search_method );
124 1
125 1
		$method = strtolower( $method );
126
127
		$this->search_method = in_array( $method, array( 'get', 'post' ) ) ? $method : 'get';
128
	}
129
130
	/**
131
	 * Returns the search method
132
	 * @since 1.16.4
133
	 * @return string
134
	 */
135
	public function get_search_method() {
136
		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_is_admin_page( $hook ) && ( 'widgets.php' !== $pagenow ) ) {
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' );
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 );
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
		foreach( $custom_fields as $custom_field_key => $custom_field ) {
329
			$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...
330
		}
331
332
		// Get fields with sub-inputs and no parent
333
		$fields = gravityview_get_form_fields( $form_id, true, true );
334
335
		/**
336
		 * @filter `gravityview/search/searchable_fields` Modify the fields that are displayed as searchable in the Search Bar dropdown\n
337
		 * @since 1.17
338
		 * @see gravityview_get_form_fields() Used to fetch the fields
339
		 * @see GravityView_Widget_Search::get_search_input_types See this method to modify the type of input types allowed for a field
340
		 * @param array $fields Array of searchable fields, as fetched by gravityview_get_form_fields()
341
		 * @param  int $form_id
342
		 */
343
		$fields = apply_filters( 'gravityview/search/searchable_fields', $fields, $form_id );
344
345
		if ( ! empty( $fields ) ) {
346
347
			$blacklist_field_types = apply_filters( 'gravityview_blacklist_field_types', array( 'fileupload', 'post_image', 'post_id', 'section' ), null );
348
349
			foreach ( $fields as $id => $field ) {
350
351
				if ( in_array( $field['type'], $blacklist_field_types ) ) {
352
					continue;
353
				}
354
355
				$types = self::get_search_input_types( $id, $field['type'] );
356
357
				$output .= '<option value="'. $id .'" '. selected( $id, $current, false ).'data-inputtypes="'. esc_attr( $types ) .'">'. esc_html( $field['label'] ) .'</option>';
358
			}
359
		}
360
361
		$output .= '</select>';
362
363
		return $output;
364
365
	}
366
367
	/**
368
	 * Assign an input type according to the form field type
369
	 *
370
	 * @see admin-search-widget.js
371
	 *
372
	 * @param string|int|float $field_id Gravity Forms field ID
373
	 * @param string $field_type Gravity Forms field type (also the `name` parameter of GravityView_Field classes)
374
	 *
375
	 * @return string GV field search input type ('multi', 'boolean', 'select', 'date', 'text')
376
	 */
377
	public static function get_search_input_types( $field_id = '', $field_type = null ) {
378
379
		// @todo - This needs to be improved - many fields have . including products and addresses
380
		if ( false !== strpos( (string) $field_id, '.' ) && in_array( $field_type, array( 'checkbox' ) ) || in_array( $field_id, array( 'is_fulfilled' ) ) ) {
381
			$input_type = 'boolean'; // on/off checkbox
382
		} elseif ( in_array( $field_type, array( 'checkbox', 'post_category', 'multiselect' ) ) ) {
383
			$input_type = 'multi'; //multiselect
384
		} elseif ( in_array( $field_type, array( 'select', 'radio' ) ) ) {
385
			$input_type = 'select';
386
		} elseif ( in_array( $field_type, array( 'date' ) ) || in_array( $field_id, array( 'payment_date' ) ) ) {
387
			$input_type = 'date';
388
		} elseif ( in_array( $field_type, array( 'number' ) ) || in_array( $field_id, array( 'payment_amount' ) ) ) {
389
			$input_type = 'number';
390
		} else {
391
			$input_type = 'text';
392
		}
393
394
		/**
395
		 * @filter `gravityview/extension/search/input_type` Modify the search form input type based on field type
396
		 * @since 1.2
397
		 * @since 1.19.2 Added $field_id parameter
398
		 * @param string $input_type Assign an input type according to the form field type. Defaults: `boolean`, `multi`, `select`, `date`, `text`
399
		 * @param string $field_type Gravity Forms field type (also the `name` parameter of GravityView_Field classes)
400
		 * @param string|int|float $field_id ID of the field being processed
401
		 */
402
		$input_type = apply_filters( 'gravityview/extension/search/input_type', $input_type, $field_type, $field_id );
403
404
		return $input_type;
405
	}
406
407
	/**
408
	 * Display hidden fields to add support for sites using Default permalink structure
409
	 *
410
	 * @since 1.8
411
	 * @return array Search fields, modified if not using permalinks
412
	 */
413
	public function add_no_permalink_fields( $search_fields, $object, $widget_args = array() ) {
414
		/** @global WP_Rewrite $wp_rewrite */
415
		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...
416
417
		// Support default permalink structure
418
		if ( false === $wp_rewrite->using_permalinks() ) {
419
420
			// By default, use current post.
421
			$post_id = 0;
422
423
			// We're in the WordPress Widget context, and an overriding post ID has been set.
424
			if ( ! empty( $widget_args['post_id'] ) ) {
425
				$post_id = absint( $widget_args['post_id'] );
426
			}
427
			// We're in the WordPress Widget context, and the base View ID should be used
428
			else if ( ! empty( $widget_args['view_id'] ) ) {
429
				$post_id = absint( $widget_args['view_id'] );
430
			}
431
432
			$args = gravityview_get_permalink_query_args( $post_id );
433
434
			// Add hidden fields to the search form
435
			foreach ( $args as $key => $value ) {
436
				$search_fields[] = array(
437
					'name'  => $key,
438
					'input' => 'hidden',
439
					'value' => $value,
440
				);
441
			}
442
		}
443
444
		return $search_fields;
445
	}
446
447
	/**
448 2
	 * Get the fields that are searchable for a View
449
	 *
450 2
	 * @since 2.0
451
	 *
452
	 * @param \GV\View|null $view
453 2
	 *
454
	 * TODO: Move to \GV\View, perhaps? And return a Field_Collection
455
	 * TODO: Use in gravityview()->request->is_search() to calculate whether a valid search
456 2
	 *
457
	 * @return array If no View, returns empty array. Otherwise, returns array of fields configured in widgets and Search Bar for a View
458 2
	 */
459 1
	private function get_view_searchable_fields( $view ) {
460
461
		/**
462 2
		 * Find all search widgets on the view and get the searchable fields settings.
463
		 */
464 2
		$searchable_fields = array();
465
466
		if ( ! $view ) {
467 2
			return $searchable_fields;
468
		}
469
470 2
		/**
471
		 * Include the sidebar Widgets.
472 1
		 */
473
		$widgets = (array) get_option( 'widget_gravityview_search', array() );
474
475
		foreach ( $widgets as $widget ) {
476
			if ( ! empty( $widget['view_id'] ) && $widget['view_id'] == $view->ID ) {
477
				if( $_fields = json_decode( $widget['search_fields'], true ) ) {
478
					foreach ( $_fields as $field ) {
479 1
						$searchable_fields [] = $field['field'];
480
					}
481 1
				}
482
			}
483
		}
484 1
485
		foreach ( $view->widgets->by_id( $this->get_widget_id() )->all() as $widget ) {
486 1
			if( $_fields = json_decode( $widget->configuration->get( 'search_fields' ), true ) ) {
487
				foreach ( $_fields as $field ) {
488
					$searchable_fields [] = $field['field'];
489
				}
490
			}
491 1
		}
492
493 1
		return $searchable_fields;
494
	}
495
496 1
	/** --- Frontend --- */
497 1
498 1
	/**
499 1
	 * Calculate the search criteria to filter entries
500 1
	 * @param  array $search_criteria
501
	 * @return array
502
	 */
503
	public function filter_entries( $search_criteria, $form_id = null, $args = array() ) {
504
505
		if( 'post' === $this->search_method ) {
506 2
			$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...
507 2
		} else {
508
			$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...
509
		}
510
511
		$view = \GV\View::by_id( \GV\Utils::get( $args, 'id' ) );
512
513
		gravityview()->log->debug( 'Requested $_{method}: ', array( 'method' => $this->search_method, 'data' => $get ) );
514
515
		if ( empty( $get ) || ! is_array( $get ) ) {
516 2
			return $search_criteria;
517
		}
518
519
		$get = stripslashes_deep( $get );
520
521 2
		$get = gv_map_deep( $get, 'rawurldecode' );
522 2
523 2
		// Make sure array key is set up
524
		$search_criteria['field_filters'] = \GV\Utils::get( $search_criteria, 'field_filters', array() );
525
526 2
		$searchable_fields = $this->get_view_searchable_fields( $view );
527
528 2
		// add free search
529 2
		if ( isset( $get['gv_search'] ) && '' !== $get['gv_search'] && in_array( 'search_all', $searchable_fields ) ) {
530
531
			$search_all_value = trim( $get['gv_search'] );
532
533 2
			/**
534 1
			 * @filter `gravityview/search-all-split-words` Search for each word separately or the whole phrase?
535 1
			 * @since 1.20.2
536 1
			 * @param bool $split_words True: split a phrase into words; False: search whole word only [Default: true]
537 1
			 */
538
			$split_words = apply_filters( 'gravityview/search-all-split-words', true );
539
540
			if ( $split_words ) {
541
542 2
				// Search for a piece
543 1
				$words = explode( ' ', $search_all_value );
544 1
545 1
				$words = array_filter( $words );
546 1
547
			} else {
548
549
				// Replace multiple spaces with one space
550
				$search_all_value = preg_replace( '/\s+/ism', ' ', $search_all_value );
551
552 2
				$words = array( $search_all_value );
553
			}
554
555 2
			foreach ( $words as $word ) {
556
				$search_criteria['field_filters'][] = array(
557 2
					'key' => null, // The field ID to search
558 2
					'value' => $word, // The value to search
559
					'operator' => 'contains', // What to search in. Options: `is` or `contains`
560
				);
561
			}
562
		}
563
564
		// start date & end date
565
		if ( in_array( 'entry_date', $searchable_fields ) ) {
0 ignored issues
show
Coding Style Best Practice introduced by
It is generally a best practice to always use braces with control structures.

Adding braces to control structures avoids accidental mistakes as your code changes:

// Without braces (not recommended)
if (true)
    doSomething();

// Recommended
if (true) {
    doSomething();
}
Loading history...
566
			$curr_start = !empty( $get['gv_start'] ) ? $get['gv_start'] : '';
0 ignored issues
show
introduced by
Expected 1 space after "!"; 0 found
Loading history...
567
			$curr_end = !empty( $get['gv_start'] ) ? $get['gv_end'] : '';
0 ignored issues
show
introduced by
Expected 1 space after "!"; 0 found
Loading history...
568
569
			if ( $view ) {
570
				/**
571
				 * Override start and end dates if View is limited to some already.
572
				 */
573
				if ( $start_date =$view->settings->get( 'start_date' ) ) {
0 ignored issues
show
introduced by
Expected 1 space after "="; 0 found
Loading history...
574
					if ( $start_timestamp = strtotime( $curr_start ) ) {
575
						$curr_start = $start_timestamp < strtotime( $start_date ) ? $start_date : $curr_start;
576
					}
577
				}
578
				if ( $end_date =$view->settings->get( 'end_date' ) ) {
0 ignored issues
show
introduced by
Expected 1 space after "="; 0 found
Loading history...
579
					if ( $end_timestamp = strtotime( $curr_end ) ) {
580
						$curr_end = $end_timestamp > strtotime( $end_date ) ? $end_date : $curr_end;
581 2
					}
582
				}
583 2
			}
584
585 2
			/**
586
			 * @filter `gravityview_date_created_adjust_timezone` Whether to adjust the timezone for entries. \n
587 2
			 * date_created is stored in UTC format. Convert search date into UTC (also used on templates/fields/date_created.php)
588
			 * @since 1.12
589
			 * @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
590
			 * @param[in] string $context Where the filter is being called from. `search` in this case.
591
			 */
592
			$adjust_tz = apply_filters( 'gravityview_date_created_adjust_timezone', true, 'search' );
593
594
<<<<<<< HEAD
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_SL
Loading history...
introduced by
Expected 1 space after "<<"; 0 found
Loading history...
introduced by
Expected 1 space before "<<"; 0 found
Loading history...
595
		/**
596
		 * Don't set $search_criteria['start_date'] if start_date is empty as it may lead to bad query results (GFAPI::get_entries)
597
		 */
598
		if ( ! empty( $curr_start ) ) {
599
			$curr_start = date( 'Y-m-d H:i:s', strtotime( $curr_start ) );
600
			$search_criteria['start_date'] = $adjust_tz ? get_gmt_from_date( $curr_start ) : $curr_start;
601
		}
602
603
		if ( ! empty( $curr_end ) ) {
604
			// Fast-forward 24 hour on the end time
605
			$curr_end = date( 'Y-m-d H:i:s', strtotime( $curr_end ) + DAY_IN_SECONDS );
606
			$search_criteria['end_date'] = $adjust_tz ? get_gmt_from_date( $curr_end ) : $curr_end;
607
=======
608
			/**
609
			 * Don't set $search_criteria['start_date'] if start_date is empty as it may lead to bad query results (GFAPI::get_entries)
610
			 */
611
			if ( ! empty( $curr_start ) ) {
612
				$curr_start = date( 'Y-m-d H:i:s', strtotime( $curr_start ) );
613
				$search_criteria['start_date'] = $adjust_tz ? get_gmt_from_date( $curr_start ) : $curr_start;
614
			}
615
616
			if ( ! empty( $curr_end ) ) {
617
				// Fast-forward 24 hour on the end time
618
				$curr_end = date( 'Y-m-d H:i:s', strtotime( $curr_end ) + DAY_IN_SECONDS );
619
				$search_criteria['end_date'] = $adjust_tz ? get_gmt_from_date( $curr_end ) : $curr_end;
620
			}
621
>>>>>>> master
622
		}
623
624
		// search for a specific entry ID
625
		if ( ! empty( $get[ 'gv_id' ] ) && in_array( 'entry_id', $searchable_fields ) ) {
626
			$search_criteria['field_filters'][] = array(
627
				'key' => 'id',
628
				'value' => absint( $get[ 'gv_id' ] ),
629
				'operator' => '=',
630
			);
631
		}
632
633
		// search for a specific Created_by ID
634
		if ( ! empty( $get[ 'gv_by' ] ) && in_array( 'entry_creator', $searchable_fields ) ) {
635
			$search_criteria['field_filters'][] = array(
636
				'key' => 'created_by',
637
				'value' => absint( $get['gv_by'] ),
638
				'operator' => '=',
639
			);
640
		}
641
642
643
		// Get search mode passed in URL
644
		$mode = isset( $get['mode'] ) && in_array( $get['mode'], array( 'any', 'all' ) ) ?  $get['mode'] : 'any';
645
646
		// get the other search filters
647
		foreach ( $get as $key => $value ) {
648
649
			if ( 0 !== strpos( $key, 'filter_' ) || gv_empty( $value, false, false ) || ( is_array( $value ) && count( $value ) === 1 && gv_empty( $value[0], false, false ) ) ) {
650
				continue;
651
			}
652
653
			$filter_key = $this->convert_request_key_to_filter_key( $key );
654
655
			// could return simple filter or multiple filters
656
			if ( ! in_array( 'search_all', $searchable_fields ) && ! in_array( $filter_key , $searchable_fields ) ) {
657
				continue;
658
			}
659
660
			$filter = $this->prepare_field_filter( $filter_key, $value, $view );
661
662
			if ( isset( $filter[0]['value'] ) ) {
663
				$search_criteria['field_filters'] = array_merge( $search_criteria['field_filters'], $filter );
664
665
				// if date range type, set search mode to ALL
666
				if ( ! empty( $filter[0]['operator'] ) && in_array( $filter[0]['operator'], array( '>=', '<=', '>', '<' ) ) ) {
667
					$mode = 'all';
668
				}
669
			} elseif( !empty( $filter ) ) {
670
				$search_criteria['field_filters'][] = $filter;
671
			}
672
		}
673
674
		/**
675
		 * @filter `gravityview/search/mode` Set the Search Mode (`all` or `any`)
676
		 * @since 1.5.1
677
		 * @param[out,in] string $mode Search mode (`any` vs `all`)
678
		 */
679
		$search_criteria['field_filters']['mode'] = apply_filters( 'gravityview/search/mode', $mode );
680
681
		gravityview()->log->debug( 'Returned Search Criteria: ', array( 'data' => $search_criteria ) );
682
683
		unset( $get );
684
685
		return $search_criteria;
686
	}
687
688
	/**
689
	 * Convert $_GET/$_POST key to the field/meta ID
690
	 *
691
	 * Examples:
692
	 * - `filter_is_starred` => `is_starred`
693
	 * - `filter_1_2` => `1.2`
694
	 * - `filter_5` => `5`
695
	 *
696
	 * @since 2.0
697
	 *
698
	 * @param string $key $_GET/_$_POST search key
699
	 *
700
	 * @return string
701
	 */
702
	private function convert_request_key_to_filter_key( $key ) {
703
704
		$field_id = str_replace( 'filter_', '', $key );
705
706
		// calculates field_id, removing 'filter_' and for '_' for advanced fields ( like name or checkbox )
707
		if ( preg_match('/^[0-9_]+$/ism', $field_id ) ) {
708
			$field_id = str_replace( '_', '.', $field_id );
709
		}
710
711
		return $field_id;
712
	}
713
714
	/**
715
	 * Prepare the field filters to GFAPI
716
	 *
717
	 * 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.
718
	 *
719
	 * Format searched values
720
	 *
721
	 * @param  string $filter_key ID of the field, or entry meta key
722
	 * @param  string $value $_GET/$_POST search value
723
	 * @param  \GV\View $view The view we're looking at
724
	 *
725
	 * @return array        1 or 2 deph levels
726
	 */
727
	public function prepare_field_filter( $filter_key, $value, $view ) {
728
729
		// get form field array
730
		$form_field = is_numeric( $filter_key ) ? \GV\GF_Field::by_id( $view->form, $filter_key ) : \GV\Internal_Field::by_id( $filter_key );
731
732
		// default filter array
733
		$filter = array(
734
			'key'   => $filter_key,
735
			'value' => $value,
736
		);
737
738
		switch ( $form_field->type ) {
739
740
			case 'select':
741
			case 'radio':
742
				$filter['operator'] = 'is';
743
				break;
744
745
			case 'post_category':
746
747
				if ( ! is_array( $value ) ) {
748
					$value = array( $value );
749
				}
750
751
				// Reset filter variable
752
				$filter = array();
753
754
				foreach ( $value as $val ) {
755
					$cat = get_term( $val, 'category' );
756
					$filter[] = array(
757
						'key'      => $filter_key,
758
						'value'    => esc_attr( $cat->name ) . ':' . $val,
759
						'operator' => 'is',
760
					);
761
				}
762
763
				break;
764
765
			case 'multiselect':
766
767
				if ( ! is_array( $value ) ) {
768
					break;
769
				}
770
771
				// Reset filter variable
772
				$filter = array();
773
774
				foreach ( $value as $val ) {
775
					$filter[] = array( 'key' => $filter_key, 'value' => $val );
776
				}
777
778
				break;
779
780
			case 'checkbox':
781
				// convert checkbox on/off into the correct search filter
782
				if ( false !== strpos( $filter_key, '.' ) && ! empty( $form_field->inputs ) && ! empty( $form_field->choices ) ) {
783
					foreach ( $form_field->inputs as $k => $input ) {
784
						if ( $input['id'] == $filter_key ) {
785
							$filter['value'] = $form_field->choices[ $k ]['value'];
786
							$filter['operator'] = 'is';
787
							break;
788
						}
789
					}
790
				} elseif ( is_array( $value ) ) {
791
792
					// Reset filter variable
793
					$filter = array();
794
795
					foreach ( $value as $val ) {
796
						$filter[] = array(
797
							'key'      => $filter_key,
798
							'value'    => $val,
799
							'operator' => 'is',
800
						);
801
					}
802
				}
803
804
				break;
805
806
			case 'name':
807
			case 'address':
808
809
				if ( false === strpos( $filter_key, '.' ) ) {
810
811
					$words = explode( ' ', $value );
812
813
					$filters = array();
814
					foreach ( $words as $word ) {
815
						if ( ! empty( $word ) && strlen( $word ) > 1 ) {
816
							// Keep the same key for each filter
817
							$filter['value'] = $word;
818
							// Add a search for the value
819
							$filters[] = $filter;
820
						}
821
					}
822
823
					$filter = $filters;
824
				}
825
826
				break;
827
828
			case 'date':
829
830
				if ( is_array( $value ) ) {
831
832
					// Reset filter variable
833
					$filter = array();
834
835
					foreach ( $value as $k => $date ) {
836
						if ( empty( $date ) ) {
837
							continue;
838
						}
839
						$operator = 'start' === $k ? '>=' : '<=';
840
841
						/**
842
						 * @hack
843
						 * @since 1.16.3
844
						 * Safeguard until GF implements '<=' operator
845
						 */
846
						if( !GFFormsModel::is_valid_operator( $operator ) && $operator === '<=' ) {
847
							$operator = '<';
848
							$date = date( 'Y-m-d', strtotime( $date . ' +1 day' ) );
849
						}
850
851
						$filter[] = array(
852
							'key'      => $filter_key,
853
							'value'    => self::get_formatted_date( $date, 'Y-m-d' ),
854
							'operator' => $operator,
855
						);
856
					}
857
				} else {
858
					$filter['value'] = self::get_formatted_date( $value, 'Y-m-d' );
859
				}
860
861
				break;
862
863
864
		} // switch field type
865
866
		return $filter;
867
	}
868
869
	/**
870
	 * Get the Field Format form GravityForms
871
	 *
872
	 * @param GF_Field_Date $field The field object
873
	 * @since 1.10
874
	 *
875
	 * @return string Format of the date in the database
876
	 */
877
	public static function get_date_field_format( GF_Field_Date $field ) {
878
		$format = 'm/d/Y';
879
		$datepicker = array(
880
			'mdy' => 'm/d/Y',
881
			'dmy' => 'd/m/Y',
882
			'dmy_dash' => 'd-m-Y',
883
			'dmy_dot' => 'm.d.Y',
884
			'ymd_slash' => 'Y/m/d',
885
			'ymd_dash' => 'Y-m-d',
886
			'ymd_dot' => 'Y.m.d',
887
		);
888
889
		if ( ! empty( $field->dateFormat ) && isset( $datepicker[ $field->dateFormat ] ) ){
890
			$format = $datepicker[ $field->dateFormat ];
891
		}
892
893
		return $format;
894
	}
895
896
	/**
897
	 * Format a date value
898
	 *
899
	 * @param string $value Date value input
900
	 * @param string $format Wanted formatted date
901
	 * @return string
902
	 */
903
	public static function get_formatted_date( $value = '', $format = 'Y-m-d' ) {
904
		$date = date_create( $value );
905
		if ( empty( $date ) ) {
906
			gravityview()->log->debug( 'Date format not valid: {value}', array( 'value' => $value ) );
907
			return '';
908
		}
909
		return $date->format( $format );
910
	}
911
912
913
	/**
914
	 * Include this extension templates path
915
	 * @param array $file_paths List of template paths ordered
916
	 */
917
	public function add_template_path( $file_paths ) {
918
919
		// Index 100 is the default GravityView template path.
920
		$file_paths[102] = self::$file . 'templates/';
921
922
		return $file_paths;
923
	}
924
925
	/**
926
	 * Check whether the configured search fields have a date field
927
	 *
928
	 * @since 1.17.5
929
	 *
930
	 * @param array $search_fields
931
	 *
932
	 * @return bool True: has a `date` or `date_range` field
933
	 */
934
	private function has_date_field( $search_fields ) {
935
936
		$has_date = false;
937
938
		foreach ( $search_fields as $k => $field ) {
939
			if ( in_array( $field['input'], array( 'date', 'date_range', 'entry_date' ) ) ) {
940
				$has_date = true;
941
				break;
942
			}
943
		}
944
945
		return $has_date;
946
	}
947
948
	/**
949
	 * Renders the Search Widget
950
	 * @param array $widget_args
951
	 * @param string $content
952
	 * @param string $context
953
	 *
954
	 * @return void
955
	 */
956
	public function render_frontend( $widget_args, $content = '', $context = '' ) {
957
		/** @var GravityView_View $gravityview_view */
958
		$gravityview_view = GravityView_View::getInstance();
959
960
		if ( empty( $gravityview_view ) ) {
961
			gravityview()->log->debug( '$gravityview_view not instantiated yet.' );
962
			return;
963
		}
964
965
		// get configured search fields
966
		$search_fields = ! empty( $widget_args['search_fields'] ) ? json_decode( $widget_args['search_fields'], true ) : '';
967
968
		if ( empty( $search_fields ) || ! is_array( $search_fields ) ) {
969
			gravityview()->log->debug( 'No search fields configured for widget:', array( 'data' => $widget_args ) );
970
			return;
971
		}
972
973
974
		// prepare fields
975
		foreach ( $search_fields as $k => $field ) {
976
977
			$updated_field = $field;
978
979
			$updated_field = $this->get_search_filter_details( $updated_field );
980
981
			switch ( $field['field'] ) {
982
983
				case 'search_all':
984
					$updated_field['key'] = 'search_all';
985
					$updated_field['input'] = 'search_all';
986
					$updated_field['value'] = $this->rgget_or_rgpost( 'gv_search' );
987
					break;
988
989
				case 'entry_date':
990
					$updated_field['key'] = 'entry_date';
991
					$updated_field['input'] = 'entry_date';
992
					$updated_field['value'] = array(
993
						'start' => $this->rgget_or_rgpost( 'gv_start' ),
994
						'end' => $this->rgget_or_rgpost( 'gv_end' ),
995
					);
996
					break;
997
998
				case 'entry_id':
999
					$updated_field['key'] = 'entry_id';
1000
					$updated_field['input'] = 'entry_id';
1001
					$updated_field['value'] = $this->rgget_or_rgpost( 'gv_id' );
1002
					break;
1003
1004
				case 'created_by':
1005
					$updated_field['key'] = 'created_by';
1006
					$updated_field['name'] = 'gv_by';
1007
					$updated_field['value'] = $this->rgget_or_rgpost( 'gv_by' );
1008
					$updated_field['choices'] = self::get_created_by_choices();
1009
					break;
1010
			}
1011
1012
			$search_fields[ $k ] = $updated_field;
1013
		}
1014
1015
		gravityview()->log->debug( 'Calculated Search Fields: ', array( 'data' => $search_fields ) );
1016
1017
		/**
1018
		 * @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.
1019
		 * @param array $search_fields Array of search filters with `key`, `label`, `value`, `type`, `choices` keys
1020
		 * @param GravityView_Widget_Search $this Current widget object
1021
		 * @param array $widget_args Args passed to this method. {@since 1.8}
1022
		 * @param \GV\Template_Context $context {@since 2.0}
1023
		 * @var array
1024
		 */
1025
		$gravityview_view->search_fields = apply_filters( 'gravityview_widget_search_filters', $search_fields, $this, $widget_args, $context );
1026
1027
		$gravityview_view->search_layout = ! empty( $widget_args['search_layout'] ) ? $widget_args['search_layout'] : 'horizontal';
1028
1029
		/** @since 1.14 */
1030
		$gravityview_view->search_mode = ! empty( $widget_args['search_mode'] ) ? $widget_args['search_mode'] : 'any';
1031
1032
		$custom_class = ! empty( $widget_args['custom_class'] ) ? $widget_args['custom_class'] : '';
1033
1034
		$gravityview_view->search_class = self::get_search_class( $custom_class );
1035
1036
		$gravityview_view->search_clear = ! empty( $widget_args['search_clear'] ) ? $widget_args['search_clear'] : false;
1037
1038
		if ( $this->has_date_field( $search_fields ) ) {
1039
			// enqueue datepicker stuff only if needed!
1040
			$this->enqueue_datepicker();
1041
		}
1042
1043
		$this->maybe_enqueue_flexibility();
1044
1045
		$gravityview_view->render( 'widget', 'search', false );
1046
	}
1047
1048
	/**
1049
	 * Get the search class for a search form
1050
	 *
1051
	 * @since 1.5.4
1052
	 *
1053
	 * @return string Sanitized CSS class for the search form
1054
	 */
1055
	public static function get_search_class( $custom_class = '' ) {
1056
		$gravityview_view = GravityView_View::getInstance();
1057
1058
		$search_class = 'gv-search-'.$gravityview_view->search_layout;
1059
1060
		if ( ! empty( $custom_class )  ) {
1061
			$search_class .= ' '.$custom_class;
1062
		}
1063
1064
		/**
1065
		 * @filter `gravityview_search_class` Modify the CSS class for the search form
1066
		 * @param string $search_class The CSS class for the search form
1067
		 */
1068
		$search_class = apply_filters( 'gravityview_search_class', $search_class );
1069
1070
		// Is there an active search being performed? Used by fe-views.js
1071
		$search_class .= GravityView_frontend::getInstance()->isSearch() ? ' gv-is-search' : '';
1072
1073
		return gravityview_sanitize_html_class( $search_class );
1074
	}
1075
1076
1077
	/**
1078
	 * Calculate the search form action
1079
	 * @since 1.6
1080
	 *
1081
	 * @return string
1082
	 */
1083
	public static function get_search_form_action() {
1084
		$gravityview_view = GravityView_View::getInstance();
1085
1086
		$post_id = $gravityview_view->getPostId() ? $gravityview_view->getPostId() : $gravityview_view->getViewId();
1087
1088
		$url = add_query_arg( array(), get_permalink( $post_id ) );
1089
1090
		return esc_url( $url );
1091
	}
1092
1093
	/**
1094
	 * Get the label for a search form field
1095
	 * @param  array $field      Field setting as sent by the GV configuration - has `field`, `input` (input type), and `label` keys
1096
	 * @param  array $form_field Form field data, as fetched by `gravityview_get_field()`
1097
	 * @return string             Label for the search form
1098
	 */
1099
	private static function get_field_label( $field, $form_field = array() ) {
1100
1101
		$label = \GV\Utils::_GET( 'label', \GV\Utils::get( $field, 'label' ) );
1102
1103
		if ( ! $label ) {
1104
1105
			$label = isset( $form_field['label'] ) ? $form_field['label'] : '';
1106
1107
			switch( $field['field'] ) {
1108
				case 'search_all':
1109
					$label = __( 'Search Entries:', 'gravityview' );
1110
					break;
1111
				case 'entry_date':
1112
					$label = __( 'Filter by date:', 'gravityview' );
1113
					break;
1114
				case 'entry_id':
1115
					$label = __( 'Entry ID:', 'gravityview' );
1116
					break;
1117
				default:
1118
					// If this is a field input, not a field
1119
					if ( strpos( $field['field'], '.' ) > 0 && ! empty( $form_field['inputs'] ) ) {
1120
1121
						// Get the label for the field in question, which returns an array
1122
						$items = wp_list_filter( $form_field['inputs'], array( 'id' => $field['field'] ) );
1123
1124
						// Get the item with the `label` key
1125
						$values = wp_list_pluck( $items, 'label' );
1126
1127
						// There will only one item in the array, but this is easier
1128
						foreach ( $values as $value ) {
1129
							$label = $value;
1130
							break;
1131
						}
1132
					}
1133
			}
1134
		}
1135
1136
		/**
1137
		 * @filter `gravityview_search_field_label` Modify the label for a search field. Supports returning HTML
1138
		 * @since 1.17.3 Added $field parameter
1139
		 * @param[in,out] string $label Existing label text, sanitized.
1140
		 * @param[in] array $form_field Gravity Forms field array, as returned by `GFFormsModel::get_field()`
1141
		 * @param[in] array $field Field setting as sent by the GV configuration - has `field`, `input` (input type), and `label` keys
1142
		 */
1143
		$label = apply_filters( 'gravityview_search_field_label', esc_attr( $label ), $form_field, $field );
1144
1145
		return $label;
1146
	}
1147
1148
	/**
1149
	 * Prepare search fields to frontend render with other details (label, field type, searched values)
1150
	 *
1151
	 * @param array $field
1152
	 * @return array
1153
	 */
1154
	private function get_search_filter_details( $field ) {
1155
1156
		$gravityview_view = GravityView_View::getInstance();
1157
1158
		$form = $gravityview_view->getForm();
1159
1160
		// for advanced field ids (eg, first name / last name )
1161
		$name = 'filter_' . str_replace( '.', '_', $field['field'] );
1162
1163
		// get searched value from $_GET/$_POST (string or array)
1164
		$value = $this->rgget_or_rgpost( $name );
1165
1166
		// get form field details
1167
		$form_field = gravityview_get_field( $form, $field['field'] );
1168
1169
		$filter = array(
1170
			'key' => $field['field'],
1171
			'name' => $name,
1172
			'label' => self::get_field_label( $field, $form_field ),
1173
			'input' => $field['input'],
1174
			'value' => $value,
1175
			'type' => $form_field['type'],
1176
		);
1177
1178
		// collect choices
1179
		if ( 'post_category' === $form_field['type'] && ! empty( $form_field['displayAllCategories'] ) && empty( $form_field['choices'] ) ) {
1180
			$filter['choices'] = gravityview_get_terms_choices();
1181
		} elseif ( ! empty( $form_field['choices'] ) ) {
1182
			$filter['choices'] = $form_field['choices'];
1183
		}
1184
1185
		if ( 'date_range' === $field['input'] && empty( $value ) ) {
1186
			$filter['value'] = array( 'start' => '', 'end' => '' );
1187
		}
1188
1189
		return $filter;
1190
1191
	}
1192
1193
	/**
1194
	 * Calculate the search choices for the users
1195
	 *
1196
	 * @since 1.8
1197
	 *
1198
	 * @return array Array of user choices (value = ID, text = display name)
1199
	 */
1200
	private static function get_created_by_choices() {
1201
1202
		/**
1203
		 * filter gravityview/get_users/search_widget
1204
		 * @see \GVCommon::get_users
1205
		 */
1206
		$users = GVCommon::get_users( 'search_widget', array( 'fields' => array( 'ID', 'display_name' ) ) );
1207
1208
		$choices = array();
1209
		foreach ( $users as $user ) {
1210
			$choices[] = array(
1211
				'value' => $user->ID,
1212
				'text' => $user->display_name,
1213
			);
1214
		}
1215
1216
		return $choices;
1217
	}
1218
1219
1220
	/**
1221
	 * Output the Clear Search Results button
1222
	 * @since 1.5.4
1223
	 */
1224
	public static function the_clear_search_button() {
1225
		$gravityview_view = GravityView_View::getInstance();
1226
1227
		if ( $gravityview_view->search_clear ) {
1228
1229
			$url = strtok( add_query_arg( array() ), '?' );
1230
1231
			echo gravityview_get_link( $url, esc_html__( 'Clear', 'gravityview' ), 'class=button gv-search-clear' );
1232
1233
		}
1234
	}
1235
1236
	/**
1237
	 * Based on the search method, fetch the value for a specific key
1238
	 *
1239
	 * @since 1.16.4
1240
	 *
1241
	 * @param string $name Name of the request key to fetch the value for
1242
	 *
1243
	 * @return mixed|string Value of request at $name key. Empty string if empty.
1244
	 */
1245
	private function rgget_or_rgpost( $name ) {
1246
		$value = \GV\Utils::_REQUEST( $name );
1247
1248
		$value = stripslashes_deep( $value );
1249
1250
		$value = gv_map_deep( $value, 'rawurldecode' );
1251
1252
		$value = gv_map_deep( $value, '_wp_specialchars' );
1253
1254
		return $value;
1255
	}
1256
1257
1258
	/**
1259
	 * Require the datepicker script for the frontend GV script
1260
	 * @param array $js_dependencies Array of existing required scripts for the fe-views.js script
1261
	 * @return array Array required scripts, with `jquery-ui-datepicker` added
1262
	 */
1263
	public function add_datepicker_js_dependency( $js_dependencies ) {
1264
1265
		$js_dependencies[] = 'jquery-ui-datepicker';
1266
1267
		return $js_dependencies;
1268
	}
1269
1270
	/**
1271
	 * Modify the array passed to wp_localize_script()
1272
	 *
1273
	 * @param array $js_localization The data padded to the Javascript file
1274
	 * @param array $view_data View data array with View settings
1275
	 *
1276
	 * @return array
1277
	 */
1278
	public function add_datepicker_localization( $localizations = array(), $view_data = array() ) {
1279
		global $wp_locale;
1280
1281
		/**
1282
		 * @filter `gravityview_datepicker_settings` Modify the datepicker settings
1283
		 * @see http://api.jqueryui.com/datepicker/ Learn what settings are available
1284
		 * @see http://www.renegadetechconsulting.com/tutorials/jquery-datepicker-and-wordpress-i18n Thanks for the helpful information on $wp_locale
1285
		 * @param array $js_localization The data padded to the Javascript file
1286
		 * @param array $view_data View data array with View settings
1287
		 */
1288
		$datepicker_settings = apply_filters( 'gravityview_datepicker_settings', array(
1289
			'yearRange' => '-5:+5',
1290
			'changeMonth' => true,
1291
			'changeYear' => true,
1292
			'closeText' => esc_attr_x( 'Close', 'Close calendar', 'gravityview' ),
1293
			'prevText' => esc_attr_x( 'Prev', 'Previous month in calendar', 'gravityview' ),
1294
			'nextText' => esc_attr_x( 'Next', 'Next month in calendar', 'gravityview' ),
1295
			'currentText' => esc_attr_x( 'Today', 'Today in calendar', 'gravityview' ),
1296
			'weekHeader' => esc_attr_x( 'Week', 'Week in calendar', 'gravityview' ),
1297
			'monthStatus'       => __( 'Show a different month', 'gravityview' ),
1298
			'monthNames'        => array_values( $wp_locale->month ),
1299
			'monthNamesShort'   => array_values( $wp_locale->month_abbrev ),
1300
			'dayNames'          => array_values( $wp_locale->weekday ),
1301
			'dayNamesShort'     => array_values( $wp_locale->weekday_abbrev ),
1302
			'dayNamesMin'       => array_values( $wp_locale->weekday_initial ),
1303
			// get the start of week from WP general setting
1304
			'firstDay'          => get_option( 'start_of_week' ),
1305
			// is Right to left language? default is false
1306
			'isRTL'             => is_rtl(),
1307
		), $view_data );
1308
1309
		$localizations['datepicker'] = $datepicker_settings;
1310
1311
		return $localizations;
1312
1313
	}
1314
1315
	/**
1316
	 * Register search widget scripts, including Flexibility
1317
	 *
1318
	 * @see https://github.com/10up/flexibility
1319
	 *
1320
	 * @since 1.17
1321
	 *
1322
	 * @return void
1323
	 */
1324
	public function register_scripts() {
1325
		wp_register_script( 'gv-flexibility', plugins_url( 'assets/lib/flexibility/flexibility.js', GRAVITYVIEW_FILE ), array(), \GV\Plugin::$version, true );
1326
	}
1327
1328
	/**
1329
	 * If the current visitor is running IE 8 or 9, enqueue Flexibility
1330
	 *
1331
	 * @since 1.17
1332
	 *
1333
	 * @return void
1334
	 */
1335
	private function maybe_enqueue_flexibility() {
1336
		if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && preg_match( '/MSIE [8-9]/', $_SERVER['HTTP_USER_AGENT'] ) ) {
1337
			wp_enqueue_script( 'gv-flexibility' );
1338
		}
1339
	}
1340
1341
	/**
1342
	 * Enqueue the datepicker script
1343
	 *
1344
	 * It sets the $gravityview->datepicker_class parameter
1345
	 *
1346
	 * @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.
1347
	 * @return void
1348
	 */
1349
	public function enqueue_datepicker() {
1350
		$gravityview_view = GravityView_View::getInstance();
1351
1352
		wp_enqueue_script( 'jquery-ui-datepicker' );
1353
1354
		add_filter( 'gravityview_js_dependencies', array( $this, 'add_datepicker_js_dependency' ) );
1355
		add_filter( 'gravityview_js_localization', array( $this, 'add_datepicker_localization' ), 10, 2 );
1356
1357
		$scheme = is_ssl() ? 'https://' : 'http://';
1358
		wp_enqueue_style( 'jquery-ui-datepicker', $scheme.'ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/smoothness/jquery-ui.css' );
1359
1360
		/**
1361
		 * @filter `gravityview_search_datepicker_class`
1362
		 * 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.
1363
		 * @param string $css_class CSS class to use. Default: `gv-datepicker datepicker mdy` \n
1364
		 * Options are:
1365
		 * - `mdy` (mm/dd/yyyy)
1366
		 * - `dmy` (dd/mm/yyyy)
1367
		 * - `dmy_dash` (dd-mm-yyyy)
1368
		 * - `dmy_dot` (dd.mm.yyyy)
1369
		 * - `ymp_slash` (yyyy/mm/dd)
1370
		 * - `ymd_dash` (yyyy-mm-dd)
1371
		 * - `ymp_dot` (yyyy.mm.dd)
1372
		 */
1373
		$datepicker_class = apply_filters( 'gravityview_search_datepicker_class', 'gv-datepicker datepicker mdy' );
1374
1375
		$gravityview_view->datepicker_class = $datepicker_class;
1376
1377
	}
1378
1379
1380
} // end class
1381
1382
new GravityView_Widget_Search;
1383