1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* The GravityView New Search widget |
4
|
|
|
* |
5
|
|
|
* @package GravityView-DataTables-Ext |
6
|
|
|
* @license GPL2+ |
7
|
|
|
* @author Katz Web Services, Inc. |
8
|
|
|
* @link http://gravityview.co |
9
|
|
|
* @copyright Copyright 2014, Katz Web Services, Inc. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
if ( ! defined( 'WPINC' ) ) { |
13
|
|
|
die; |
14
|
|
|
} |
15
|
|
|
|
16
|
|
|
class GravityView_Widget_Search extends \GV\Widget { |
17
|
|
|
|
18
|
|
|
public static $file; |
19
|
|
|
public static $instance; |
20
|
|
|
|
21
|
|
|
private $search_filters = array(); |
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
|
39 |
|
public function __construct() { |
31
|
|
|
|
32
|
39 |
|
$this->widget_id = 'search_bar'; |
33
|
39 |
|
$this->widget_description = esc_html__( 'Search form for searching entries.', 'gravityview' ); |
34
|
|
|
|
35
|
39 |
|
self::$instance = &$this; |
36
|
|
|
|
37
|
39 |
|
self::$file = plugin_dir_path( __FILE__ ); |
38
|
|
|
|
39
|
39 |
|
$default_values = array( 'header' => 0, 'footer' => 0 ); |
40
|
|
|
|
41
|
|
|
$settings = array( |
42
|
39 |
|
'search_layout' => array( |
43
|
39 |
|
'type' => 'radio', |
44
|
|
|
'full_width' => true, |
45
|
39 |
|
'label' => esc_html__( 'Search Layout', 'gravityview' ), |
46
|
39 |
|
'value' => 'horizontal', |
47
|
|
|
'options' => array( |
48
|
39 |
|
'horizontal' => esc_html__( 'Horizontal', 'gravityview' ), |
49
|
39 |
|
'vertical' => esc_html__( 'Vertical', 'gravityview' ), |
50
|
|
|
), |
51
|
|
|
), |
52
|
|
|
'search_clear' => array( |
53
|
39 |
|
'type' => 'checkbox', |
54
|
39 |
|
'label' => __( 'Show Clear button', 'gravityview' ), |
55
|
|
|
'value' => false, |
56
|
|
|
), |
57
|
|
|
'search_fields' => array( |
58
|
|
|
'type' => 'hidden', |
59
|
|
|
'label' => '', |
60
|
|
|
'class' => 'gv-search-fields-value', |
61
|
|
|
'value' => '[{"field":"search_all","input":"input_text"}]', // Default: Search Everything text box |
62
|
|
|
), |
63
|
|
|
'search_mode' => array( |
64
|
39 |
|
'type' => 'radio', |
65
|
|
|
'full_width' => true, |
66
|
39 |
|
'label' => esc_html__( 'Search Mode', 'gravityview' ), |
67
|
39 |
|
'desc' => __('Should search results match all search fields, or any?', 'gravityview'), |
68
|
39 |
|
'value' => 'any', |
69
|
39 |
|
'class' => 'hide-if-js', |
70
|
|
|
'options' => array( |
71
|
39 |
|
'any' => esc_html__( 'Match Any Fields', 'gravityview' ), |
72
|
39 |
|
'all' => esc_html__( 'Match All Fields', 'gravityview' ), |
73
|
|
|
), |
74
|
|
|
), |
75
|
|
|
); |
76
|
|
|
|
77
|
39 |
|
if ( ! $this->is_registered() ) { |
78
|
|
|
// frontend - filter entries |
79
|
|
|
add_filter( 'gravityview_fe_search_criteria', array( $this, 'filter_entries' ), 10, 3 ); |
80
|
|
|
|
81
|
|
|
// frontend - add template path |
82
|
|
|
add_filter( 'gravityview_template_paths', array( $this, 'add_template_path' ) ); |
83
|
|
|
|
84
|
|
|
// admin - add scripts - run at 1100 to make sure GravityView_Admin_Views::add_scripts_and_styles() runs first at 999 |
85
|
|
|
add_action( 'admin_enqueue_scripts', array( $this, 'add_scripts_and_styles' ), 1100 ); |
86
|
|
|
add_action( 'wp_enqueue_scripts', array( $this, 'register_scripts') ); |
87
|
|
|
add_filter( 'gravityview_noconflict_scripts', array( $this, 'register_no_conflict' ) ); |
88
|
|
|
|
89
|
|
|
// ajax - get the searchable fields |
90
|
|
|
add_action( 'wp_ajax_gv_searchable_fields', array( 'GravityView_Widget_Search', 'get_searchable_fields' ) ); |
91
|
|
|
|
92
|
|
|
add_action( 'gravityview_search_widget_fields_after', array( $this, 'add_preview_inputs' ) ); |
93
|
|
|
} |
94
|
|
|
|
95
|
39 |
|
parent::__construct( esc_html__( 'Search Bar', 'gravityview' ), null, $default_values, $settings ); |
96
|
|
|
|
97
|
|
|
// calculate the search method (POST / GET) |
98
|
39 |
|
$this->set_search_method(); |
99
|
39 |
|
} |
100
|
|
|
|
101
|
|
|
/** |
102
|
|
|
* @return GravityView_Widget_Search |
103
|
|
|
*/ |
104
|
6 |
|
public static function getInstance() { |
105
|
6 |
|
if ( empty( self::$instance ) ) { |
106
|
|
|
self::$instance = new GravityView_Widget_Search; |
107
|
|
|
} |
108
|
6 |
|
return self::$instance; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* Sets the search method to GET (default) or POST |
113
|
|
|
* @since 1.16.4 |
114
|
|
|
*/ |
115
|
39 |
|
private function set_search_method() { |
116
|
|
|
/** |
117
|
|
|
* @filter `gravityview/search/method` Modify the search form method (GET / POST) |
118
|
|
|
* @since 1.16.4 |
119
|
|
|
* @param string $search_method Assign an input type according to the form field type. Defaults: `boolean`, `multi`, `select`, `date`, `text` |
120
|
|
|
* @param string $field_type Gravity Forms field type (also the `name` parameter of GravityView_Field classes) |
121
|
|
|
*/ |
122
|
39 |
|
$method = apply_filters( 'gravityview/search/method', $this->search_method ); |
123
|
|
|
|
124
|
39 |
|
$method = strtolower( $method ); |
125
|
|
|
|
126
|
39 |
|
$this->search_method = in_array( $method, array( 'get', 'post' ) ) ? $method : 'get'; |
127
|
39 |
|
} |
128
|
|
|
|
129
|
|
|
/** |
130
|
|
|
* Returns the search method |
131
|
|
|
* @since 1.16.4 |
132
|
|
|
* @return string |
133
|
|
|
*/ |
134
|
6 |
|
public function get_search_method() { |
135
|
6 |
|
return $this->search_method; |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
/** |
139
|
|
|
* Get the input types available for different field types |
140
|
|
|
* |
141
|
|
|
* @since 1.17.5 |
142
|
|
|
* |
143
|
|
|
* @return array [field type name] => (array|string) search bar input types |
144
|
|
|
*/ |
145
|
|
|
public static function get_input_types_by_field_type() { |
146
|
|
|
/** |
147
|
|
|
* Input Type groups |
148
|
|
|
* @see admin-search-widget.js (getSelectInput) |
149
|
|
|
* @var array |
150
|
|
|
*/ |
151
|
|
|
$input_types = array( |
152
|
|
|
'text' => array( 'input_text' ), |
153
|
|
|
'address' => array( 'input_text' ), |
154
|
|
|
'number' => array( 'input_text' ), |
155
|
|
|
'date' => array( 'date', 'date_range' ), |
156
|
|
|
'boolean' => array( 'single_checkbox' ), |
157
|
|
|
'select' => array( 'select', 'radio', 'link' ), |
158
|
|
|
'multi' => array( 'select', 'multiselect', 'radio', 'checkbox', 'link' ), |
159
|
|
|
|
160
|
|
|
// hybrids |
161
|
|
|
'created_by' => array( 'select', 'radio', 'checkbox', 'multiselect', 'link', 'input_text' ), |
162
|
|
|
'product' => array( 'select', 'radio', 'link', 'input_text' ), |
163
|
|
|
); |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* @filter `gravityview/search/input_types` Change the types of search fields available to a field type |
167
|
|
|
* @see GravityView_Widget_Search::get_search_input_labels() for the available input types |
168
|
|
|
* @param array $input_types Associative array: key is field `name`, value is array of GravityView input types (note: use `input_text` for `text`) |
169
|
|
|
*/ |
170
|
|
|
$input_types = apply_filters( 'gravityview/search/input_types', $input_types ); |
171
|
|
|
|
172
|
|
|
return $input_types; |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
/** |
176
|
|
|
* Get labels for different types of search bar inputs |
177
|
|
|
* |
178
|
|
|
* @since 1.17.5 |
179
|
|
|
* |
180
|
|
|
* @return array [input type] => input type label |
181
|
|
|
*/ |
182
|
|
|
public static function get_search_input_labels() { |
183
|
|
|
/** |
184
|
|
|
* Input Type labels l10n |
185
|
|
|
* @see admin-search-widget.js (getSelectInput) |
186
|
|
|
* @var array |
187
|
|
|
*/ |
188
|
|
|
$input_labels = array( |
189
|
|
|
'input_text' => esc_html__( 'Text', 'gravityview' ), |
190
|
|
|
'date' => esc_html__( 'Date', 'gravityview' ), |
191
|
|
|
'select' => esc_html__( 'Select', 'gravityview' ), |
192
|
|
|
'multiselect' => esc_html__( 'Select (multiple values)', 'gravityview' ), |
193
|
|
|
'radio' => esc_html__( 'Radio', 'gravityview' ), |
194
|
|
|
'checkbox' => esc_html__( 'Checkbox', 'gravityview' ), |
195
|
|
|
'single_checkbox' => esc_html__( 'Checkbox', 'gravityview' ), |
196
|
|
|
'link' => esc_html__( 'Links', 'gravityview' ), |
197
|
|
|
'date_range' => esc_html__( 'Date range', 'gravityview' ), |
198
|
|
|
); |
199
|
|
|
|
200
|
|
|
/** |
201
|
|
|
* @filter `gravityview/search/input_types` Change the label of search field input types |
202
|
|
|
* @param array $input_types Associative array: key is input type name, value is label |
203
|
|
|
*/ |
204
|
|
|
$input_labels = apply_filters( 'gravityview/search/input_labels', $input_labels ); |
205
|
|
|
|
206
|
|
|
return $input_labels; |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
public static function get_search_input_label( $input_type ) { |
210
|
|
|
$labels = self::get_search_input_labels(); |
211
|
|
|
|
212
|
|
|
return \GV\Utils::get( $labels, $input_type, false ); |
213
|
|
|
} |
214
|
|
|
|
215
|
|
|
/** |
216
|
|
|
* Add script to Views edit screen (admin) |
217
|
|
|
* @param mixed $hook |
218
|
|
|
*/ |
219
|
|
|
public function add_scripts_and_styles( $hook ) { |
220
|
|
|
global $pagenow; |
221
|
|
|
|
222
|
|
|
// Don't process any scripts below here if it's not a GravityView page or the widgets screen |
223
|
|
|
if ( ! gravityview()->request->is_admin( $hook, 'single' ) && ( 'widgets.php' !== $pagenow ) ) { |
|
|
|
|
224
|
|
|
return; |
225
|
|
|
} |
226
|
|
|
|
227
|
|
|
$script_min = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min'; |
228
|
|
|
$script_source = empty( $script_min ) ? '/source' : ''; |
229
|
|
|
|
230
|
|
|
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 ); |
231
|
|
|
|
232
|
|
|
wp_localize_script( 'gravityview_searchwidget_admin', 'gvSearchVar', array( |
233
|
|
|
'nonce' => wp_create_nonce( 'gravityview_ajaxsearchwidget' ), |
234
|
|
|
'label_nofields' => esc_html__( 'No search fields configured yet.', 'gravityview' ), |
235
|
|
|
'label_addfield' => esc_html__( 'Add Search Field', 'gravityview' ), |
236
|
|
|
'label_label' => esc_html__( 'Label', 'gravityview' ), |
237
|
|
|
'label_searchfield' => esc_html__( 'Search Field', 'gravityview' ), |
238
|
|
|
'label_inputtype' => esc_html__( 'Input Type', 'gravityview' ), |
239
|
|
|
'label_ajaxerror' => esc_html__( 'There was an error loading searchable fields. Save the View or refresh the page to fix this issue.', 'gravityview' ), |
240
|
|
|
'input_labels' => json_encode( self::get_search_input_labels() ), |
241
|
|
|
'input_types' => json_encode( self::get_input_types_by_field_type() ), |
242
|
|
|
) ); |
243
|
|
|
|
244
|
|
|
} |
245
|
|
|
|
246
|
|
|
/** |
247
|
|
|
* Add admin script to the no-conflict scripts whitelist |
248
|
|
|
* @param array $allowed Scripts allowed in no-conflict mode |
249
|
|
|
* @return array Scripts allowed in no-conflict mode, plus the search widget script |
250
|
|
|
*/ |
251
|
|
|
public function register_no_conflict( $allowed ) { |
252
|
|
|
$allowed[] = 'gravityview_searchwidget_admin'; |
253
|
|
|
return $allowed; |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
/** |
257
|
|
|
* Ajax |
258
|
|
|
* Returns the form fields ( only the searchable ones ) |
259
|
|
|
* |
260
|
|
|
* @access public |
261
|
|
|
* @return void |
262
|
|
|
*/ |
263
|
|
|
public static function get_searchable_fields() { |
264
|
|
|
|
265
|
|
|
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'gravityview_ajaxsearchwidget' ) ) { |
266
|
|
|
exit( '0' ); |
267
|
|
|
} |
268
|
|
|
|
269
|
|
|
$form = ''; |
270
|
|
|
|
271
|
|
|
// Fetch the form for the current View |
272
|
|
|
if ( ! empty( $_POST['view_id'] ) ) { |
273
|
|
|
|
274
|
|
|
$form = gravityview_get_form_id( $_POST['view_id'] ); |
275
|
|
|
|
276
|
|
|
} elseif ( ! empty( $_POST['formid'] ) ) { |
277
|
|
|
|
278
|
|
|
$form = (int) $_POST['formid']; |
279
|
|
|
|
280
|
|
|
} elseif ( ! empty( $_POST['template_id'] ) && class_exists( 'GravityView_Ajax' ) ) { |
281
|
|
|
|
282
|
|
|
$form = GravityView_Ajax::pre_get_form_fields( $_POST['template_id'] ); |
283
|
|
|
|
284
|
|
|
} |
285
|
|
|
|
286
|
|
|
// fetch form id assigned to the view |
287
|
|
|
$response = self::render_searchable_fields( $form ); |
288
|
|
|
|
289
|
|
|
exit( $response ); |
290
|
|
|
} |
291
|
|
|
|
292
|
|
|
/** |
293
|
|
|
* Generates html for the available Search Fields dropdown |
294
|
|
|
* @param int $form_id |
295
|
|
|
* @param string $current (for future use) |
296
|
|
|
* @return string |
297
|
|
|
*/ |
298
|
|
|
public static function render_searchable_fields( $form_id = null, $current = '' ) { |
299
|
|
|
|
300
|
|
|
if ( is_null( $form_id ) ) { |
301
|
|
|
return ''; |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
// start building output |
305
|
|
|
|
306
|
|
|
$output = '<select class="gv-search-fields">'; |
307
|
|
|
|
308
|
|
|
$custom_fields = array( |
309
|
|
|
'search_all' => array( |
310
|
|
|
'text' => esc_html__( 'Search Everything', 'gravityview' ), |
311
|
|
|
'type' => 'text', |
312
|
|
|
), |
313
|
|
|
'entry_date' => array( |
314
|
|
|
'text' => esc_html__( 'Entry Date', 'gravityview' ), |
315
|
|
|
'type' => 'date', |
316
|
|
|
), |
317
|
|
|
'entry_id' => array( |
318
|
|
|
'text' => esc_html__( 'Entry ID', 'gravityview' ), |
319
|
|
|
'type' => 'text', |
320
|
|
|
), |
321
|
|
|
'created_by' => array( |
322
|
|
|
'text' => esc_html__( 'Entry Creator', 'gravityview' ), |
323
|
|
|
'type' => 'created_by', |
324
|
|
|
), |
325
|
|
|
'is_starred' => array( |
326
|
|
|
'text' => esc_html__( 'Is Starred', 'gravityview' ), |
327
|
|
|
'type' => 'boolean', |
328
|
|
|
), |
329
|
|
|
); |
330
|
|
|
|
331
|
|
|
if ( gravityview()->plugin->supports( \GV\Plugin::FEATURE_GFQUERY ) ) { |
332
|
|
|
$custom_fields['is_approved'] = array( |
333
|
|
|
'text' => esc_html__( 'Approval Status', 'gravityview' ), |
334
|
|
|
'type' => 'multi', |
335
|
|
|
); |
336
|
|
|
} |
337
|
|
|
|
338
|
|
|
foreach( $custom_fields as $custom_field_key => $custom_field ) { |
339
|
|
|
$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'] ); |
340
|
|
|
} |
341
|
|
|
|
342
|
|
|
// Get fields with sub-inputs and no parent |
343
|
|
|
$fields = gravityview_get_form_fields( $form_id, true, true ); |
344
|
|
|
|
345
|
|
|
/** |
346
|
|
|
* @filter `gravityview/search/searchable_fields` Modify the fields that are displayed as searchable in the Search Bar dropdown\n |
347
|
|
|
* @since 1.17 |
348
|
|
|
* @see gravityview_get_form_fields() Used to fetch the fields |
349
|
|
|
* @see GravityView_Widget_Search::get_search_input_types See this method to modify the type of input types allowed for a field |
350
|
|
|
* @param array $fields Array of searchable fields, as fetched by gravityview_get_form_fields() |
351
|
|
|
* @param int $form_id |
352
|
|
|
*/ |
353
|
|
|
$fields = apply_filters( 'gravityview/search/searchable_fields', $fields, $form_id ); |
354
|
|
|
|
355
|
|
|
if ( ! empty( $fields ) ) { |
356
|
|
|
|
357
|
|
|
$blacklist_field_types = apply_filters( 'gravityview_blacklist_field_types', array( 'fileupload', 'post_image', 'post_id', 'section' ), null ); |
358
|
|
|
|
359
|
|
|
foreach ( $fields as $id => $field ) { |
360
|
|
|
|
361
|
|
|
if ( in_array( $field['type'], $blacklist_field_types ) ) { |
362
|
|
|
continue; |
363
|
|
|
} |
364
|
|
|
|
365
|
|
|
$types = self::get_search_input_types( $id, $field['type'] ); |
366
|
|
|
|
367
|
|
|
$output .= '<option value="'. $id .'" '. selected( $id, $current, false ).'data-inputtypes="'. esc_attr( $types ) .'">'. esc_html( $field['label'] ) .'</option>'; |
368
|
|
|
} |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
$output .= '</select>'; |
372
|
|
|
|
373
|
|
|
return $output; |
374
|
|
|
|
375
|
|
|
} |
376
|
|
|
|
377
|
|
|
/** |
378
|
|
|
* Assign an input type according to the form field type |
379
|
|
|
* |
380
|
|
|
* @see admin-search-widget.js |
381
|
|
|
* |
382
|
|
|
* @param string|int|float $field_id Gravity Forms field ID |
383
|
|
|
* @param string $field_type Gravity Forms field type (also the `name` parameter of GravityView_Field classes) |
384
|
|
|
* |
385
|
|
|
* @return string GV field search input type ('multi', 'boolean', 'select', 'date', 'text') |
386
|
|
|
*/ |
387
|
|
|
public static function get_search_input_types( $field_id = '', $field_type = null ) { |
388
|
|
|
|
389
|
|
|
// @todo - This needs to be improved - many fields have . including products and addresses |
390
|
|
|
if ( false !== strpos( (string) $field_id, '.' ) && in_array( $field_type, array( 'checkbox' ) ) || in_array( $field_id, array( 'is_fulfilled' ) ) ) { |
391
|
|
|
$input_type = 'boolean'; // on/off checkbox |
392
|
|
|
} elseif ( in_array( $field_type, array( 'checkbox', 'post_category', 'multiselect' ) ) ) { |
393
|
|
|
$input_type = 'multi'; //multiselect |
394
|
|
|
} elseif ( in_array( $field_type, array( 'select', 'radio' ) ) ) { |
395
|
|
|
$input_type = 'select'; |
396
|
|
|
} elseif ( in_array( $field_type, array( 'date' ) ) || in_array( $field_id, array( 'payment_date' ) ) ) { |
397
|
|
|
$input_type = 'date'; |
398
|
|
|
} elseif ( in_array( $field_type, array( 'number' ) ) || in_array( $field_id, array( 'payment_amount' ) ) ) { |
399
|
|
|
$input_type = 'number'; |
400
|
|
|
} elseif ( in_array( $field_type, array( 'product' ) ) ) { |
401
|
|
|
$input_type = 'product'; |
402
|
|
|
} else { |
403
|
|
|
$input_type = 'text'; |
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
/** |
407
|
|
|
* @filter `gravityview/extension/search/input_type` Modify the search form input type based on field type |
408
|
|
|
* @since 1.2 |
409
|
|
|
* @since 1.19.2 Added $field_id parameter |
410
|
|
|
* @param string $input_type Assign an input type according to the form field type. Defaults: `boolean`, `multi`, `select`, `date`, `text` |
411
|
|
|
* @param string $field_type Gravity Forms field type (also the `name` parameter of GravityView_Field classes) |
412
|
|
|
* @param string|int|float $field_id ID of the field being processed |
413
|
|
|
*/ |
414
|
|
|
$input_type = apply_filters( 'gravityview/extension/search/input_type', $input_type, $field_type, $field_id ); |
415
|
|
|
|
416
|
|
|
return $input_type; |
417
|
|
|
} |
418
|
|
|
|
419
|
|
|
/** |
420
|
|
|
* Display hidden fields to add support for sites using Default permalink structure |
421
|
|
|
* |
422
|
|
|
* @since 1.8 |
423
|
|
|
* @return array Search fields, modified if not using permalinks |
424
|
|
|
*/ |
425
|
4 |
|
public function add_no_permalink_fields( $search_fields, $object, $widget_args = array() ) { |
|
|
|
|
426
|
|
|
/** @global WP_Rewrite $wp_rewrite */ |
427
|
4 |
|
global $wp_rewrite; |
428
|
|
|
|
429
|
|
|
// Support default permalink structure |
430
|
4 |
|
if ( false === $wp_rewrite->using_permalinks() ) { |
431
|
|
|
|
432
|
|
|
// By default, use current post. |
433
|
4 |
|
$post_id = 0; |
434
|
|
|
|
435
|
|
|
// We're in the WordPress Widget context, and an overriding post ID has been set. |
436
|
4 |
|
if ( ! empty( $widget_args['post_id'] ) ) { |
437
|
|
|
$post_id = absint( $widget_args['post_id'] ); |
438
|
|
|
} |
439
|
|
|
// We're in the WordPress Widget context, and the base View ID should be used |
440
|
4 |
|
else if ( ! empty( $widget_args['view_id'] ) ) { |
441
|
|
|
$post_id = absint( $widget_args['view_id'] ); |
442
|
|
|
} |
443
|
|
|
|
444
|
4 |
|
$args = gravityview_get_permalink_query_args( $post_id ); |
445
|
|
|
|
446
|
|
|
// Add hidden fields to the search form |
447
|
4 |
|
foreach ( $args as $key => $value ) { |
|
|
|
|
448
|
4 |
|
$search_fields[] = array( |
449
|
4 |
|
'name' => $key, |
450
|
4 |
|
'input' => 'hidden', |
451
|
4 |
|
'value' => $value, |
452
|
|
|
); |
453
|
|
|
} |
454
|
|
|
} |
455
|
|
|
|
456
|
4 |
|
return $search_fields; |
457
|
|
|
} |
458
|
|
|
|
459
|
|
|
/** |
460
|
|
|
* Get the fields that are searchable for a View |
461
|
|
|
* |
462
|
|
|
* @since 2.0 |
463
|
|
|
* @since 2.0.9 Added $with_full_field parameter |
464
|
|
|
* |
465
|
|
|
* @param \GV\View|null $view |
466
|
|
|
* @param bool $with_full_field Return full field array, or just field ID? Default: false (just field ID) |
467
|
|
|
* |
468
|
|
|
* TODO: Move to \GV\View, perhaps? And return a Field_Collection |
469
|
|
|
* TODO: Use in gravityview()->request->is_search() to calculate whether a valid search |
470
|
|
|
* |
471
|
|
|
* @return array If no View, returns empty array. Otherwise, returns array of fields configured in widgets and Search Bar for a View |
472
|
|
|
*/ |
473
|
39 |
|
private function get_view_searchable_fields( $view, $with_full_field = false ) { |
474
|
|
|
|
475
|
|
|
/** |
476
|
|
|
* Find all search widgets on the view and get the searchable fields settings. |
477
|
|
|
*/ |
478
|
39 |
|
$searchable_fields = array(); |
479
|
|
|
|
480
|
39 |
|
if ( ! $view ) { |
481
|
|
|
return $searchable_fields; |
482
|
|
|
} |
483
|
|
|
|
484
|
|
|
/** |
485
|
|
|
* Include the sidebar Widgets. |
486
|
|
|
*/ |
487
|
39 |
|
$widgets = (array) get_option( 'widget_gravityview_search', array() ); |
488
|
|
|
|
489
|
39 |
|
foreach ( $widgets as $widget ) { |
490
|
39 |
|
if ( ! empty( $widget['view_id'] ) && $widget['view_id'] == $view->ID ) { |
491
|
|
|
if( $_fields = json_decode( $widget['search_fields'], true ) ) { |
492
|
|
|
foreach ( $_fields as $field ) { |
493
|
|
|
if ( empty( $field['form_id'] ) ) { |
494
|
|
|
$field['form_id'] = $view->form ? $view->form->ID : 0; |
495
|
|
|
} |
496
|
|
|
$searchable_fields[] = $with_full_field ? $field : $field['field']; |
497
|
|
|
} |
498
|
|
|
} |
499
|
|
|
} |
500
|
|
|
} |
501
|
|
|
|
502
|
39 |
|
foreach ( $view->widgets->by_id( $this->get_widget_id() )->all() as $widget ) { |
503
|
33 |
|
if( $_fields = json_decode( $widget->configuration->get( 'search_fields' ), true ) ) { |
504
|
33 |
|
foreach ( $_fields as $field ) { |
505
|
33 |
|
if ( empty( $field['form_id'] ) ) { |
506
|
33 |
|
$field['form_id'] = $view->form ? $view->form->ID : 0; |
507
|
|
|
} |
508
|
33 |
|
$searchable_fields[] = $with_full_field ? $field : $field['field']; |
509
|
|
|
} |
510
|
|
|
} |
511
|
|
|
} |
512
|
|
|
|
513
|
|
|
/** |
514
|
|
|
* @filter `gravityview/search/searchable_fields/whitelist` Modifies the fields able to be searched using the Search Bar |
515
|
|
|
* @since 2.5.1 |
516
|
|
|
* |
517
|
|
|
* @param array $searchable_fields Array of GravityView-formatted fields or only the field ID? Example: [ '1.2', 'created_by' ] |
518
|
|
|
* @param \GV\View $view Object of View being searched. |
519
|
|
|
* @param bool $with_full_field Does $searchable_fields contain the full field array or just field ID? Default: false (just field ID) |
520
|
|
|
*/ |
521
|
39 |
|
return apply_filters( 'gravityview/search/searchable_fields/whitelist', $searchable_fields, $view, $with_full_field ); |
522
|
|
|
} |
523
|
|
|
|
524
|
|
|
/** --- Frontend --- */ |
525
|
|
|
|
526
|
|
|
/** |
527
|
|
|
* Calculate the search criteria to filter entries |
528
|
|
|
* @param array $search_criteria The search criteria |
529
|
|
|
* @param int $form_id The form ID |
530
|
|
|
* @param array $args Some args |
531
|
|
|
* |
532
|
|
|
* @param bool $force_search_criteria Whether to suppress GF_Query filter, internally used in self::gf_query_filter |
533
|
|
|
* |
534
|
|
|
* @return array |
535
|
|
|
*/ |
536
|
86 |
|
public function filter_entries( $search_criteria, $form_id = null, $args = array(), $force_search_criteria = false ) { |
537
|
86 |
|
if ( ! $force_search_criteria && gravityview()->plugin->supports( \GV\Plugin::FEATURE_GFQUERY ) ) { |
538
|
|
|
/** |
539
|
|
|
* If GF_Query is available, we can construct custom conditions with nested |
540
|
|
|
* booleans on the query, giving up the old ways of flat search_criteria field_filters. |
541
|
|
|
*/ |
542
|
66 |
|
add_action( 'gravityview/view/query', array( $this, 'gf_query_filter' ), 10, 3 ); |
543
|
66 |
|
return $search_criteria; // Return the original criteria, GF_Query modification kicks in later |
544
|
|
|
} |
545
|
|
|
|
546
|
85 |
|
if( 'post' === $this->search_method ) { |
547
|
|
|
$get = $_POST; |
548
|
|
|
} else { |
549
|
85 |
|
$get = $_GET; |
550
|
|
|
} |
551
|
|
|
|
552
|
85 |
|
$view = \GV\View::by_id( \GV\Utils::get( $args, 'id' ) ); |
553
|
|
|
|
554
|
85 |
|
gravityview()->log->debug( 'Requested $_{method}: ', array( 'method' => $this->search_method, 'data' => $get ) ); |
555
|
|
|
|
556
|
85 |
|
if ( empty( $get ) || ! is_array( $get ) ) { |
557
|
58 |
|
return $search_criteria; |
558
|
|
|
} |
559
|
|
|
|
560
|
40 |
|
$get = stripslashes_deep( $get ); |
561
|
|
|
|
562
|
40 |
|
$get = gv_map_deep( $get, 'rawurldecode' ); |
563
|
|
|
|
564
|
|
|
// Make sure array key is set up |
565
|
40 |
|
$search_criteria['field_filters'] = \GV\Utils::get( $search_criteria, 'field_filters', array() ); |
566
|
|
|
|
567
|
40 |
|
$searchable_fields = $this->get_view_searchable_fields( $view ); |
568
|
40 |
|
$searchable_field_objects = $this->get_view_searchable_fields( $view, true ); |
569
|
|
|
|
570
|
|
|
// add free search |
571
|
40 |
|
if ( isset( $get['gv_search'] ) && '' !== $get['gv_search'] && in_array( 'search_all', $searchable_fields ) ) { |
572
|
|
|
|
573
|
5 |
|
$search_all_value = trim( $get['gv_search'] ); |
574
|
|
|
|
575
|
|
|
/** |
576
|
|
|
* @filter `gravityview/search-all-split-words` Search for each word separately or the whole phrase? |
577
|
|
|
* @since 1.20.2 |
578
|
|
|
* @param bool $split_words True: split a phrase into words; False: search whole word only [Default: true] |
579
|
|
|
*/ |
580
|
5 |
|
$split_words = apply_filters( 'gravityview/search-all-split-words', true ); |
581
|
|
|
|
582
|
5 |
|
if ( $split_words ) { |
583
|
|
|
|
584
|
|
|
// Search for a piece |
585
|
5 |
|
$words = explode( ' ', $search_all_value ); |
586
|
|
|
|
587
|
5 |
|
$words = array_filter( $words ); |
588
|
|
|
|
589
|
|
|
} else { |
590
|
|
|
|
591
|
|
|
// Replace multiple spaces with one space |
592
|
1 |
|
$search_all_value = preg_replace( '/\s+/ism', ' ', $search_all_value ); |
593
|
|
|
|
594
|
1 |
|
$words = array( $search_all_value ); |
595
|
|
|
} |
596
|
|
|
|
597
|
5 |
|
foreach ( $words as $word ) { |
598
|
5 |
|
$search_criteria['field_filters'][] = array( |
599
|
5 |
|
'key' => null, // The field ID to search |
600
|
5 |
|
'value' => $word, // The value to search |
601
|
5 |
|
'operator' => 'contains', // What to search in. Options: `is` or `contains` |
602
|
|
|
); |
603
|
|
|
} |
604
|
|
|
} |
605
|
|
|
|
606
|
|
|
// start date & end date |
607
|
40 |
|
if ( in_array( 'entry_date', $searchable_fields ) ) { |
608
|
|
|
/** |
609
|
|
|
* Get and normalize the dates according to the input format. |
610
|
|
|
*/ |
611
|
12 |
|
if ( $curr_start = ! empty( $get['gv_start'] ) ? $get['gv_start'] : '' ) { |
612
|
12 |
|
if( $curr_start_date = date_create_from_format( $this->get_datepicker_format( true ), $curr_start ) ) { |
613
|
12 |
|
$curr_start = $curr_start_date->format( 'Y-m-d' ); |
614
|
|
|
} |
615
|
|
|
} |
616
|
|
|
|
617
|
12 |
|
if ( $curr_end = ! empty( $get['gv_start'] ) ? ( ! empty( $get['gv_end'] ) ? $get['gv_end'] : '' ) : '' ) { |
618
|
12 |
|
if( $curr_end_date = date_create_from_format( $this->get_datepicker_format( true ), $curr_end ) ) { |
619
|
12 |
|
$curr_end = $curr_end_date->format( 'Y-m-d' ); |
620
|
|
|
} |
621
|
|
|
} |
622
|
|
|
|
623
|
12 |
|
if ( $view ) { |
624
|
|
|
/** |
625
|
|
|
* Override start and end dates if View is limited to some already. |
626
|
|
|
*/ |
627
|
12 |
|
if ( $start_date = $view->settings->get( 'start_date' ) ) { |
628
|
1 |
|
if ( $start_timestamp = strtotime( $curr_start ) ) { |
629
|
1 |
|
$curr_start = $start_timestamp < strtotime( $start_date ) ? $start_date : $curr_start; |
630
|
|
|
} |
631
|
|
|
} |
632
|
12 |
|
if ( $end_date = $view->settings->get( 'end_date' ) ) { |
633
|
|
|
if ( $end_timestamp = strtotime( $curr_end ) ) { |
634
|
|
|
$curr_end = $end_timestamp > strtotime( $end_date ) ? $end_date : $curr_end; |
635
|
|
|
} |
636
|
|
|
} |
637
|
|
|
} |
638
|
|
|
|
639
|
|
|
/** |
640
|
|
|
* @filter `gravityview_date_created_adjust_timezone` Whether to adjust the timezone for entries. \n |
641
|
|
|
* date_created is stored in UTC format. Convert search date into UTC (also used on templates/fields/date_created.php) |
642
|
|
|
* @since 1.12 |
643
|
|
|
* @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 |
644
|
|
|
* @param[in] string $context Where the filter is being called from. `search` in this case. |
645
|
|
|
*/ |
646
|
12 |
|
$adjust_tz = apply_filters( 'gravityview_date_created_adjust_timezone', true, 'search' ); |
647
|
|
|
|
648
|
|
|
/** |
649
|
|
|
* Don't set $search_criteria['start_date'] if start_date is empty as it may lead to bad query results (GFAPI::get_entries) |
650
|
|
|
*/ |
651
|
12 |
|
if ( ! empty( $curr_start ) ) { |
652
|
12 |
|
$curr_start = date( 'Y-m-d H:i:s', strtotime( $curr_start ) ); |
653
|
12 |
|
$search_criteria['start_date'] = $adjust_tz ? get_gmt_from_date( $curr_start ) : $curr_start; |
654
|
|
|
} |
655
|
|
|
|
656
|
12 |
|
if ( ! empty( $curr_end ) ) { |
657
|
|
|
// Fast-forward 24 hour on the end time |
658
|
12 |
|
$curr_end = date( 'Y-m-d H:i:s', strtotime( $curr_end ) + DAY_IN_SECONDS ); |
659
|
12 |
|
$search_criteria['end_date'] = $adjust_tz ? get_gmt_from_date( $curr_end ) : $curr_end; |
660
|
12 |
|
if ( strpos( $search_criteria['end_date'], '00:00:00' ) ) { // See https://github.com/gravityview/GravityView/issues/1056 |
661
|
12 |
|
$search_criteria['end_date'] = date( 'Y-m-d H:i:s', strtotime( $search_criteria['end_date'] ) - 1 ); |
662
|
|
|
} |
663
|
|
|
} |
664
|
|
|
} |
665
|
|
|
|
666
|
|
|
// search for a specific entry ID |
667
|
40 |
|
if ( ! empty( $get[ 'gv_id' ] ) && in_array( 'entry_id', $searchable_fields ) ) { |
668
|
2 |
|
$search_criteria['field_filters'][] = array( |
669
|
2 |
|
'key' => 'id', |
670
|
2 |
|
'value' => absint( $get[ 'gv_id' ] ), |
671
|
2 |
|
'operator' => $this->get_operator( $get, 'gv_id', array( '=' ), '=' ), |
672
|
|
|
); |
673
|
|
|
} |
674
|
|
|
|
675
|
|
|
// search for a specific Created_by ID |
676
|
40 |
|
if ( ! empty( $get[ 'gv_by' ] ) && in_array( 'created_by', $searchable_fields ) ) { |
677
|
4 |
|
$search_criteria['field_filters'][] = array( |
678
|
4 |
|
'key' => 'created_by', |
679
|
4 |
|
'value' => $get['gv_by'], |
680
|
4 |
|
'operator' => $this->get_operator( $get, 'gv_by', array( '=' ), '=' ), |
681
|
|
|
); |
682
|
|
|
} |
683
|
|
|
|
684
|
|
|
// Get search mode passed in URL |
685
|
40 |
|
$mode = isset( $get['mode'] ) && in_array( $get['mode'], array( 'any', 'all' ) ) ? $get['mode'] : 'any'; |
686
|
|
|
|
687
|
|
|
// get the other search filters |
688
|
40 |
|
foreach ( $get as $key => $value ) { |
689
|
|
|
|
690
|
40 |
|
if ( 0 !== strpos( $key, 'filter_' ) || gv_empty( $value, false, false ) || ( is_array( $value ) && count( $value ) === 1 && gv_empty( $value[0], false, false ) ) ) { |
691
|
27 |
|
continue; // Not a filter, or empty |
692
|
|
|
} |
693
|
|
|
|
694
|
17 |
|
if ( strpos( $key, '|op' ) !== false ) { |
695
|
1 |
|
continue; // This is an operator |
696
|
|
|
} |
697
|
|
|
|
698
|
17 |
|
$filter_key = $this->convert_request_key_to_filter_key( $key ); |
699
|
|
|
|
700
|
17 |
|
if ( ! $filter = $this->prepare_field_filter( $filter_key, $value, $view, $searchable_field_objects, $get ) ) { |
701
|
3 |
|
continue; |
702
|
|
|
} |
703
|
|
|
|
704
|
16 |
|
if ( ! isset( $filter['operator'] ) ) { |
705
|
7 |
|
$filter['operator'] = $this->get_operator( $get, $key, array( 'contains' ), 'contains' ); |
706
|
|
|
} |
707
|
|
|
|
708
|
16 |
|
if ( isset( $filter[0]['value'] ) ) { |
709
|
|
|
$search_criteria['field_filters'] = array_merge( $search_criteria['field_filters'], $filter ); |
710
|
|
|
|
711
|
|
|
// if date range type, set search mode to ALL |
712
|
|
|
if ( ! empty( $filter[0]['operator'] ) && in_array( $filter[0]['operator'], array( '>=', '<=', '>', '<' ) ) ) { |
713
|
|
|
$mode = 'all'; |
714
|
|
|
} |
715
|
16 |
|
} elseif( !empty( $filter ) ) { |
716
|
16 |
|
$search_criteria['field_filters'][] = $filter; |
717
|
|
|
} |
718
|
|
|
} |
719
|
|
|
|
720
|
|
|
/** |
721
|
|
|
* @filter `gravityview/search/mode` Set the Search Mode (`all` or `any`) |
722
|
|
|
* @since 1.5.1 |
723
|
|
|
* @param[out,in] string $mode Search mode (`any` vs `all`) |
724
|
|
|
*/ |
725
|
40 |
|
$search_criteria['field_filters']['mode'] = apply_filters( 'gravityview/search/mode', $mode ); |
726
|
|
|
|
727
|
40 |
|
gravityview()->log->debug( 'Returned Search Criteria: ', array( 'data' => $search_criteria ) ); |
728
|
|
|
|
729
|
40 |
|
unset( $get ); |
730
|
|
|
|
731
|
40 |
|
return $search_criteria; |
732
|
|
|
} |
733
|
|
|
|
734
|
|
|
/** |
735
|
|
|
* Filters the \GF_Query with advanced logic. |
736
|
|
|
* |
737
|
|
|
* Dropin for the legacy flat filters when \GF_Query is available. |
738
|
|
|
* |
739
|
|
|
* @param \GF_Query $query The current query object reference |
740
|
|
|
* @param \GV\View $this The current view object |
|
|
|
|
741
|
|
|
* @param \GV\Request $request The request object |
742
|
|
|
*/ |
743
|
65 |
|
public function gf_query_filter( &$query, $view, $request ) { |
|
|
|
|
744
|
|
|
/** |
745
|
|
|
* This is a shortcut to get all the needed search criteria. |
746
|
|
|
* We feed these into an new GF_Query and tack them onto the current object. |
747
|
|
|
*/ |
748
|
65 |
|
$search_criteria = $this->filter_entries( array(), null, array( 'id' => $view->ID ), true /** force search_criteria */ ); |
749
|
|
|
|
750
|
|
|
/** |
751
|
|
|
* Call any userland filters that they might have. |
752
|
|
|
*/ |
753
|
65 |
|
remove_filter( 'gravityview_fe_search_criteria', array( $this, 'filter_entries' ), 10, 3 ); |
754
|
65 |
|
$search_criteria = apply_filters( 'gravityview_fe_search_criteria', $search_criteria, $view->form->ID, $view->settings->as_atts() ); |
755
|
65 |
|
add_filter( 'gravityview_fe_search_criteria', array( $this, 'filter_entries' ), 10, 3 ); |
756
|
|
|
|
757
|
65 |
|
$query_class = $view->get_query_class(); |
758
|
|
|
|
759
|
65 |
|
if ( empty( $search_criteria['field_filters'] ) ) { |
760
|
57 |
|
return; |
761
|
|
|
} |
762
|
|
|
|
763
|
20 |
|
$widgets = $view->widgets->by_id( $this->widget_id ); |
764
|
20 |
|
if ( $widgets->count() ) { |
765
|
14 |
|
$widgets = $widgets->all(); |
766
|
14 |
|
$widget = $widgets[0]; |
767
|
|
|
|
768
|
14 |
|
$search_fields = json_decode( $widget->configuration->get( 'search_fields' ), true ); |
769
|
|
|
|
770
|
14 |
|
foreach ( (array) $search_fields as $search_field ) { |
771
|
14 |
|
if ( 'created_by' === $search_field['field'] && 'input_text' === $search_field['input'] ) { |
772
|
1 |
|
$created_by_text_mode = true; |
773
|
|
|
} |
774
|
|
|
} |
775
|
|
|
} |
776
|
|
|
|
777
|
20 |
|
$extra_conditions = array(); |
778
|
20 |
|
$mode = 'any'; |
779
|
|
|
|
780
|
20 |
|
foreach ( $search_criteria['field_filters'] as &$filter ) { |
781
|
20 |
|
if ( ! is_array( $filter ) ) { |
782
|
20 |
|
if ( in_array( strtolower( $filter ), array( 'any', 'all' ) ) ) { |
783
|
20 |
|
$mode = $filter; |
784
|
|
|
} |
785
|
20 |
|
continue; |
786
|
|
|
} |
787
|
|
|
|
788
|
|
|
// Construct a manual query for unapproved statuses |
789
|
13 |
|
if ( 'is_approved' === $filter['key'] && in_array( \GravityView_Entry_Approval_Status::UNAPPROVED, (array) $filter['value'] ) ) { |
790
|
2 |
|
$_tmp_query = new $query_class( $view->form->ID, array( |
791
|
2 |
|
'field_filters' => array( |
792
|
|
|
array( |
793
|
2 |
|
'operator' => 'in', |
794
|
2 |
|
'key' => 'is_approved', |
795
|
2 |
|
'value' => (array) $filter['value'], |
796
|
|
|
), |
797
|
|
|
array( |
798
|
|
|
'operator' => 'is', |
799
|
|
|
'key' => 'is_approved', |
800
|
|
|
'value' => '', |
801
|
|
|
), |
802
|
2 |
|
'mode' => 'any' |
803
|
|
|
), |
804
|
|
|
) ); |
805
|
2 |
|
$_tmp_query_parts = $_tmp_query->_introspect(); |
806
|
|
|
|
807
|
2 |
|
$extra_conditions[] = $_tmp_query_parts['where']; |
808
|
|
|
|
809
|
2 |
|
$filter = false; |
810
|
2 |
|
continue; |
811
|
|
|
} |
812
|
|
|
|
813
|
|
|
// Construct manual query for text mode creator search |
814
|
13 |
|
if ( 'created_by' === $filter['key'] && ! empty( $created_by_text_mode ) ) { |
815
|
1 |
|
$extra_conditions[] = new GravityView_Widget_Search_Author_GF_Query_Condition( $filter, $view ); |
816
|
1 |
|
$filter = false; |
817
|
1 |
|
continue; |
818
|
|
|
} |
819
|
|
|
|
820
|
|
|
// By default, we want searches to be wildcard for each field. |
821
|
12 |
|
$filter['operator'] = empty( $filter['operator'] ) ? 'contains' : $filter['operator']; |
822
|
|
|
|
823
|
|
|
// For multichoice, let's have an in (OR) search. |
824
|
12 |
|
if ( is_array( $filter['value'] ) ) { |
825
|
3 |
|
$filter['operator'] = 'in'; // @todo what about in contains (OR LIKE chains)? |
826
|
|
|
} |
827
|
|
|
|
828
|
|
|
// Default form with joins functionality |
829
|
12 |
|
if ( empty( $filter['form_id'] ) ) { |
830
|
5 |
|
$filter['form_id'] = $view->form ? $view->form->ID : 0; |
831
|
|
|
} |
832
|
|
|
|
833
|
|
|
/** |
834
|
|
|
* @filter `gravityview_search_operator` Modify the search operator for the field (contains, is, isnot, etc) |
835
|
|
|
* @param string $operator Existing search operator |
836
|
|
|
* @param array $filter array with `key`, `value`, `operator`, `type` keys |
837
|
|
|
* @since develop |
838
|
|
|
* @param \GV\View $view The View we're operating on. |
839
|
|
|
*/ |
840
|
12 |
|
$filter['operator'] = apply_filters( 'gravityview_search_operator', $filter['operator'], $filter, $view ); |
841
|
|
|
} |
842
|
|
|
|
843
|
20 |
|
if ( ! empty( $search_criteria['start_date'] ) || ! empty( $search_criteria['end_date'] ) ) { |
844
|
1 |
|
$date_criteria = array(); |
845
|
|
|
|
846
|
1 |
|
if ( isset( $search_criteria['start_date'] ) ) { |
847
|
1 |
|
$date_criteria['start_date'] = $search_criteria['start_date']; |
848
|
|
|
} |
849
|
|
|
|
850
|
1 |
|
if ( isset( $search_criteria['end_date'] ) ) { |
851
|
1 |
|
$date_criteria['end_date'] = $search_criteria['end_date']; |
852
|
|
|
} |
853
|
|
|
|
854
|
1 |
|
$_tmp_query = new $query_class( $view->form->ID, $date_criteria ); |
855
|
1 |
|
$_tmp_query_parts = $_tmp_query->_introspect(); |
856
|
1 |
|
$extra_conditions[] = $_tmp_query_parts['where']; |
857
|
|
|
} |
858
|
|
|
|
859
|
20 |
|
$search_conditions = array(); |
860
|
|
|
|
861
|
20 |
|
if ( $filters = array_filter( $search_criteria['field_filters'] ) ) { |
862
|
20 |
|
foreach ( $filters as &$filter ) { |
863
|
20 |
|
if ( ! is_array( $filter ) ) { |
864
|
20 |
|
continue; |
865
|
|
|
} |
866
|
|
|
|
867
|
|
|
/** |
868
|
|
|
* Parse the filter criteria to generate the needed |
869
|
|
|
* WHERE condition. This is a trick to not write our own generation |
870
|
|
|
* code by reusing what's inside GF_Query already as they |
871
|
|
|
* take care of many small things like forcing numeric, etc. |
872
|
|
|
*/ |
873
|
12 |
|
$_tmp_query = new $query_class( $filter['form_id'], array( 'mode' => 'any', 'field_filters' => array( $filter ) ) ); |
874
|
12 |
|
$_tmp_query_parts = $_tmp_query->_introspect(); |
875
|
12 |
|
$search_condition = $_tmp_query_parts['where']; |
876
|
|
|
|
877
|
12 |
|
if ( empty( $filter['key'] ) && $search_condition->expressions ) { |
878
|
1 |
|
$search_conditions[] = $search_condition; |
879
|
|
|
} else { |
880
|
11 |
|
$left = $search_condition->left; |
881
|
11 |
|
$alias = $query->_alias( $left->field_id, $left->source, $left->is_entry_column() ? 't' : 'm' ); |
882
|
|
|
|
883
|
11 |
|
if ( $view->joins && $left->field_id == GF_Query_Column::META ) { |
884
|
2 |
|
foreach ( $view->joins as $_join ) { |
885
|
2 |
|
$on = $_join->join_on; |
886
|
2 |
|
$join = $_join->join; |
887
|
|
|
|
888
|
2 |
|
$search_conditions[] = GF_Query_Condition::_or( |
889
|
|
|
// Join |
890
|
2 |
|
new GF_Query_Condition( |
891
|
2 |
|
new GF_Query_Column( GF_Query_Column::META, $join->ID, $query->_alias( GF_Query_Column::META, $join->ID, 'm' ) ), |
892
|
2 |
|
$search_condition->operator, |
893
|
2 |
|
$search_condition->right |
894
|
|
|
), |
895
|
|
|
// On |
896
|
2 |
|
new GF_Query_Condition( |
897
|
2 |
|
new GF_Query_Column( GF_Query_Column::META, $on->ID, $query->_alias( GF_Query_Column::META, $on->ID, 'm' ) ), |
898
|
2 |
|
$search_condition->operator, |
899
|
2 |
|
$search_condition->right |
900
|
|
|
) |
901
|
|
|
); |
902
|
|
|
} |
903
|
|
|
} else { |
904
|
9 |
|
$search_conditions[] = new GF_Query_Condition( |
905
|
9 |
|
new GF_Query_Column( $left->field_id, $left->source, $alias ), |
906
|
9 |
|
$search_condition->operator, |
907
|
9 |
|
$search_condition->right |
908
|
|
|
); |
909
|
|
|
} |
910
|
|
|
} |
911
|
|
|
} |
912
|
|
|
|
913
|
20 |
|
if ( $search_conditions ) { |
|
|
|
|
914
|
12 |
|
$search_conditions = array( call_user_func_array( '\GF_Query_Condition::' . ( $mode == 'all' ? '_and' : '_or' ), $search_conditions ) ); |
915
|
|
|
} |
916
|
|
|
} |
917
|
|
|
|
918
|
|
|
/** |
919
|
|
|
* Grab the current clauses. We'll be combining them shortly. |
920
|
|
|
*/ |
921
|
20 |
|
$query_parts = $query->_introspect(); |
922
|
|
|
|
923
|
|
|
/** |
924
|
|
|
* Combine the parts as a new WHERE clause. |
925
|
|
|
*/ |
926
|
20 |
|
$where = call_user_func_array( '\GF_Query_Condition::_and', array_merge( array( $query_parts['where'] ), $search_conditions, $extra_conditions ) ); |
927
|
20 |
|
$query->where( $where ); |
928
|
20 |
|
} |
929
|
|
|
|
930
|
|
|
/** |
931
|
|
|
* Convert $_GET/$_POST key to the field/meta ID |
932
|
|
|
* |
933
|
|
|
* Examples: |
934
|
|
|
* - `filter_is_starred` => `is_starred` |
935
|
|
|
* - `filter_1_2` => `1.2` |
936
|
|
|
* - `filter_5` => `5` |
937
|
|
|
* |
938
|
|
|
* @since 2.0 |
939
|
|
|
* |
940
|
|
|
* @param string $key $_GET/_$_POST search key |
941
|
|
|
* |
942
|
|
|
* @return string |
943
|
|
|
*/ |
944
|
17 |
|
private function convert_request_key_to_filter_key( $key ) { |
945
|
|
|
|
946
|
17 |
|
$field_id = str_replace( 'filter_', '', $key ); |
947
|
|
|
|
948
|
|
|
// calculates field_id, removing 'filter_' and for '_' for advanced fields ( like name or checkbox ) |
949
|
17 |
|
if ( preg_match('/^[0-9_]+$/ism', $field_id ) ) { |
950
|
15 |
|
$field_id = str_replace( '_', '.', $field_id ); |
951
|
|
|
} |
952
|
|
|
|
953
|
17 |
|
return $field_id; |
954
|
|
|
} |
955
|
|
|
|
956
|
|
|
/** |
957
|
|
|
* Prepare the field filters to GFAPI |
958
|
|
|
* |
959
|
|
|
* 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. |
960
|
|
|
* |
961
|
|
|
* Format searched values |
962
|
|
|
* |
963
|
|
|
* @param string $filter_key ID of the field, or entry meta key |
964
|
|
|
* @param string $value $_GET/$_POST search value |
965
|
|
|
* @param \GV\View $view The view we're looking at |
966
|
|
|
* @param array[] $searchable_fields The searchable fields as configured by the widget. |
967
|
|
|
* @param string[] $get The $_GET/$_POST array. |
968
|
|
|
* |
969
|
|
|
* @since develop Added 5th $get parameter for operator overrides. |
970
|
|
|
* @todo Set function as private. |
971
|
|
|
* |
972
|
|
|
* @return array|false 1 or 2 deph levels, false if not allowed |
973
|
|
|
*/ |
974
|
17 |
|
public function prepare_field_filter( $filter_key, $value, $view, $searchable_fields, $get = array() ) { |
975
|
17 |
|
$key = $filter_key; |
976
|
17 |
|
$filter_key = explode( ':', $filter_key ); // field_id, form_id |
977
|
|
|
|
978
|
17 |
|
$form = null; |
979
|
|
|
|
980
|
17 |
|
if ( count( $filter_key ) > 1 ) { |
981
|
|
|
// form is specified |
982
|
1 |
|
list( $field_id, $form_id ) = $filter_key; |
983
|
|
|
|
984
|
1 |
|
if ( $forms = \GV\View::get_joined_forms( $view->ID ) ) { |
985
|
1 |
|
if ( ! $form = \GV\GF_Form::by_id( $form_id ) ) { |
986
|
|
|
return false; |
987
|
|
|
} |
988
|
|
|
} |
989
|
|
|
|
990
|
|
|
// form is allowed |
991
|
1 |
|
$found = false; |
992
|
1 |
|
foreach ( $forms as $form ) { |
993
|
1 |
|
if ( $form->ID == $form_id ) { |
994
|
1 |
|
$found = true; |
995
|
1 |
|
break; |
996
|
|
|
} |
997
|
|
|
} |
998
|
|
|
|
999
|
1 |
|
if ( ! $found ) { |
1000
|
|
|
return false; |
1001
|
|
|
} |
1002
|
|
|
|
1003
|
|
|
// form is in searchable fields |
1004
|
1 |
|
$found = false; |
1005
|
1 |
|
foreach ( $searchable_fields as $field ) { |
1006
|
1 |
|
if ( $field_id == $field['field'] && $form->ID == $field['form_id'] ) { |
1007
|
1 |
|
$found = true; |
1008
|
1 |
|
break; |
1009
|
|
|
} |
1010
|
|
|
} |
1011
|
|
|
|
1012
|
1 |
|
if ( ! $found ) { |
1013
|
1 |
|
return false; |
1014
|
|
|
} |
1015
|
|
|
} else { |
1016
|
17 |
|
$field_id = reset( $filter_key ); |
1017
|
17 |
|
$searchable_fields = wp_list_pluck( $searchable_fields, 'field' ); |
1018
|
17 |
|
if ( ! in_array( 'search_all', $searchable_fields ) && ! in_array( $field_id, $searchable_fields ) ) { |
1019
|
2 |
|
return false; |
1020
|
|
|
} |
1021
|
|
|
} |
1022
|
|
|
|
1023
|
16 |
|
if ( ! $form ) { |
1024
|
|
|
// fallback |
1025
|
16 |
|
$form = $view->form; |
1026
|
|
|
} |
1027
|
|
|
|
1028
|
|
|
// get form field array |
1029
|
16 |
|
$form_field = is_numeric( $field_id ) ? \GV\GF_Field::by_id( $form, $field_id ) : \GV\Internal_Field::by_id( $field_id ); |
1030
|
|
|
|
1031
|
16 |
|
if ( ! $form_field ) { |
1032
|
|
|
return false; |
1033
|
|
|
} |
1034
|
|
|
|
1035
|
|
|
// default filter array |
1036
|
|
|
$filter = array( |
1037
|
16 |
|
'key' => $field_id, |
1038
|
16 |
|
'value' => $value, |
1039
|
16 |
|
'form_id' => $form->ID, |
1040
|
|
|
); |
1041
|
|
|
|
1042
|
16 |
|
switch ( $form_field->type ) { |
1043
|
|
|
|
1044
|
16 |
|
case 'select': |
1045
|
16 |
|
case 'radio': |
1046
|
1 |
|
$filter['operator'] = $this->get_operator( $get, $key, array( 'is' ), 'is' ); |
1047
|
1 |
|
break; |
1048
|
|
|
|
1049
|
15 |
|
case 'post_category': |
1050
|
|
|
|
1051
|
|
|
if ( ! is_array( $value ) ) { |
1052
|
|
|
$value = array( $value ); |
1053
|
|
|
} |
1054
|
|
|
|
1055
|
|
|
// Reset filter variable |
1056
|
|
|
$filter = array(); |
1057
|
|
|
|
1058
|
|
|
foreach ( $value as $val ) { |
1059
|
|
|
$cat = get_term( $val, 'category' ); |
1060
|
|
|
$filter[] = array( |
1061
|
|
|
'key' => $field_id, |
1062
|
|
|
'value' => esc_attr( $cat->name ) . ':' . $val, |
1063
|
|
|
'operator' => $this->get_operator( $get, $key, array( 'is' ), 'is' ), |
1064
|
|
|
); |
1065
|
|
|
} |
1066
|
|
|
|
1067
|
|
|
break; |
1068
|
|
|
|
1069
|
15 |
|
case 'multiselect': |
1070
|
|
|
|
1071
|
|
|
if ( ! is_array( $value ) ) { |
1072
|
|
|
break; |
1073
|
|
|
} |
1074
|
|
|
|
1075
|
|
|
// Reset filter variable |
1076
|
|
|
$filter = array(); |
1077
|
|
|
|
1078
|
|
|
foreach ( $value as $val ) { |
1079
|
|
|
$filter[] = array( 'key' => $field_id, 'value' => $val ); |
1080
|
|
|
} |
1081
|
|
|
|
1082
|
|
|
break; |
1083
|
|
|
|
1084
|
15 |
|
case 'checkbox': |
1085
|
|
|
// convert checkbox on/off into the correct search filter |
1086
|
|
|
if ( false !== strpos( $field_id, '.' ) && ! empty( $form_field->inputs ) && ! empty( $form_field->choices ) ) { |
1087
|
|
|
foreach ( $form_field->inputs as $k => $input ) { |
1088
|
|
|
if ( $input['id'] == $field_id ) { |
1089
|
|
|
$filter['value'] = $form_field->choices[ $k ]['value']; |
1090
|
|
|
$filter['operator'] = $this->get_operator( $get, $key, array( 'is' ), 'is' ); |
1091
|
|
|
break; |
1092
|
|
|
} |
1093
|
|
|
} |
1094
|
|
|
} elseif ( is_array( $value ) ) { |
1095
|
|
|
|
1096
|
|
|
// Reset filter variable |
1097
|
|
|
$filter = array(); |
1098
|
|
|
|
1099
|
|
|
foreach ( $value as $val ) { |
1100
|
|
|
$filter[] = array( |
1101
|
|
|
'key' => $field_id, |
1102
|
|
|
'value' => $val, |
1103
|
|
|
'operator' => $this->get_operator( $get, $key, array( 'is' ), 'is' ), |
1104
|
|
|
); |
1105
|
|
|
} |
1106
|
|
|
} |
1107
|
|
|
|
1108
|
|
|
break; |
1109
|
|
|
|
1110
|
15 |
|
case 'name': |
1111
|
15 |
|
case 'address': |
1112
|
|
|
|
1113
|
1 |
|
if ( false === strpos( $field_id, '.' ) ) { |
1114
|
|
|
|
1115
|
|
|
$words = explode( ' ', $value ); |
1116
|
|
|
|
1117
|
|
|
$filters = array(); |
1118
|
|
|
foreach ( $words as $word ) { |
1119
|
|
|
if ( ! empty( $word ) && strlen( $word ) > 1 ) { |
1120
|
|
|
// Keep the same key for each filter |
1121
|
|
|
$filter['value'] = $word; |
1122
|
|
|
// Add a search for the value |
1123
|
|
|
$filters[] = $filter; |
1124
|
|
|
} |
1125
|
|
|
} |
1126
|
|
|
|
1127
|
|
|
$filter = $filters; |
1128
|
|
|
} |
1129
|
|
|
|
1130
|
|
|
// State/Province should be exact matches |
1131
|
1 |
|
if ( 'address' === $form_field->field->type ) { |
1132
|
|
|
|
1133
|
1 |
|
$searchable_fields = $this->get_view_searchable_fields( $view, true ); |
1134
|
|
|
|
1135
|
1 |
|
foreach ( $searchable_fields as $searchable_field ) { |
1136
|
|
|
|
1137
|
1 |
|
if( $form_field->ID !== $searchable_field['field'] ) { |
1138
|
|
|
continue; |
1139
|
|
|
} |
1140
|
|
|
|
1141
|
|
|
// Only exact-match dropdowns, not text search |
1142
|
1 |
|
if( in_array( $searchable_field['input'], array( 'text', 'search' ), true ) ) { |
1143
|
1 |
|
continue; |
1144
|
|
|
} |
1145
|
|
|
|
1146
|
|
|
$input_id = gravityview_get_input_id_from_id( $form_field->ID ); |
1147
|
|
|
|
1148
|
|
|
if ( 4 === $input_id ) { |
1149
|
|
|
$filter['operator'] = $this->get_operator( $get, $key, array( 'is' ), 'is' ); |
1150
|
|
|
}; |
1151
|
|
|
} |
1152
|
|
|
} |
1153
|
|
|
|
1154
|
1 |
|
break; |
1155
|
|
|
|
1156
|
15 |
|
case 'date': |
1157
|
|
|
|
1158
|
8 |
|
$date_format = $this->get_datepicker_format( true ); |
1159
|
|
|
|
1160
|
8 |
|
if ( is_array( $value ) ) { |
1161
|
|
|
|
1162
|
|
|
// Reset filter variable |
1163
|
|
|
$filter = array(); |
1164
|
|
|
|
1165
|
|
|
foreach ( $value as $k => $date ) { |
1166
|
|
|
if ( empty( $date ) ) { |
1167
|
|
|
continue; |
1168
|
|
|
} |
1169
|
|
|
$operator = 'start' === $k ? '>=' : '<='; |
1170
|
|
|
|
1171
|
|
|
/** |
1172
|
|
|
* @hack |
1173
|
|
|
* @since 1.16.3 |
1174
|
|
|
* Safeguard until GF implements '<=' operator |
1175
|
|
|
*/ |
1176
|
|
|
if( !GFFormsModel::is_valid_operator( $operator ) && $operator === '<=' ) { |
1177
|
|
|
$operator = '<'; |
1178
|
|
|
$date = date( 'Y-m-d', strtotime( self::get_formatted_date( $date, 'Y-m-d', $date_format ) . ' +1 day' ) ); |
1179
|
|
|
} |
1180
|
|
|
|
1181
|
|
|
$filter[] = array( |
1182
|
|
|
'key' => $field_id, |
1183
|
|
|
'value' => self::get_formatted_date( $date, 'Y-m-d', $date_format ), |
1184
|
|
|
'operator' => $this->get_operator( $get, $key, array( $operator ), $operator ), |
1185
|
|
|
); |
1186
|
|
|
} |
1187
|
|
|
} else { |
1188
|
8 |
|
$date = $value; |
1189
|
8 |
|
$filter['value'] = self::get_formatted_date( $date, 'Y-m-d', $date_format ); |
1190
|
8 |
|
$filter['operator'] = $this->get_operator( $get, $key, array( 'is' ), 'is' ); |
1191
|
|
|
} |
1192
|
|
|
|
1193
|
8 |
|
break; |
1194
|
|
|
|
1195
|
|
|
|
1196
|
|
|
} // switch field type |
1197
|
|
|
|
1198
|
16 |
|
return $filter; |
1199
|
|
|
} |
1200
|
|
|
|
1201
|
|
|
/** |
1202
|
|
|
* Get the Field Format form GravityForms |
1203
|
|
|
* |
1204
|
|
|
* @param GF_Field_Date $field The field object |
1205
|
|
|
* @since 1.10 |
1206
|
|
|
* |
1207
|
|
|
* @return string Format of the date in the database |
1208
|
|
|
*/ |
1209
|
|
|
public static function get_date_field_format( GF_Field_Date $field ) { |
1210
|
|
|
$format = 'm/d/Y'; |
1211
|
|
|
$datepicker = array( |
1212
|
|
|
'mdy' => 'm/d/Y', |
1213
|
|
|
'dmy' => 'd/m/Y', |
1214
|
|
|
'dmy_dash' => 'd-m-Y', |
1215
|
|
|
'dmy_dot' => 'd.m.Y', |
1216
|
|
|
'ymd_slash' => 'Y/m/d', |
1217
|
|
|
'ymd_dash' => 'Y-m-d', |
1218
|
|
|
'ymd_dot' => 'Y.m.d', |
1219
|
|
|
); |
1220
|
|
|
|
1221
|
|
|
if ( ! empty( $field->dateFormat ) && isset( $datepicker[ $field->dateFormat ] ) ){ |
1222
|
|
|
$format = $datepicker[ $field->dateFormat ]; |
1223
|
|
|
} |
1224
|
|
|
|
1225
|
|
|
return $format; |
1226
|
|
|
} |
1227
|
|
|
|
1228
|
|
|
/** |
1229
|
|
|
* Format a date value |
1230
|
|
|
* |
1231
|
|
|
* @param string $value Date value input |
1232
|
|
|
* @param string $format Wanted formatted date |
1233
|
|
|
* |
1234
|
|
|
* @since 2.1.2 |
1235
|
|
|
* @param string $value_format The value format. Default: Y-m-d |
1236
|
|
|
* |
1237
|
|
|
* @return string |
1238
|
|
|
*/ |
1239
|
8 |
|
public static function get_formatted_date( $value = '', $format = 'Y-m-d', $value_format = 'Y-m-d' ) { |
1240
|
|
|
|
1241
|
8 |
|
$date = date_create_from_format( $value_format, $value ); |
1242
|
|
|
|
1243
|
8 |
|
if ( empty( $date ) ) { |
1244
|
|
|
gravityview()->log->debug( 'Date format not valid: {value}', array( 'value' => $value ) ); |
1245
|
|
|
return ''; |
1246
|
|
|
} |
1247
|
8 |
|
return $date->format( $format ); |
1248
|
|
|
} |
1249
|
|
|
|
1250
|
|
|
|
1251
|
|
|
/** |
1252
|
|
|
* Include this extension templates path |
1253
|
|
|
* @param array $file_paths List of template paths ordered |
1254
|
|
|
*/ |
1255
|
1 |
|
public function add_template_path( $file_paths ) { |
1256
|
|
|
|
1257
|
|
|
// Index 100 is the default GravityView template path. |
1258
|
1 |
|
$file_paths[102] = self::$file . 'templates/'; |
1259
|
|
|
|
1260
|
1 |
|
return $file_paths; |
1261
|
|
|
} |
1262
|
|
|
|
1263
|
|
|
/** |
1264
|
|
|
* Check whether the configured search fields have a date field |
1265
|
|
|
* |
1266
|
|
|
* @since 1.17.5 |
1267
|
|
|
* |
1268
|
|
|
* @param array $search_fields |
1269
|
|
|
* |
1270
|
|
|
* @return bool True: has a `date` or `date_range` field |
1271
|
|
|
*/ |
1272
|
4 |
|
private function has_date_field( $search_fields ) { |
1273
|
|
|
|
1274
|
4 |
|
$has_date = false; |
1275
|
|
|
|
1276
|
4 |
|
foreach ( $search_fields as $k => $field ) { |
1277
|
4 |
|
if ( in_array( $field['input'], array( 'date', 'date_range', 'entry_date' ) ) ) { |
1278
|
|
|
$has_date = true; |
1279
|
|
|
break; |
1280
|
|
|
} |
1281
|
|
|
} |
1282
|
|
|
|
1283
|
4 |
|
return $has_date; |
1284
|
|
|
} |
1285
|
|
|
|
1286
|
|
|
/** |
1287
|
|
|
* Renders the Search Widget |
1288
|
|
|
* @param array $widget_args |
1289
|
|
|
* @param string $content |
1290
|
|
|
* @param string $context |
1291
|
|
|
* |
1292
|
|
|
* @return void |
1293
|
|
|
*/ |
1294
|
4 |
|
public function render_frontend( $widget_args, $content = '', $context = '' ) { |
1295
|
|
|
/** @var GravityView_View $gravityview_view */ |
1296
|
4 |
|
$gravityview_view = GravityView_View::getInstance(); |
1297
|
|
|
|
1298
|
4 |
|
if ( empty( $gravityview_view ) ) { |
1299
|
|
|
gravityview()->log->debug( '$gravityview_view not instantiated yet.' ); |
1300
|
|
|
return; |
1301
|
|
|
} |
1302
|
|
|
|
1303
|
|
|
// get configured search fields |
1304
|
4 |
|
$search_fields = ! empty( $widget_args['search_fields'] ) ? json_decode( $widget_args['search_fields'], true ) : ''; |
1305
|
|
|
|
1306
|
4 |
|
if ( empty( $search_fields ) || ! is_array( $search_fields ) ) { |
1307
|
|
|
gravityview()->log->debug( 'No search fields configured for widget:', array( 'data' => $widget_args ) ); |
1308
|
|
|
return; |
1309
|
|
|
} |
1310
|
|
|
|
1311
|
4 |
|
$view = \GV\View::by_id( $gravityview_view->view_id ); |
1312
|
|
|
|
1313
|
|
|
// prepare fields |
1314
|
4 |
|
foreach ( $search_fields as $k => $field ) { |
1315
|
|
|
|
1316
|
4 |
|
$updated_field = $field; |
1317
|
|
|
|
1318
|
4 |
|
$updated_field = $this->get_search_filter_details( $updated_field, $context ); |
1319
|
|
|
|
1320
|
4 |
|
switch ( $field['field'] ) { |
1321
|
|
|
|
1322
|
4 |
|
case 'search_all': |
1323
|
4 |
|
$updated_field['key'] = 'search_all'; |
1324
|
4 |
|
$updated_field['input'] = 'search_all'; |
1325
|
4 |
|
$updated_field['value'] = $this->rgget_or_rgpost( 'gv_search' ); |
1326
|
4 |
|
break; |
1327
|
|
|
|
1328
|
|
|
case 'entry_date': |
1329
|
|
|
$updated_field['key'] = 'entry_date'; |
1330
|
|
|
$updated_field['input'] = 'entry_date'; |
1331
|
|
|
$updated_field['value'] = array( |
1332
|
|
|
'start' => $this->rgget_or_rgpost( 'gv_start' ), |
1333
|
|
|
'end' => $this->rgget_or_rgpost( 'gv_end' ), |
1334
|
|
|
); |
1335
|
|
|
break; |
1336
|
|
|
|
1337
|
|
|
case 'entry_id': |
1338
|
|
|
$updated_field['key'] = 'entry_id'; |
1339
|
|
|
$updated_field['input'] = 'entry_id'; |
1340
|
|
|
$updated_field['value'] = $this->rgget_or_rgpost( 'gv_id' ); |
1341
|
|
|
break; |
1342
|
|
|
|
1343
|
|
|
case 'created_by': |
1344
|
|
|
$updated_field['key'] = 'created_by'; |
1345
|
|
|
$updated_field['name'] = 'gv_by'; |
1346
|
|
|
$updated_field['value'] = $this->rgget_or_rgpost( 'gv_by' ); |
1347
|
|
|
$updated_field['choices'] = self::get_created_by_choices( $view ); |
1348
|
|
|
break; |
1349
|
|
|
|
1350
|
|
|
case 'is_approved': |
1351
|
|
|
$updated_field['key'] = 'is_approved'; |
1352
|
|
|
$updated_field['value'] = $this->rgget_or_rgpost( 'filter_is_approved' ); |
1353
|
|
|
$updated_field['choices'] = self::get_is_approved_choices(); |
1354
|
|
|
break; |
1355
|
|
|
} |
1356
|
|
|
|
1357
|
4 |
|
$search_fields[ $k ] = $updated_field; |
1358
|
|
|
} |
1359
|
|
|
|
1360
|
4 |
|
gravityview()->log->debug( 'Calculated Search Fields: ', array( 'data' => $search_fields ) ); |
1361
|
|
|
|
1362
|
|
|
/** |
1363
|
|
|
* @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. |
1364
|
|
|
* @param array $search_fields Array of search filters with `key`, `label`, `value`, `type`, `choices` keys |
1365
|
|
|
* @param GravityView_Widget_Search $this Current widget object |
1366
|
|
|
* @param array $widget_args Args passed to this method. {@since 1.8} |
1367
|
|
|
* @param \GV\Template_Context $context {@since 2.0} |
1368
|
|
|
* @var array |
1369
|
|
|
*/ |
1370
|
4 |
|
$gravityview_view->search_fields = apply_filters( 'gravityview_widget_search_filters', $search_fields, $this, $widget_args, $context ); |
|
|
|
|
1371
|
|
|
|
1372
|
4 |
|
$gravityview_view->permalink_fields = $this->add_no_permalink_fields( array(), $this, $widget_args ); |
|
|
|
|
1373
|
|
|
|
1374
|
4 |
|
$gravityview_view->search_layout = ! empty( $widget_args['search_layout'] ) ? $widget_args['search_layout'] : 'horizontal'; |
|
|
|
|
1375
|
|
|
|
1376
|
|
|
/** @since 1.14 */ |
1377
|
4 |
|
$gravityview_view->search_mode = ! empty( $widget_args['search_mode'] ) ? $widget_args['search_mode'] : 'any'; |
|
|
|
|
1378
|
|
|
|
1379
|
4 |
|
$custom_class = ! empty( $widget_args['custom_class'] ) ? $widget_args['custom_class'] : ''; |
1380
|
|
|
|
1381
|
4 |
|
$gravityview_view->search_class = self::get_search_class( $custom_class ); |
|
|
|
|
1382
|
|
|
|
1383
|
4 |
|
$gravityview_view->search_clear = ! empty( $widget_args['search_clear'] ) ? $widget_args['search_clear'] : false; |
|
|
|
|
1384
|
|
|
|
1385
|
4 |
|
if ( $this->has_date_field( $search_fields ) ) { |
1386
|
|
|
// enqueue datepicker stuff only if needed! |
1387
|
|
|
$this->enqueue_datepicker(); |
1388
|
|
|
} |
1389
|
|
|
|
1390
|
4 |
|
$this->maybe_enqueue_flexibility(); |
1391
|
|
|
|
1392
|
4 |
|
$gravityview_view->render( 'widget', 'search', false ); |
1393
|
4 |
|
} |
1394
|
|
|
|
1395
|
|
|
/** |
1396
|
|
|
* Get the search class for a search form |
1397
|
|
|
* |
1398
|
|
|
* @since 1.5.4 |
1399
|
|
|
* |
1400
|
|
|
* @return string Sanitized CSS class for the search form |
1401
|
|
|
*/ |
1402
|
4 |
|
public static function get_search_class( $custom_class = '' ) { |
1403
|
4 |
|
$gravityview_view = GravityView_View::getInstance(); |
1404
|
|
|
|
1405
|
4 |
|
$search_class = 'gv-search-'.$gravityview_view->search_layout; |
|
|
|
|
1406
|
|
|
|
1407
|
4 |
|
if ( ! empty( $custom_class ) ) { |
1408
|
|
|
$search_class .= ' '.$custom_class; |
1409
|
|
|
} |
1410
|
|
|
|
1411
|
|
|
/** |
1412
|
|
|
* @filter `gravityview_search_class` Modify the CSS class for the search form |
1413
|
|
|
* @param string $search_class The CSS class for the search form |
1414
|
|
|
*/ |
1415
|
4 |
|
$search_class = apply_filters( 'gravityview_search_class', $search_class ); |
1416
|
|
|
|
1417
|
|
|
// Is there an active search being performed? Used by fe-views.js |
1418
|
4 |
|
$search_class .= GravityView_frontend::getInstance()->isSearch() ? ' gv-is-search' : ''; |
1419
|
|
|
|
1420
|
4 |
|
return gravityview_sanitize_html_class( $search_class ); |
1421
|
|
|
} |
1422
|
|
|
|
1423
|
|
|
|
1424
|
|
|
/** |
1425
|
|
|
* Calculate the search form action |
1426
|
|
|
* @since 1.6 |
1427
|
|
|
* |
1428
|
|
|
* @return string |
1429
|
|
|
*/ |
1430
|
4 |
|
public static function get_search_form_action() { |
1431
|
4 |
|
$gravityview_view = GravityView_View::getInstance(); |
1432
|
|
|
|
1433
|
4 |
|
$post_id = $gravityview_view->getPostId() ? $gravityview_view->getPostId() : $gravityview_view->getViewId(); |
1434
|
|
|
|
1435
|
4 |
|
$url = add_query_arg( array(), get_permalink( $post_id ) ); |
1436
|
|
|
|
1437
|
4 |
|
return esc_url( $url ); |
1438
|
|
|
} |
1439
|
|
|
|
1440
|
|
|
/** |
1441
|
|
|
* Get the label for a search form field |
1442
|
|
|
* @param array $field Field setting as sent by the GV configuration - has `field`, `input` (input type), and `label` keys |
1443
|
|
|
* @param array $form_field Form field data, as fetched by `gravityview_get_field()` |
1444
|
|
|
* @return string Label for the search form |
1445
|
|
|
*/ |
1446
|
4 |
|
private static function get_field_label( $field, $form_field = array() ) { |
1447
|
|
|
|
1448
|
4 |
|
$label = \GV\Utils::_GET( 'label', \GV\Utils::get( $field, 'label' ) ); |
1449
|
|
|
|
1450
|
4 |
|
if ( ! $label ) { |
1451
|
|
|
|
1452
|
4 |
|
$label = isset( $form_field['label'] ) ? $form_field['label'] : ''; |
1453
|
|
|
|
1454
|
4 |
|
switch( $field['field'] ) { |
1455
|
4 |
|
case 'search_all': |
1456
|
4 |
|
$label = __( 'Search Entries:', 'gravityview' ); |
1457
|
4 |
|
break; |
1458
|
|
|
case 'entry_date': |
1459
|
|
|
$label = __( 'Filter by date:', 'gravityview' ); |
1460
|
|
|
break; |
1461
|
|
|
case 'entry_id': |
1462
|
|
|
$label = __( 'Entry ID:', 'gravityview' ); |
1463
|
|
|
break; |
1464
|
|
|
default: |
1465
|
|
|
// If this is a field input, not a field |
1466
|
|
|
if ( strpos( $field['field'], '.' ) > 0 && ! empty( $form_field['inputs'] ) ) { |
1467
|
|
|
|
1468
|
|
|
// Get the label for the field in question, which returns an array |
1469
|
|
|
$items = wp_list_filter( $form_field['inputs'], array( 'id' => $field['field'] ) ); |
1470
|
|
|
|
1471
|
|
|
// Get the item with the `label` key |
1472
|
|
|
$values = wp_list_pluck( $items, 'label' ); |
1473
|
|
|
|
1474
|
|
|
// There will only one item in the array, but this is easier |
1475
|
|
|
foreach ( $values as $value ) { |
1476
|
|
|
$label = $value; |
1477
|
|
|
break; |
1478
|
|
|
} |
1479
|
|
|
} |
1480
|
|
|
} |
1481
|
|
|
} |
1482
|
|
|
|
1483
|
|
|
/** |
1484
|
|
|
* @filter `gravityview_search_field_label` Modify the label for a search field. Supports returning HTML |
1485
|
|
|
* @since 1.17.3 Added $field parameter |
1486
|
|
|
* @param[in,out] string $label Existing label text, sanitized. |
1487
|
|
|
* @param[in] array $form_field Gravity Forms field array, as returned by `GFFormsModel::get_field()` |
1488
|
|
|
* @param[in] array $field Field setting as sent by the GV configuration - has `field`, `input` (input type), and `label` keys |
1489
|
|
|
*/ |
1490
|
4 |
|
$label = apply_filters( 'gravityview_search_field_label', esc_attr( $label ), $form_field, $field ); |
1491
|
|
|
|
1492
|
4 |
|
return $label; |
1493
|
|
|
} |
1494
|
|
|
|
1495
|
|
|
/** |
1496
|
|
|
* Prepare search fields to frontend render with other details (label, field type, searched values) |
1497
|
|
|
* |
1498
|
|
|
* @param array $field |
1499
|
|
|
* @param \GV\Context $context |
1500
|
|
|
* |
1501
|
|
|
* @return array |
1502
|
|
|
*/ |
1503
|
4 |
|
private function get_search_filter_details( $field, $context ) { |
1504
|
|
|
|
1505
|
4 |
|
$gravityview_view = GravityView_View::getInstance(); |
1506
|
|
|
|
1507
|
4 |
|
$form = $gravityview_view->getForm(); |
1508
|
|
|
|
1509
|
|
|
// for advanced field ids (eg, first name / last name ) |
1510
|
4 |
|
$name = 'filter_' . str_replace( '.', '_', $field['field'] ); |
1511
|
|
|
|
1512
|
|
|
// get searched value from $_GET/$_POST (string or array) |
1513
|
4 |
|
$value = $this->rgget_or_rgpost( $name ); |
1514
|
|
|
|
1515
|
|
|
// get form field details |
1516
|
4 |
|
$form_field = gravityview_get_field( $form, $field['field'] ); |
1517
|
|
|
|
1518
|
|
|
$filter = array( |
1519
|
4 |
|
'key' => $field['field'], |
1520
|
4 |
|
'name' => $name, |
1521
|
4 |
|
'label' => self::get_field_label( $field, $form_field ), |
1522
|
4 |
|
'input' => $field['input'], |
1523
|
4 |
|
'value' => $value, |
1524
|
4 |
|
'type' => $form_field['type'], |
1525
|
|
|
); |
1526
|
|
|
|
1527
|
|
|
// collect choices |
1528
|
4 |
|
if ( 'post_category' === $form_field['type'] && ! empty( $form_field['displayAllCategories'] ) && empty( $form_field['choices'] ) ) { |
1529
|
|
|
$filter['choices'] = gravityview_get_terms_choices(); |
1530
|
4 |
|
} elseif ( ! empty( $form_field['choices'] ) ) { |
1531
|
|
|
$filter['choices'] = $form_field['choices']; |
1532
|
|
|
} |
1533
|
|
|
|
1534
|
4 |
|
if ( 'date_range' === $field['input'] && empty( $value ) ) { |
1535
|
|
|
$filter['value'] = array( 'start' => '', 'end' => '' ); |
1536
|
|
|
} |
1537
|
|
|
|
1538
|
4 |
|
if ( ! empty( $filter['choices'] ) ) { |
1539
|
|
|
/** |
1540
|
|
|
* @filter `gravityview/search/sieve_choices` Only output used choices for this field. |
1541
|
|
|
* @param[in,out] bool Yes or no. |
1542
|
|
|
* @param array $field The field configuration. |
1543
|
|
|
* @param \GV\Context The context. |
1544
|
|
|
*/ |
1545
|
|
|
if ( apply_filters( 'gravityview/search/sieve_choices', false, $field, $context ) ) { |
1546
|
|
|
$filter['choices'] = $this->sieve_filter_choices( $filter, $context ); |
1547
|
|
|
} |
1548
|
|
|
} |
1549
|
|
|
|
1550
|
|
|
/** |
1551
|
|
|
* @filter `gravityview/search/filter_details` Filter the output filter details for the Search widget. |
1552
|
|
|
* @param[in,out] array $filter The filter details |
1553
|
|
|
* @param array $field The search field configuration |
1554
|
|
|
* @param \GV\Context The context |
1555
|
|
|
* @since develop |
1556
|
|
|
*/ |
1557
|
4 |
|
$filter = apply_filters( 'gravityview/search/filter_details', $filter, $field, $context ); |
1558
|
|
|
|
1559
|
4 |
|
return $filter; |
1560
|
|
|
|
1561
|
|
|
} |
1562
|
|
|
|
1563
|
|
|
/** |
1564
|
|
|
* Sieve filter choices to only ones that are used. |
1565
|
|
|
* |
1566
|
|
|
* @param array $filter The filter configuration. |
1567
|
|
|
* @param \GV\Context $context The context |
1568
|
|
|
* |
1569
|
|
|
* @since develop |
1570
|
|
|
* @internal |
1571
|
|
|
* |
1572
|
|
|
* @return array The filter choices. |
1573
|
|
|
*/ |
1574
|
|
|
private function sieve_filter_choices( $filter, $context ) { |
1575
|
|
|
if ( empty( $filter['key'] ) || empty( $filter['choices'] ) ) { |
1576
|
|
|
return $filter; // @todo Populate plugins might give us empty choices |
1577
|
|
|
} |
1578
|
|
|
|
1579
|
|
|
if ( ! is_numeric( $filter['key'] ) ) { |
1580
|
|
|
return $filter; |
1581
|
|
|
} |
1582
|
|
|
|
1583
|
|
|
$form_id = $context->view->form->ID; // @todo Support multiple forms (joins) |
|
|
|
|
1584
|
|
|
|
1585
|
|
|
global $wpdb; |
1586
|
|
|
|
1587
|
|
|
$table = GFFormsModel::get_entry_meta_table_name(); |
1588
|
|
|
|
1589
|
|
|
$key_like = $wpdb->esc_like( $filter['key'] ) . '.%'; |
1590
|
|
|
|
1591
|
|
|
switch ( \GV\Utils::get( $filter, 'type' ) ): |
1592
|
|
|
case 'post_category': |
1593
|
|
|
$choices = $wpdb->get_col( $wpdb->prepare( |
1594
|
|
|
"SELECT DISTINCT SUBSTRING_INDEX(meta_value, ':', 1) FROM $table WHERE (meta_key LIKE %s OR meta_key = %d) AND form_id = %d", |
1595
|
|
|
$key_like, $filter['key'], $form_id |
1596
|
|
|
) ); |
1597
|
|
|
break; |
1598
|
|
|
default: |
1599
|
|
|
$choices = $wpdb->get_col( $wpdb->prepare( |
1600
|
|
|
"SELECT DISTINCT meta_value FROM $table WHERE (meta_key LIKE %s OR meta_key = %d) AND form_id = %d", |
1601
|
|
|
$key_like, $filter['key'], $form_id |
1602
|
|
|
) ); |
1603
|
|
|
|
1604
|
|
|
if ( ( $field = gravityview_get_field( $form_id, $filter['key'] ) ) && 'json' === $field->storageType ) { |
1605
|
|
|
$choices = array_map( 'json_decode', $choices ); |
1606
|
|
|
$_choices_array = array(); |
1607
|
|
|
foreach ( $choices as $choice ) { |
1608
|
|
|
if ( is_array( $choice ) ) { |
1609
|
|
|
$_choices_array = array_merge( $_choices_array, $choice ); |
1610
|
|
|
} else { |
1611
|
|
|
$_choices_array []= $choice; |
1612
|
|
|
} |
1613
|
|
|
} |
1614
|
|
|
$choices = array_unique( $_choices_array ); |
1615
|
|
|
} |
1616
|
|
|
|
1617
|
|
|
break; |
1618
|
|
|
endswitch; |
1619
|
|
|
|
1620
|
|
|
$filter_choices = array(); |
1621
|
|
|
foreach ( $filter['choices'] as $choice ) { |
1622
|
|
|
if ( in_array( $choice['text'], $choices, true ) || in_array( $choice['value'], $choices, true ) ) { |
1623
|
|
|
$filter_choices[] = $choice; |
1624
|
|
|
} |
1625
|
|
|
} |
1626
|
|
|
|
1627
|
|
|
return $filter_choices; |
1628
|
|
|
} |
1629
|
|
|
|
1630
|
|
|
/** |
1631
|
|
|
* Calculate the search choices for the users |
1632
|
|
|
* |
1633
|
|
|
* @param \GV\View $view The view |
1634
|
|
|
* @since develop |
1635
|
|
|
* |
1636
|
|
|
* @since 1.8 |
1637
|
|
|
* |
1638
|
|
|
* @return array Array of user choices (value = ID, text = display name) |
1639
|
|
|
*/ |
1640
|
|
|
private static function get_created_by_choices( $view ) { |
1641
|
|
|
|
1642
|
|
|
/** |
1643
|
|
|
* filter gravityview/get_users/search_widget |
1644
|
|
|
* @see \GVCommon::get_users |
1645
|
|
|
*/ |
1646
|
|
|
$users = GVCommon::get_users( 'search_widget', array( 'fields' => array( 'ID', 'display_name' ) ) ); |
1647
|
|
|
|
1648
|
|
|
$choices = array(); |
1649
|
|
|
foreach ( $users as $user ) { |
1650
|
|
|
/** |
1651
|
|
|
* @filter `gravityview/search/created_by/text` Filter the display text in created by search choices |
1652
|
|
|
* @since develop |
1653
|
|
|
* @param string[in,out] The text. Default: $user->display_name |
1654
|
|
|
* @param \WP_User $user The user. |
1655
|
|
|
* @param \GV\View $view The view. |
1656
|
|
|
*/ |
1657
|
|
|
$text = apply_filters( 'gravityview/search/created_by/text', $user->display_name, $user, $view ); |
1658
|
|
|
$choices[] = array( |
1659
|
|
|
'value' => $user->ID, |
1660
|
|
|
'text' => $text, |
1661
|
|
|
); |
1662
|
|
|
} |
1663
|
|
|
|
1664
|
|
|
return $choices; |
1665
|
|
|
} |
1666
|
|
|
|
1667
|
|
|
/** |
1668
|
|
|
* Calculate the search checkbox choices for approval status |
1669
|
|
|
* |
1670
|
|
|
* @since develop |
1671
|
|
|
* |
1672
|
|
|
* @return array Array of approval status choices (value = status, text = display name) |
1673
|
|
|
*/ |
1674
|
|
|
private static function get_is_approved_choices() { |
1675
|
|
|
|
1676
|
|
|
$choices = array(); |
1677
|
|
|
foreach ( GravityView_Entry_Approval_Status::get_all() as $status ) { |
1678
|
|
|
$choices[] = array( |
1679
|
|
|
'value' => $status['value'], |
1680
|
|
|
'text' => $status['label'], |
1681
|
|
|
); |
1682
|
|
|
} |
1683
|
|
|
|
1684
|
|
|
return $choices; |
1685
|
|
|
} |
1686
|
|
|
|
1687
|
|
|
/** |
1688
|
|
|
* Output the Clear Search Results button |
1689
|
|
|
* @since 1.5.4 |
1690
|
|
|
*/ |
1691
|
4 |
|
public static function the_clear_search_button() { |
1692
|
4 |
|
$gravityview_view = GravityView_View::getInstance(); |
1693
|
|
|
|
1694
|
4 |
|
if ( $gravityview_view->search_clear ) { |
|
|
|
|
1695
|
|
|
|
1696
|
|
|
$url = strtok( add_query_arg( array() ), '?' ); |
1697
|
|
|
|
1698
|
|
|
echo gravityview_get_link( $url, esc_html__( 'Clear', 'gravityview' ), 'class=button gv-search-clear' ); |
1699
|
|
|
|
1700
|
|
|
} |
1701
|
4 |
|
} |
1702
|
|
|
|
1703
|
|
|
/** |
1704
|
|
|
* Based on the search method, fetch the value for a specific key |
1705
|
|
|
* |
1706
|
|
|
* @since 1.16.4 |
1707
|
|
|
* |
1708
|
|
|
* @param string $name Name of the request key to fetch the value for |
1709
|
|
|
* |
1710
|
|
|
* @return mixed|string Value of request at $name key. Empty string if empty. |
1711
|
|
|
*/ |
1712
|
4 |
|
private function rgget_or_rgpost( $name ) { |
1713
|
4 |
|
$value = \GV\Utils::_REQUEST( $name ); |
1714
|
|
|
|
1715
|
4 |
|
$value = stripslashes_deep( $value ); |
1716
|
|
|
|
1717
|
4 |
|
$value = gv_map_deep( $value, 'rawurldecode' ); |
1718
|
|
|
|
1719
|
4 |
|
$value = gv_map_deep( $value, '_wp_specialchars' ); |
1720
|
|
|
|
1721
|
4 |
|
return $value; |
1722
|
|
|
} |
1723
|
|
|
|
1724
|
|
|
|
1725
|
|
|
/** |
1726
|
|
|
* Require the datepicker script for the frontend GV script |
1727
|
|
|
* @param array $js_dependencies Array of existing required scripts for the fe-views.js script |
1728
|
|
|
* @return array Array required scripts, with `jquery-ui-datepicker` added |
1729
|
|
|
*/ |
1730
|
|
|
public function add_datepicker_js_dependency( $js_dependencies ) { |
1731
|
|
|
|
1732
|
|
|
$js_dependencies[] = 'jquery-ui-datepicker'; |
1733
|
|
|
|
1734
|
|
|
return $js_dependencies; |
1735
|
|
|
} |
1736
|
|
|
|
1737
|
|
|
/** |
1738
|
|
|
* Modify the array passed to wp_localize_script() |
1739
|
|
|
* |
1740
|
|
|
* @param array $js_localization The data padded to the Javascript file |
|
|
|
|
1741
|
|
|
* @param array $view_data View data array with View settings |
1742
|
|
|
* |
1743
|
|
|
* @return array |
1744
|
|
|
*/ |
1745
|
|
|
public function add_datepicker_localization( $localizations = array(), $view_data = array() ) { |
1746
|
|
|
global $wp_locale; |
1747
|
|
|
|
1748
|
|
|
/** |
1749
|
|
|
* @filter `gravityview_datepicker_settings` Modify the datepicker settings |
1750
|
|
|
* @see http://api.jqueryui.com/datepicker/ Learn what settings are available |
1751
|
|
|
* @see http://www.renegadetechconsulting.com/tutorials/jquery-datepicker-and-wordpress-i18n Thanks for the helpful information on $wp_locale |
1752
|
|
|
* @param array $js_localization The data padded to the Javascript file |
1753
|
|
|
* @param array $view_data View data array with View settings |
1754
|
|
|
*/ |
1755
|
|
|
$datepicker_settings = apply_filters( 'gravityview_datepicker_settings', array( |
1756
|
|
|
'yearRange' => '-5:+5', |
1757
|
|
|
'changeMonth' => true, |
1758
|
|
|
'changeYear' => true, |
1759
|
|
|
'closeText' => esc_attr_x( 'Close', 'Close calendar', 'gravityview' ), |
1760
|
|
|
'prevText' => esc_attr_x( 'Prev', 'Previous month in calendar', 'gravityview' ), |
1761
|
|
|
'nextText' => esc_attr_x( 'Next', 'Next month in calendar', 'gravityview' ), |
1762
|
|
|
'currentText' => esc_attr_x( 'Today', 'Today in calendar', 'gravityview' ), |
1763
|
|
|
'weekHeader' => esc_attr_x( 'Week', 'Week in calendar', 'gravityview' ), |
1764
|
|
|
'monthStatus' => __( 'Show a different month', 'gravityview' ), |
1765
|
|
|
'monthNames' => array_values( $wp_locale->month ), |
1766
|
|
|
'monthNamesShort' => array_values( $wp_locale->month_abbrev ), |
1767
|
|
|
'dayNames' => array_values( $wp_locale->weekday ), |
1768
|
|
|
'dayNamesShort' => array_values( $wp_locale->weekday_abbrev ), |
1769
|
|
|
'dayNamesMin' => array_values( $wp_locale->weekday_initial ), |
1770
|
|
|
// get the start of week from WP general setting |
1771
|
|
|
'firstDay' => get_option( 'start_of_week' ), |
1772
|
|
|
// is Right to left language? default is false |
1773
|
|
|
'isRTL' => is_rtl(), |
1774
|
|
|
), $view_data ); |
1775
|
|
|
|
1776
|
|
|
$localizations['datepicker'] = $datepicker_settings; |
1777
|
|
|
|
1778
|
|
|
return $localizations; |
1779
|
|
|
|
1780
|
|
|
} |
1781
|
|
|
|
1782
|
|
|
/** |
1783
|
|
|
* Register search widget scripts, including Flexibility |
1784
|
|
|
* |
1785
|
|
|
* @see https://github.com/10up/flexibility |
1786
|
|
|
* |
1787
|
|
|
* @since 1.17 |
1788
|
|
|
* |
1789
|
|
|
* @return void |
1790
|
|
|
*/ |
1791
|
|
|
public function register_scripts() { |
1792
|
|
|
wp_register_script( 'gv-flexibility', plugins_url( 'assets/lib/flexibility/flexibility.js', GRAVITYVIEW_FILE ), array(), \GV\Plugin::$version, true ); |
1793
|
|
|
} |
1794
|
|
|
|
1795
|
|
|
/** |
1796
|
|
|
* If the current visitor is running IE 8 or 9, enqueue Flexibility |
1797
|
|
|
* |
1798
|
|
|
* @since 1.17 |
1799
|
|
|
* |
1800
|
|
|
* @return void |
1801
|
|
|
*/ |
1802
|
4 |
|
private function maybe_enqueue_flexibility() { |
1803
|
4 |
|
if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && preg_match( '/MSIE [8-9]/', $_SERVER['HTTP_USER_AGENT'] ) ) { |
1804
|
|
|
wp_enqueue_script( 'gv-flexibility' ); |
1805
|
|
|
} |
1806
|
4 |
|
} |
1807
|
|
|
|
1808
|
|
|
/** |
1809
|
|
|
* Enqueue the datepicker script |
1810
|
|
|
* |
1811
|
|
|
* It sets the $gravityview->datepicker_class parameter |
1812
|
|
|
* |
1813
|
|
|
* @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. |
1814
|
|
|
* @return void |
1815
|
|
|
*/ |
1816
|
|
|
public function enqueue_datepicker() { |
1817
|
|
|
$gravityview_view = GravityView_View::getInstance(); |
1818
|
|
|
|
1819
|
|
|
wp_enqueue_script( 'jquery-ui-datepicker' ); |
1820
|
|
|
|
1821
|
|
|
add_filter( 'gravityview_js_dependencies', array( $this, 'add_datepicker_js_dependency' ) ); |
1822
|
|
|
add_filter( 'gravityview_js_localization', array( $this, 'add_datepicker_localization' ), 10, 2 ); |
1823
|
|
|
|
1824
|
|
|
$scheme = is_ssl() ? 'https://' : 'http://'; |
1825
|
|
|
wp_enqueue_style( 'jquery-ui-datepicker', $scheme.'ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/smoothness/jquery-ui.css' ); |
1826
|
|
|
|
1827
|
|
|
/** |
1828
|
|
|
* @filter `gravityview_search_datepicker_class` |
1829
|
|
|
* 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. |
1830
|
|
|
* @param string $css_class CSS class to use. Default: `gv-datepicker datepicker mdy` \n |
1831
|
|
|
* Options are: |
1832
|
|
|
* - `mdy` (mm/dd/yyyy) |
1833
|
|
|
* - `dmy` (dd/mm/yyyy) |
1834
|
|
|
* - `dmy_dash` (dd-mm-yyyy) |
1835
|
|
|
* - `dmy_dot` (dd.mm.yyyy) |
1836
|
|
|
* - `ymd_slash` (yyyy/mm/dd) |
1837
|
|
|
* - `ymd_dash` (yyyy-mm-dd) |
1838
|
|
|
* - `ymd_dot` (yyyy.mm.dd) |
1839
|
|
|
*/ |
1840
|
|
|
$datepicker_class = apply_filters( 'gravityview_search_datepicker_class', "gv-datepicker datepicker " . $this->get_datepicker_format() ); |
1841
|
|
|
|
1842
|
|
|
$gravityview_view->datepicker_class = $datepicker_class; |
|
|
|
|
1843
|
|
|
} |
1844
|
|
|
|
1845
|
|
|
/** |
1846
|
|
|
* Retrieve the datepicker format. |
1847
|
|
|
* |
1848
|
|
|
* @param bool $date_format Whether to return the PHP date format or the datpicker class name. Default: false. |
1849
|
|
|
* |
1850
|
|
|
* @see https://docs.gravityview.co/article/115-changing-the-format-of-the-search-widgets-date-picker |
1851
|
|
|
* |
1852
|
|
|
* @return string The datepicker format placeholder, or the PHP date format. |
1853
|
|
|
*/ |
1854
|
19 |
|
private function get_datepicker_format( $date_format = false ) { |
1855
|
|
|
|
1856
|
19 |
|
$default_format = 'mdy'; |
1857
|
|
|
|
1858
|
|
|
/** |
1859
|
|
|
* @filter `gravityview/widgets/search/datepicker/format` |
1860
|
|
|
* @since 2.1.1 |
1861
|
|
|
* @param string $format Default: mdy |
1862
|
|
|
* Options are: |
1863
|
|
|
* - `mdy` (mm/dd/yyyy) |
1864
|
|
|
* - `dmy` (dd/mm/yyyy) |
1865
|
|
|
* - `dmy_dash` (dd-mm-yyyy) |
1866
|
|
|
* - `dmy_dot` (dd.mm.yyyy) |
1867
|
|
|
* - `ymd_slash` (yyyy/mm/dd) |
1868
|
|
|
* - `ymd_dash` (yyyy-mm-dd) |
1869
|
|
|
* - `ymd_dot` (yyyy.mm.dd) |
1870
|
|
|
*/ |
1871
|
19 |
|
$format = apply_filters( 'gravityview/widgets/search/datepicker/format', $default_format ); |
1872
|
|
|
|
1873
|
|
|
$gf_date_formats = array( |
1874
|
19 |
|
'mdy' => 'm/d/Y', |
1875
|
|
|
|
1876
|
|
|
'dmy_dash' => 'd-m-Y', |
1877
|
|
|
'dmy_dot' => 'd.m.Y', |
1878
|
|
|
'dmy' => 'd/m/Y', |
1879
|
|
|
|
1880
|
|
|
'ymd_slash' => 'Y/m/d', |
1881
|
|
|
'ymd_dash' => 'Y-m-d', |
1882
|
|
|
'ymd_dot' => 'Y.m.d', |
1883
|
|
|
); |
1884
|
|
|
|
1885
|
19 |
|
if ( ! $date_format ) { |
1886
|
|
|
// If the format key isn't valid, return default format key |
1887
|
|
|
return isset( $gf_date_formats[ $format ] ) ? $format : $default_format; |
1888
|
|
|
} |
1889
|
|
|
|
1890
|
|
|
// If the format key isn't valid, return default format value |
1891
|
19 |
|
return \GV\Utils::get( $gf_date_formats, $format, $gf_date_formats[ $default_format ] ); |
1892
|
|
|
} |
1893
|
|
|
|
1894
|
|
|
/** |
1895
|
|
|
* If previewing a View or page with embedded Views, make the search work properly by adding hidden fields with query vars |
1896
|
|
|
* |
1897
|
|
|
* @since 2.2.1 |
1898
|
|
|
* |
1899
|
|
|
* @return void |
1900
|
|
|
*/ |
1901
|
4 |
|
public function add_preview_inputs() { |
1902
|
4 |
|
global $wp; |
1903
|
|
|
|
1904
|
4 |
|
if ( ! is_preview() || ! current_user_can( 'publish_gravityviews') ) { |
1905
|
4 |
|
return; |
1906
|
|
|
} |
1907
|
|
|
|
1908
|
|
|
// Outputs `preview` and `post_id` variables |
1909
|
|
|
foreach ( $wp->query_vars as $key => $value ) { |
1910
|
|
|
printf( '<input type="hidden" name="%s" value="%s" />', esc_attr( $key ), esc_attr( $value ) ); |
1911
|
|
|
} |
1912
|
|
|
|
1913
|
|
|
} |
1914
|
|
|
|
1915
|
|
|
/** |
1916
|
|
|
* Get an operator URL override. |
1917
|
|
|
* |
1918
|
|
|
* @param array $get Where to look for the operator. |
1919
|
|
|
* @param string $key The filter key to look for. |
1920
|
|
|
* @param array $allowed The allowed operators (whitelist). |
1921
|
|
|
* @param string $default The default operator. |
1922
|
|
|
* |
1923
|
|
|
* @return string The operator. |
1924
|
|
|
*/ |
1925
|
19 |
|
private function get_operator( $get, $key, $allowed, $default ) { |
1926
|
19 |
|
$operator = \GV\Utils::get( $get, "$key|op", $default ); |
1927
|
|
|
|
1928
|
|
|
/** |
1929
|
|
|
* @filter `gravityview/search/operator_whitelist` An array of allowed operators for a field. |
1930
|
|
|
* @param[in,out] string[] A whitelist of allowed operators. |
1931
|
|
|
* @param string The filter name. |
1932
|
|
|
*/ |
1933
|
19 |
|
$allowed = apply_filters( 'gravityview/search/operator_whitelist', $allowed, $key ); |
1934
|
|
|
|
1935
|
19 |
|
if ( ! in_array( $operator, $allowed, true ) ) { |
1936
|
1 |
|
$operator = $default; |
1937
|
|
|
} |
1938
|
|
|
|
1939
|
19 |
|
return $operator; |
1940
|
|
|
} |
1941
|
|
|
|
1942
|
|
|
|
1943
|
|
|
} // end class |
1944
|
|
|
|
1945
|
|
|
new GravityView_Widget_Search; |
1946
|
|
|
|
1947
|
|
|
if ( ! gravityview()->plugin->supports( \GV\Plugin::FEATURE_GFQUERY ) ) { |
1948
|
|
|
return; |
1949
|
|
|
} |
1950
|
|
|
|
1951
|
|
|
/** |
1952
|
|
|
* A GF_Query condition that allows user data searches. |
1953
|
|
|
*/ |
1954
|
|
|
class GravityView_Widget_Search_Author_GF_Query_Condition extends \GF_Query_Condition { |
1955
|
1 |
|
public function __construct( $filter, $view ) { |
1956
|
1 |
|
$this->value = $filter['value']; |
1957
|
1 |
|
$this->view = $view; |
1958
|
1 |
|
} |
1959
|
|
|
|
1960
|
1 |
|
public function sql( $query ) { |
1961
|
1 |
|
global $wpdb; |
1962
|
|
|
|
1963
|
|
|
$user_meta_fields = array( |
1964
|
1 |
|
'nickname', 'first_name', 'last_name', |
1965
|
|
|
); |
1966
|
|
|
|
1967
|
|
|
/** |
1968
|
|
|
* @filter `gravityview/widgets/search/created_by/user_meta_fields` Filter the user meta fields to search. |
1969
|
|
|
* @param[in,out] array The user meta fields. |
1970
|
|
|
* @param \GV\View $view The view. |
1971
|
|
|
*/ |
1972
|
1 |
|
$user_meta_fields = apply_filters( 'gravityview/widgets/search/created_by/user_meta_fields', $user_meta_fields, $this->view ); |
1973
|
|
|
|
1974
|
|
|
$user_fields = array( |
1975
|
1 |
|
'user_nicename', 'user_login', 'display_name', 'user_email', |
1976
|
|
|
); |
1977
|
|
|
|
1978
|
|
|
/** |
1979
|
|
|
* @filter `gravityview/widgets/search/created_by/user_fields` Filter the user fields to search. |
1980
|
|
|
* @param[in,out] array The user fields. |
1981
|
|
|
* @param \GV\View $view The view. |
1982
|
|
|
*/ |
1983
|
1 |
|
$user_fields = apply_filters( 'gravityview/widgets/search/created_by/user_fields', $user_fields, $this->view ); |
1984
|
|
|
|
1985
|
1 |
|
$conditions = array(); |
1986
|
|
|
|
1987
|
1 |
|
foreach ( $user_fields as $user_field ) { |
1988
|
1 |
|
$conditions[] = $wpdb->prepare( "`u`.`$user_field` LIKE %s", '%' . $wpdb->esc_like( $this->value ) . '%' ); |
1989
|
|
|
} |
1990
|
|
|
|
1991
|
1 |
|
foreach ( $user_meta_fields as $meta_field ) { |
1992
|
1 |
|
$conditions[] = $wpdb->prepare( "(`um`.`meta_key` = %s AND `um`.`meta_value` LIKE %s)", $meta_field, '%' . $wpdb->esc_like( $this->value ) . '%' ); |
1993
|
|
|
} |
1994
|
|
|
|
1995
|
1 |
|
$conditions = '(' . implode( ' OR ', $conditions ) . ')'; |
1996
|
|
|
|
1997
|
1 |
|
$alias = $query->_alias( null ); |
1998
|
|
|
|
1999
|
1 |
|
return "(EXISTS (SELECT 1 FROM $wpdb->users u LEFT JOIN $wpdb->usermeta um ON u.ID = um.user_id WHERE (u.ID = `$alias`.`created_by` AND $conditions)))"; |
2000
|
|
|
} |
2001
|
|
|
} |
2002
|
|
|
|
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.
In this case you can add the
@ignore
PhpDoc annotation to the duplicate definition and it will be ignored.