GravityView  2.5
The best, easiest way to display Gravity Forms entries on your website.
class-search-widget.php
Go to the documentation of this file.
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 
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  public function __construct() {
31 
32  $this->widget_id = 'search_bar';
33  $this->widget_description = esc_html__( 'Search form for searching entries.', 'gravityview' );
34 
35  self::$instance = &$this;
36 
37  self::$file = plugin_dir_path( __FILE__ );
38 
39  $default_values = array( 'header' => 0, 'footer' => 0 );
40 
41  $settings = array(
42  'search_layout' => array(
43  'type' => 'radio',
44  'full_width' => true,
45  'label' => esc_html__( 'Search Layout', 'gravityview' ),
46  'value' => 'horizontal',
47  'options' => array(
48  'horizontal' => esc_html__( 'Horizontal', 'gravityview' ),
49  'vertical' => esc_html__( 'Vertical', 'gravityview' ),
50  ),
51  ),
52  'search_clear' => array(
53  '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  'search_mode' => array(
64  'type' => 'radio',
65  'full_width' => true,
66  'label' => esc_html__( 'Search Mode', 'gravityview' ),
67  'desc' => __('Should search results match all search fields, or any?', 'gravityview'),
68  'value' => 'any',
69  'class' => 'hide-if-js',
70  'options' => array(
71  'any' => esc_html__( 'Match Any Fields', 'gravityview' ),
72  'all' => esc_html__( 'Match All Fields', 'gravityview' ),
73  ),
74  ),
75  );
76 
77  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  parent::__construct( esc_html__( 'Search Bar', 'gravityview' ), null, $default_values, $settings );
96 
97  // calculate the search method (POST / GET)
98  $this->set_search_method();
99  }
100 
101  /**
102  * @return GravityView_Widget_Search
103  */
104  public static function getInstance() {
105  if ( empty( self::$instance ) ) {
106  self::$instance = new GravityView_Widget_Search;
107  }
108  return self::$instance;
109  }
110 
111  /**
112  * Sets the search method to GET (default) or POST
113  * @since 1.16.4
114  */
115  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  $method = apply_filters( 'gravityview/search/method', $this->search_method );
123 
124  $method = strtolower( $method );
125 
126  $this->search_method = in_array( $method, array( 'get', 'post' ) ) ? $method : 'get';
127  }
128 
129  /**
130  * Returns the search method
131  * @since 1.16.4
132  * @return string
133  */
134  public function get_search_method() {
135  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  public function add_no_permalink_fields( $search_fields, $object, $widget_args = array() ) {
426  /** @global WP_Rewrite $wp_rewrite */
427  global $wp_rewrite;
428 
429  // Support default permalink structure
430  if ( false === $wp_rewrite->using_permalinks() ) {
431 
432  // By default, use current post.
433  $post_id = 0;
434 
435  // We're in the WordPress Widget context, and an overriding post ID has been set.
436  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  else if ( ! empty( $widget_args['view_id'] ) ) {
441  $post_id = absint( $widget_args['view_id'] );
442  }
443 
444  $args = gravityview_get_permalink_query_args( $post_id );
445 
446  // Add hidden fields to the search form
447  foreach ( $args as $key => $value ) {
448  $search_fields[] = array(
449  'name' => $key,
450  'input' => 'hidden',
451  'value' => $value,
452  );
453  }
454  }
455 
456  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  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  $searchable_fields = array();
479 
480  if ( ! $view ) {
481  return $searchable_fields;
482  }
483 
484  /**
485  * Include the sidebar Widgets.
486  */
487  $widgets = (array) get_option( 'widget_gravityview_search', array() );
488 
489  foreach ( $widgets as $widget ) {
490  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  foreach ( $view->widgets->by_id( $this->get_widget_id() )->all() as $widget ) {
503  if( $_fields = json_decode( $widget->configuration->get( 'search_fields' ), true ) ) {
504  foreach ( $_fields as $field ) {
505  if ( empty( $field['form_id'] ) ) {
506  $field['form_id'] = $view->form ? $view->form->ID : 0;
507  }
508  $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  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  public function filter_entries( $search_criteria, $form_id = null, $args = array(), $force_search_criteria = false ) {
537  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  add_action( 'gravityview/view/query', array( $this, 'gf_query_filter' ), 10, 3 );
543  return $search_criteria; // Return the original criteria, GF_Query modification kicks in later
544  }
545 
546  if( 'post' === $this->search_method ) {
547  $get = $_POST;
548  } else {
549  $get = $_GET;
550  }
551 
552  $view = \GV\View::by_id( \GV\Utils::get( $args, 'id' ) );
553 
554  gravityview()->log->debug( 'Requested $_{method}: ', array( 'method' => $this->search_method, 'data' => $get ) );
555 
556  if ( empty( $get ) || ! is_array( $get ) ) {
557  return $search_criteria;
558  }
559 
560  $get = stripslashes_deep( $get );
561 
562  $get = gv_map_deep( $get, 'rawurldecode' );
563 
564  // Make sure array key is set up
565  $search_criteria['field_filters'] = \GV\Utils::get( $search_criteria, 'field_filters', array() );
566 
567  $searchable_fields = $this->get_view_searchable_fields( $view );
568  $searchable_field_objects = $this->get_view_searchable_fields( $view, true );
569 
570  // add free search
571  if ( isset( $get['gv_search'] ) && '' !== $get['gv_search'] && in_array( 'search_all', $searchable_fields ) ) {
572 
573  $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  $split_words = apply_filters( 'gravityview/search-all-split-words', true );
581 
582  if ( $split_words ) {
583 
584  // Search for a piece
585  $words = explode( ' ', $search_all_value );
586 
587  $words = array_filter( $words );
588 
589  } else {
590 
591  // Replace multiple spaces with one space
592  $search_all_value = preg_replace( '/\s+/ism', ' ', $search_all_value );
593 
594  $words = array( $search_all_value );
595  }
596 
597  foreach ( $words as $word ) {
598  $search_criteria['field_filters'][] = array(
599  'key' => null, // The field ID to search
600  'value' => $word, // The value to search
601  'operator' => 'contains', // What to search in. Options: `is` or `contains`
602  );
603  }
604  }
605 
606  // start date & end date
607  if ( in_array( 'entry_date', $searchable_fields ) ) {
608  /**
609  * Get and normalize the dates according to the input format.
610  */
611  if ( $curr_start = ! empty( $get['gv_start'] ) ? $get['gv_start'] : '' ) {
612  if( $curr_start_date = date_create_from_format( $this->get_datepicker_format( true ), $curr_start ) ) {
613  $curr_start = $curr_start_date->format( 'Y-m-d' );
614  }
615  }
616 
617  if ( $curr_end = ! empty( $get['gv_start'] ) ? ( ! empty( $get['gv_end'] ) ? $get['gv_end'] : '' ) : '' ) {
618  if( $curr_end_date = date_create_from_format( $this->get_datepicker_format( true ), $curr_end ) ) {
619  $curr_end = $curr_end_date->format( 'Y-m-d' );
620  }
621  }
622 
623  if ( $view ) {
624  /**
625  * Override start and end dates if View is limited to some already.
626  */
627  if ( $start_date = $view->settings->get( 'start_date' ) ) {
628  if ( $start_timestamp = strtotime( $curr_start ) ) {
629  $curr_start = $start_timestamp < strtotime( $start_date ) ? $start_date : $curr_start;
630  }
631  }
632  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  $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  if ( ! empty( $curr_start ) ) {
652  $curr_start = date( 'Y-m-d H:i:s', strtotime( $curr_start ) );
653  $search_criteria['start_date'] = $adjust_tz ? get_gmt_from_date( $curr_start ) : $curr_start;
654  }
655 
656  if ( ! empty( $curr_end ) ) {
657  // Fast-forward 24 hour on the end time
658  $curr_end = date( 'Y-m-d H:i:s', strtotime( $curr_end ) + DAY_IN_SECONDS );
659  $search_criteria['end_date'] = $adjust_tz ? get_gmt_from_date( $curr_end ) : $curr_end;
660  if ( strpos( $search_criteria['end_date'], '00:00:00' ) ) { // See https://github.com/gravityview/GravityView/issues/1056
661  $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  if ( ! empty( $get[ 'gv_id' ] ) && in_array( 'entry_id', $searchable_fields ) ) {
668  $search_criteria['field_filters'][] = array(
669  'key' => 'id',
670  'value' => absint( $get[ 'gv_id' ] ),
671  'operator' => $this->get_operator( $get, 'gv_id', array( '=' ), '=' ),
672  );
673  }
674 
675  // search for a specific Created_by ID
676  if ( ! empty( $get[ 'gv_by' ] ) && in_array( 'created_by', $searchable_fields ) ) {
677  $search_criteria['field_filters'][] = array(
678  'key' => 'created_by',
679  'value' => $get['gv_by'],
680  'operator' => $this->get_operator( $get, 'gv_by', array( '=' ), '=' ),
681  );
682  }
683 
684  // Get search mode passed in URL
685  $mode = isset( $get['mode'] ) && in_array( $get['mode'], array( 'any', 'all' ) ) ? $get['mode'] : 'any';
686 
687  // get the other search filters
688  foreach ( $get as $key => $value ) {
689 
690  if ( 0 !== strpos( $key, 'filter_' ) || gv_empty( $value, false, false ) || ( is_array( $value ) && count( $value ) === 1 && gv_empty( $value[0], false, false ) ) ) {
691  continue; // Not a filter, or empty
692  }
693 
694  if ( strpos( $key, '|op' ) !== false ) {
695  continue; // This is an operator
696  }
697 
698  $filter_key = $this->convert_request_key_to_filter_key( $key );
699 
700  if ( ! $filter = $this->prepare_field_filter( $filter_key, $value, $view, $searchable_field_objects, $get ) ) {
701  continue;
702  }
703 
704  if ( ! isset( $filter['operator'] ) ) {
705  $filter['operator'] = $this->get_operator( $get, $key, array( 'contains' ), 'contains' );
706  }
707 
708  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  } elseif( !empty( $filter ) ) {
716  $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  $search_criteria['field_filters']['mode'] = apply_filters( 'gravityview/search/mode', $mode );
726 
727  gravityview()->log->debug( 'Returned Search Criteria: ', array( 'data' => $search_criteria ) );
728 
729  unset( $get );
730 
731  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  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  $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  remove_filter( 'gravityview_fe_search_criteria', array( $this, 'filter_entries' ), 10, 3 );
754  $search_criteria = apply_filters( 'gravityview_fe_search_criteria', $search_criteria, $view->form->ID, $view->settings->as_atts() );
755  add_filter( 'gravityview_fe_search_criteria', array( $this, 'filter_entries' ), 10, 3 );
756 
757  $query_class = $view->get_query_class();
758 
759  if ( empty( $search_criteria['field_filters'] ) ) {
760  return;
761  }
762 
763  $widgets = $view->widgets->by_id( $this->widget_id );
764  if ( $widgets->count() ) {
765  $widgets = $widgets->all();
766  $widget = $widgets[0];
767 
768  $search_fields = json_decode( $widget->configuration->get( 'search_fields' ), true );
769 
770  foreach ( (array) $search_fields as $search_field ) {
771  if ( 'created_by' === $search_field['field'] && 'input_text' === $search_field['input'] ) {
772  $created_by_text_mode = true;
773  }
774  }
775  }
776 
777  $extra_conditions = array();
778  $mode = 'any';
779 
780  foreach ( $search_criteria['field_filters'] as &$filter ) {
781  if ( ! is_array( $filter ) ) {
782  if ( in_array( strtolower( $filter ), array( 'any', 'all' ) ) ) {
783  $mode = $filter;
784  }
785  continue;
786  }
787 
788  // Construct a manual query for unapproved statuses
789  if ( 'is_approved' === $filter['key'] && in_array( \GravityView_Entry_Approval_Status::UNAPPROVED, (array) $filter['value'] ) ) {
790  $_tmp_query = new $query_class( $view->form->ID, array(
791  'field_filters' => array(
792  array(
793  'operator' => 'in',
794  'key' => 'is_approved',
795  'value' => (array) $filter['value'],
796  ),
797  array(
798  'operator' => 'is',
799  'key' => 'is_approved',
800  'value' => '',
801  ),
802  'mode' => 'any'
803  ),
804  ) );
805  $_tmp_query_parts = $_tmp_query->_introspect();
806 
807  $extra_conditions[] = $_tmp_query_parts['where'];
808 
809  $filter = false;
810  continue;
811  }
812 
813  // Construct manual query for text mode creator search
814  if ( 'created_by' === $filter['key'] && ! empty( $created_by_text_mode ) ) {
815  $extra_conditions[] = new GravityView_Widget_Search_Author_GF_Query_Condition( $filter, $view );
816  $filter = false;
817  continue;
818  }
819 
820  // By default, we want searches to be wildcard for each field.
821  $filter['operator'] = empty( $filter['operator'] ) ? 'contains' : $filter['operator'];
822 
823  // For multichoice, let's have an in (OR) search.
824  if ( is_array( $filter['value'] ) ) {
825  $filter['operator'] = 'in'; // @todo what about in contains (OR LIKE chains)?
826  }
827 
828  // Default form with joins functionality
829  if ( empty( $filter['form_id'] ) ) {
830  $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  $filter['operator'] = apply_filters( 'gravityview_search_operator', $filter['operator'], $filter, $view );
841  }
842 
843  if ( ! empty( $search_criteria['start_date'] ) || ! empty( $search_criteria['end_date'] ) ) {
844  $date_criteria = array();
845 
846  if ( isset( $search_criteria['start_date'] ) ) {
847  $date_criteria['start_date'] = $search_criteria['start_date'];
848  }
849 
850  if ( isset( $search_criteria['end_date'] ) ) {
851  $date_criteria['end_date'] = $search_criteria['end_date'];
852  }
853 
854  $_tmp_query = new $query_class( $view->form->ID, $date_criteria );
855  $_tmp_query_parts = $_tmp_query->_introspect();
856  $extra_conditions[] = $_tmp_query_parts['where'];
857  }
858 
859  $search_conditions = array();
860 
861  if ( $filters = array_filter( $search_criteria['field_filters'] ) ) {
862 
863  foreach ( $filters as $filter ) {
864  if ( ! is_array( $filter ) ) {
865  continue;
866  }
867 
868  /**
869  * Parse the filter criteria to generate the needed
870  * WHERE condition. This is a trick to not write our own generation
871  * code by reusing what's inside GF_Query already as they
872  * take care of many small things like forcing numeric, etc.
873  */
874  $_tmp_query = new $query_class( $filter['form_id'], array( 'mode' => 'any', 'field_filters' => array( $filter ) ) );
875  $_tmp_query_parts = $_tmp_query->_introspect();
876  $search_condition = $_tmp_query_parts['where'];
877 
878  if ( empty( $filter['key'] ) && $search_condition->expressions ) {
879  $search_conditions[] = $search_condition;
880  } else {
881  $left = $search_condition->left;
882  $alias = $query->_alias( $left->field_id, $left->source, $left->is_entry_column() ? 't' : 'm' );
883 
884  if ( $view->joins && $left->field_id == GF_Query_Column::META ) {
885  foreach ( $view->joins as $_join ) {
886  $on = $_join->join_on;
887  $join = $_join->join;
888 
889  // Join
890  $search_conditions[] = new GF_Query_Condition(
891  new GF_Query_Column( GF_Query_Column::META, $join->ID, $query->_alias( GF_Query_Column::META, $join->ID, 'm' ) ),
892  $search_condition->operator,
893  $search_condition->right
894  );
895 
896  // On
897  $search_conditions[] = new GF_Query_Condition(
898  new GF_Query_Column( GF_Query_Column::META, $on->ID, $query->_alias( GF_Query_Column::META, $on->ID, 'm' ) ),
899  $search_condition->operator,
900  $search_condition->right
901  );
902  }
903  } else {
904  $search_conditions[] = new GF_Query_Condition(
905  new GF_Query_Column( $left->field_id, $left->source, $alias ),
906  $search_condition->operator,
907  $search_condition->right
908  );
909  }
910  }
911  }
912 
913  if ( $search_conditions ) {
914  $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  $query_parts = $query->_introspect();
922 
923  /**
924  * Combine the parts as a new WHERE clause.
925  */
926  $where = call_user_func_array( '\GF_Query_Condition::_and', array_merge( array( $query_parts['where'] ), $search_conditions, $extra_conditions ) );
927  $query->where( $where );
928  }
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  private function convert_request_key_to_filter_key( $key ) {
945 
946  $field_id = str_replace( 'filter_', '', $key );
947 
948  // calculates field_id, removing 'filter_' and for '_' for advanced fields ( like name or checkbox )
949  if ( preg_match('/^[0-9_]+$/ism', $field_id ) ) {
950  $field_id = str_replace( '_', '.', $field_id );
951  }
952 
953  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  public function prepare_field_filter( $filter_key, $value, $view, $searchable_fields, $get = array() ) {
975  $key = $filter_key;
976  $filter_key = explode( ':', $filter_key ); // field_id, form_id
977 
978  $form = null;
979 
980  if ( count( $filter_key ) > 1 ) {
981  // form is specified
982  list( $field_id, $form_id ) = $filter_key;
983 
984  if ( $forms = \GV\View::get_joined_forms( $view->ID ) ) {
985  if ( ! $form = \GV\GF_Form::by_id( $form_id ) ) {
986  return false;
987  }
988  }
989 
990  // form is allowed
991  $found = false;
992  foreach ( $forms as $form ) {
993  if ( $form->ID == $form_id ) {
994  $found = true;
995  break;
996  }
997  }
998 
999  if ( ! $found ) {
1000  return false;
1001  }
1002 
1003  // form is in searchable fields
1004  $found = false;
1005  foreach ( $searchable_fields as $field ) {
1006  if ( $field_id == $field['field'] && $form->ID == $field['form_id'] ) {
1007  $found = true;
1008  break;
1009  }
1010  }
1011 
1012  if ( ! $found ) {
1013  return false;
1014  }
1015  } else {
1016  $field_id = reset( $filter_key );
1017  $searchable_fields = wp_list_pluck( $searchable_fields, 'field' );
1018  if ( ! in_array( 'search_all', $searchable_fields ) && ! in_array( $field_id, $searchable_fields ) ) {
1019  return false;
1020  }
1021  }
1022 
1023  if ( ! $form ) {
1024  // fallback
1025  $form = $view->form;
1026  }
1027 
1028  // get form field array
1029  if ( ! $form_field = is_numeric( $field_id ) ? \GV\GF_Field::by_id( $form, $field_id ) : \GV\Internal_Field::by_id( $field_id ) ) {
1030  return false;
1031  }
1032 
1033  // default filter array
1034  $filter = array(
1035  'key' => $field_id,
1036  'value' => $value,
1037  'form_id' => $form->ID,
1038  );
1039 
1040  switch ( $form_field->type ) {
1041 
1042  case 'select':
1043  case 'radio':
1044  $filter['operator'] = $this->get_operator( $get, $key, array( 'is' ), 'is' );
1045  break;
1046 
1047  case 'post_category':
1048 
1049  if ( ! is_array( $value ) ) {
1050  $value = array( $value );
1051  }
1052 
1053  // Reset filter variable
1054  $filter = array();
1055 
1056  foreach ( $value as $val ) {
1057  $cat = get_term( $val, 'category' );
1058  $filter[] = array(
1059  'key' => $field_id,
1060  'value' => esc_attr( $cat->name ) . ':' . $val,
1061  'operator' => $this->get_operator( $get, $key, array( 'is' ), 'is' ),
1062  );
1063  }
1064 
1065  break;
1066 
1067  case 'multiselect':
1068 
1069  if ( ! is_array( $value ) ) {
1070  break;
1071  }
1072 
1073  // Reset filter variable
1074  $filter = array();
1075 
1076  foreach ( $value as $val ) {
1077  $filter[] = array( 'key' => $field_id, 'value' => $val );
1078  }
1079 
1080  break;
1081 
1082  case 'checkbox':
1083  // convert checkbox on/off into the correct search filter
1084  if ( false !== strpos( $field_id, '.' ) && ! empty( $form_field->inputs ) && ! empty( $form_field->choices ) ) {
1085  foreach ( $form_field->inputs as $k => $input ) {
1086  if ( $input['id'] == $field_id ) {
1087  $filter['value'] = $form_field->choices[ $k ]['value'];
1088  $filter['operator'] = $this->get_operator( $get, $key, array( 'is' ), 'is' );
1089  break;
1090  }
1091  }
1092  } elseif ( is_array( $value ) ) {
1093 
1094  // Reset filter variable
1095  $filter = array();
1096 
1097  foreach ( $value as $val ) {
1098  $filter[] = array(
1099  'key' => $field_id,
1100  'value' => $val,
1101  'operator' => $this->get_operator( $get, $key, array( 'is' ), 'is' ),
1102  );
1103  }
1104  }
1105 
1106  break;
1107 
1108  case 'name':
1109  case 'address':
1110 
1111  if ( false === strpos( $field_id, '.' ) ) {
1112 
1113  $words = explode( ' ', $value );
1114 
1115  $filters = array();
1116  foreach ( $words as $word ) {
1117  if ( ! empty( $word ) && strlen( $word ) > 1 ) {
1118  // Keep the same key for each filter
1119  $filter['value'] = $word;
1120  // Add a search for the value
1121  $filters[] = $filter;
1122  }
1123  }
1124 
1125  $filter = $filters;
1126  }
1127 
1128  // State/Province should be exact matches
1129  if ( 'address' === $form_field->field->type ) {
1130 
1131  $searchable_fields = $this->get_view_searchable_fields( $view, true );
1132 
1133  foreach ( $searchable_fields as $searchable_field ) {
1134 
1135  if( $form_field->ID !== $searchable_field['field'] ) {
1136  continue;
1137  }
1138 
1139  // Only exact-match dropdowns, not text search
1140  if( in_array( $searchable_field['input'], array( 'text', 'search' ), true ) ) {
1141  continue;
1142  }
1143 
1144  $input_id = gravityview_get_input_id_from_id( $form_field->ID );
1145 
1146  if ( 4 === $input_id ) {
1147  $filter['operator'] = $this->get_operator( $get, $key, array( 'is' ), 'is' );
1148  };
1149  }
1150  }
1151 
1152  break;
1153 
1154  case 'date':
1155 
1156  $date_format = $this->get_datepicker_format( true );
1157 
1158  if ( is_array( $value ) ) {
1159 
1160  // Reset filter variable
1161  $filter = array();
1162 
1163  foreach ( $value as $k => $date ) {
1164  if ( empty( $date ) ) {
1165  continue;
1166  }
1167  $operator = 'start' === $k ? '>=' : '<=';
1168 
1169  /**
1170  * @hack
1171  * @since 1.16.3
1172  * Safeguard until GF implements '<=' operator
1173  */
1174  if( !GFFormsModel::is_valid_operator( $operator ) && $operator === '<=' ) {
1175  $operator = '<';
1176  $date = date( 'Y-m-d', strtotime( self::get_formatted_date( $date, 'Y-m-d', $date_format ) . ' +1 day' ) );
1177  }
1178 
1179  $filter[] = array(
1180  'key' => $field_id,
1181  'value' => self::get_formatted_date( $date, 'Y-m-d', $date_format ),
1182  'operator' => $this->get_operator( $get, $key, array( $operator ), $operator ),
1183  );
1184  }
1185  } else {
1186  $date = $value;
1187  $filter['value'] = self::get_formatted_date( $date, 'Y-m-d', $date_format );
1188  $filter['operator'] = $this->get_operator( $get, $key, array( 'is' ), 'is' );
1189  }
1190 
1191  break;
1192 
1193 
1194  } // switch field type
1195 
1196  return $filter;
1197  }
1198 
1199  /**
1200  * Get the Field Format form GravityForms
1201  *
1202  * @param GF_Field_Date $field The field object
1203  * @since 1.10
1204  *
1205  * @return string Format of the date in the database
1206  */
1207  public static function get_date_field_format( GF_Field_Date $field ) {
1208  $format = 'm/d/Y';
1209  $datepicker = array(
1210  'mdy' => 'm/d/Y',
1211  'dmy' => 'd/m/Y',
1212  'dmy_dash' => 'd-m-Y',
1213  'dmy_dot' => 'd.m.Y',
1214  'ymd_slash' => 'Y/m/d',
1215  'ymd_dash' => 'Y-m-d',
1216  'ymd_dot' => 'Y.m.d',
1217  );
1218 
1219  if ( ! empty( $field->dateFormat ) && isset( $datepicker[ $field->dateFormat ] ) ){
1220  $format = $datepicker[ $field->dateFormat ];
1221  }
1222 
1223  return $format;
1224  }
1225 
1226  /**
1227  * Format a date value
1228  *
1229  * @param string $value Date value input
1230  * @param string $format Wanted formatted date
1231  *
1232  * @since 2.1.2
1233  * @param string $value_format The value format. Default: Y-m-d
1234  *
1235  * @return string
1236  */
1237  public static function get_formatted_date( $value = '', $format = 'Y-m-d', $value_format = 'Y-m-d' ) {
1238 
1239  $date = date_create_from_format( $value_format, $value );
1240 
1241  if ( empty( $date ) ) {
1242  gravityview()->log->debug( 'Date format not valid: {value}', array( 'value' => $value ) );
1243  return '';
1244  }
1245  return $date->format( $format );
1246  }
1247 
1248 
1249  /**
1250  * Include this extension templates path
1251  * @param array $file_paths List of template paths ordered
1252  */
1253  public function add_template_path( $file_paths ) {
1254 
1255  // Index 100 is the default GravityView template path.
1256  $file_paths[102] = self::$file . 'templates/';
1257 
1258  return $file_paths;
1259  }
1260 
1261  /**
1262  * Check whether the configured search fields have a date field
1263  *
1264  * @since 1.17.5
1265  *
1266  * @param array $search_fields
1267  *
1268  * @return bool True: has a `date` or `date_range` field
1269  */
1270  private function has_date_field( $search_fields ) {
1271 
1272  $has_date = false;
1273 
1274  foreach ( $search_fields as $k => $field ) {
1275  if ( in_array( $field['input'], array( 'date', 'date_range', 'entry_date' ) ) ) {
1276  $has_date = true;
1277  break;
1278  }
1279  }
1280 
1281  return $has_date;
1282  }
1283 
1284  /**
1285  * Renders the Search Widget
1286  * @param array $widget_args
1287  * @param string $content
1288  * @param string $context
1289  *
1290  * @return void
1291  */
1292  public function render_frontend( $widget_args, $content = '', $context = '' ) {
1293  /** @var GravityView_View $gravityview_view */
1295 
1296  if ( empty( $gravityview_view ) ) {
1297  gravityview()->log->debug( '$gravityview_view not instantiated yet.' );
1298  return;
1299  }
1300 
1301  // get configured search fields
1302  $search_fields = ! empty( $widget_args['search_fields'] ) ? json_decode( $widget_args['search_fields'], true ) : '';
1303 
1304  if ( empty( $search_fields ) || ! is_array( $search_fields ) ) {
1305  gravityview()->log->debug( 'No search fields configured for widget:', array( 'data' => $widget_args ) );
1306  return;
1307  }
1308 
1309  $view = \GV\View::by_id( $gravityview_view->view_id );
1310 
1311  // prepare fields
1312  foreach ( $search_fields as $k => $field ) {
1313 
1314  $updated_field = $field;
1315 
1316  $updated_field = $this->get_search_filter_details( $updated_field, $context );
1317 
1318  switch ( $field['field'] ) {
1319 
1320  case 'search_all':
1321  $updated_field['key'] = 'search_all';
1322  $updated_field['input'] = 'search_all';
1323  $updated_field['value'] = $this->rgget_or_rgpost( 'gv_search' );
1324  break;
1325 
1326  case 'entry_date':
1327  $updated_field['key'] = 'entry_date';
1328  $updated_field['input'] = 'entry_date';
1329  $updated_field['value'] = array(
1330  'start' => $this->rgget_or_rgpost( 'gv_start' ),
1331  'end' => $this->rgget_or_rgpost( 'gv_end' ),
1332  );
1333  break;
1334 
1335  case 'entry_id':
1336  $updated_field['key'] = 'entry_id';
1337  $updated_field['input'] = 'entry_id';
1338  $updated_field['value'] = $this->rgget_or_rgpost( 'gv_id' );
1339  break;
1340 
1341  case 'created_by':
1342  $updated_field['key'] = 'created_by';
1343  $updated_field['name'] = 'gv_by';
1344  $updated_field['value'] = $this->rgget_or_rgpost( 'gv_by' );
1345  $updated_field['choices'] = self::get_created_by_choices( $view );
1346  break;
1347 
1348  case 'is_approved':
1349  $updated_field['key'] = 'is_approved';
1350  $updated_field['value'] = $this->rgget_or_rgpost( 'filter_is_approved' );
1351  $updated_field['choices'] = self::get_is_approved_choices();
1352  break;
1353  }
1354 
1355  $search_fields[ $k ] = $updated_field;
1356  }
1357 
1358  gravityview()->log->debug( 'Calculated Search Fields: ', array( 'data' => $search_fields ) );
1359 
1360  /**
1361  * @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.
1362  * @param array $search_fields Array of search filters with `key`, `label`, `value`, `type`, `choices` keys
1363  * @param GravityView_Widget_Search $this Current widget object
1364  * @param array $widget_args Args passed to this method. {@since 1.8}
1365  * @param \GV\Template_Context $context {@since 2.0}
1366  * @var array
1367  */
1368  $gravityview_view->search_fields = apply_filters( 'gravityview_widget_search_filters', $search_fields, $this, $widget_args, $context );
1369 
1370  $gravityview_view->permalink_fields = $this->add_no_permalink_fields( array(), $this, $widget_args );
1371 
1372  $gravityview_view->search_layout = ! empty( $widget_args['search_layout'] ) ? $widget_args['search_layout'] : 'horizontal';
1373 
1374  /** @since 1.14 */
1375  $gravityview_view->search_mode = ! empty( $widget_args['search_mode'] ) ? $widget_args['search_mode'] : 'any';
1376 
1377  $custom_class = ! empty( $widget_args['custom_class'] ) ? $widget_args['custom_class'] : '';
1378 
1379  $gravityview_view->search_class = self::get_search_class( $custom_class );
1380 
1381  $gravityview_view->search_clear = ! empty( $widget_args['search_clear'] ) ? $widget_args['search_clear'] : false;
1382 
1383  if ( $this->has_date_field( $search_fields ) ) {
1384  // enqueue datepicker stuff only if needed!
1385  $this->enqueue_datepicker();
1386  }
1387 
1388  $this->maybe_enqueue_flexibility();
1389 
1390  $gravityview_view->render( 'widget', 'search', false );
1391  }
1392 
1393  /**
1394  * Get the search class for a search form
1395  *
1396  * @since 1.5.4
1397  *
1398  * @return string Sanitized CSS class for the search form
1399  */
1400  public static function get_search_class( $custom_class = '' ) {
1402 
1403  $search_class = 'gv-search-'.$gravityview_view->search_layout;
1404 
1405  if ( ! empty( $custom_class ) ) {
1406  $search_class .= ' '.$custom_class;
1407  }
1408 
1409  /**
1410  * @filter `gravityview_search_class` Modify the CSS class for the search form
1411  * @param string $search_class The CSS class for the search form
1412  */
1413  $search_class = apply_filters( 'gravityview_search_class', $search_class );
1414 
1415  // Is there an active search being performed? Used by fe-views.js
1416  $search_class .= GravityView_frontend::getInstance()->isSearch() ? ' gv-is-search' : '';
1417 
1418  return gravityview_sanitize_html_class( $search_class );
1419  }
1420 
1421 
1422  /**
1423  * Calculate the search form action
1424  * @since 1.6
1425  *
1426  * @return string
1427  */
1428  public static function get_search_form_action() {
1430 
1431  $post_id = $gravityview_view->getPostId() ? $gravityview_view->getPostId() : $gravityview_view->getViewId();
1432 
1433  $url = add_query_arg( array(), get_permalink( $post_id ) );
1434 
1435  return esc_url( $url );
1436  }
1437 
1438  /**
1439  * Get the label for a search form field
1440  * @param array $field Field setting as sent by the GV configuration - has `field`, `input` (input type), and `label` keys
1441  * @param array $form_field Form field data, as fetched by `gravityview_get_field()`
1442  * @return string Label for the search form
1443  */
1444  private static function get_field_label( $field, $form_field = array() ) {
1445 
1446  $label = \GV\Utils::_GET( 'label', \GV\Utils::get( $field, 'label' ) );
1447 
1448  if ( ! $label ) {
1449 
1450  $label = isset( $form_field['label'] ) ? $form_field['label'] : '';
1451 
1452  switch( $field['field'] ) {
1453  case 'search_all':
1454  $label = __( 'Search Entries:', 'gravityview' );
1455  break;
1456  case 'entry_date':
1457  $label = __( 'Filter by date:', 'gravityview' );
1458  break;
1459  case 'entry_id':
1460  $label = __( 'Entry ID:', 'gravityview' );
1461  break;
1462  default:
1463  // If this is a field input, not a field
1464  if ( strpos( $field['field'], '.' ) > 0 && ! empty( $form_field['inputs'] ) ) {
1465 
1466  // Get the label for the field in question, which returns an array
1467  $items = wp_list_filter( $form_field['inputs'], array( 'id' => $field['field'] ) );
1468 
1469  // Get the item with the `label` key
1470  $values = wp_list_pluck( $items, 'label' );
1471 
1472  // There will only one item in the array, but this is easier
1473  foreach ( $values as $value ) {
1474  $label = $value;
1475  break;
1476  }
1477  }
1478  }
1479  }
1480 
1481  /**
1482  * @filter `gravityview_search_field_label` Modify the label for a search field. Supports returning HTML
1483  * @since 1.17.3 Added $field parameter
1484  * @param[in,out] string $label Existing label text, sanitized.
1485  * @param[in] array $form_field Gravity Forms field array, as returned by `GFFormsModel::get_field()`
1486  * @param[in] array $field Field setting as sent by the GV configuration - has `field`, `input` (input type), and `label` keys
1487  */
1488  $label = apply_filters( 'gravityview_search_field_label', esc_attr( $label ), $form_field, $field );
1489 
1490  return $label;
1491  }
1492 
1493  /**
1494  * Prepare search fields to frontend render with other details (label, field type, searched values)
1495  *
1496  * @param array $field
1497  * @param \GV\Context $context
1498  *
1499  * @return array
1500  */
1501  private function get_search_filter_details( $field, $context ) {
1502 
1504 
1505  $form = $gravityview_view->getForm();
1506 
1507  // for advanced field ids (eg, first name / last name )
1508  $name = 'filter_' . str_replace( '.', '_', $field['field'] );
1509 
1510  // get searched value from $_GET/$_POST (string or array)
1511  $value = $this->rgget_or_rgpost( $name );
1512 
1513  // get form field details
1514  $form_field = gravityview_get_field( $form, $field['field'] );
1515 
1516  $filter = array(
1517  'key' => $field['field'],
1518  'name' => $name,
1519  'label' => self::get_field_label( $field, $form_field ),
1520  'input' => $field['input'],
1521  'value' => $value,
1522  'type' => $form_field['type'],
1523  );
1524 
1525  // collect choices
1526  if ( 'post_category' === $form_field['type'] && ! empty( $form_field['displayAllCategories'] ) && empty( $form_field['choices'] ) ) {
1527  $filter['choices'] = gravityview_get_terms_choices();
1528  } elseif ( ! empty( $form_field['choices'] ) ) {
1529  $filter['choices'] = $form_field['choices'];
1530  }
1531 
1532  if ( 'date_range' === $field['input'] && empty( $value ) ) {
1533  $filter['value'] = array( 'start' => '', 'end' => '' );
1534  }
1535 
1536  if ( ! empty( $filter['choices'] ) ) {
1537  /**
1538  * @filter `gravityview/search/sieve_choices` Only output used choices for this field.
1539  * @param[in,out] bool Yes or no.
1540  * @param array $field The field configuration.
1541  * @param \GV\Context The context.
1542  */
1543  if ( apply_filters( 'gravityview/search/sieve_choices', false, $field, $context ) ) {
1544  $filter['choices'] = $this->sieve_filter_choices( $filter, $context );
1545  }
1546  }
1547 
1548  /**
1549  * @filter `gravityview/search/filter_details` Filter the output filter details for the Search widget.
1550  * @param[in,out] array $filter The filter details
1551  * @param array $field The search field configuration
1552  * @param \GV\Context The context
1553  * @since develop
1554  */
1555  $filter = apply_filters( 'gravityview/search/filter_details', $filter, $field, $context );
1556 
1557  return $filter;
1558 
1559  }
1560 
1561  /**
1562  * Sieve filter choices to only ones that are used.
1563  *
1564  * @param array $filter The filter configuration.
1565  * @param \GV\Context $context The context
1566  *
1567  * @since develop
1568  * @internal
1569  *
1570  * @return array The filter choices.
1571  */
1572  private function sieve_filter_choices( $filter, $context ) {
1573  if ( empty( $filter['key'] ) || empty( $filter['choices'] ) ) {
1574  return $filter; // @todo Populate plugins might give us empty choices
1575  }
1576 
1577  if ( ! is_numeric( $filter['key'] ) ) {
1578  return $filter;
1579  }
1580 
1581  $form_id = $context->view->form->ID; // @todo Support multiple forms (joins)
1582 
1583  global $wpdb;
1584 
1585  $table = GFFormsModel::get_entry_meta_table_name();
1586 
1587  $key_like = $wpdb->esc_like( $filter['key'] ) . '.%';
1588 
1589  switch ( \GV\Utils::get( $filter, 'type' ) ):
1590  case 'post_category':
1591  $choices = $wpdb->get_col( $wpdb->prepare(
1592  "SELECT DISTINCT SUBSTRING_INDEX(meta_value, ':', 1) FROM $table WHERE (meta_key LIKE %s OR meta_key = %d) AND form_id = %d",
1593  $key_like, $filter['key'], $form_id
1594  ) );
1595  break;
1596  default:
1597  $choices = $wpdb->get_col( $wpdb->prepare(
1598  "SELECT DISTINCT meta_value FROM $table WHERE (meta_key LIKE %s OR meta_key = %d) AND form_id = %d",
1599  $key_like, $filter['key'], $form_id
1600  ) );
1601 
1602  if ( ( $field = gravityview_get_field( $form_id, $filter['key'] ) ) && 'json' === $field->storageType ) {
1603  $choices = array_map( 'json_decode', $choices );
1604  $_choices_array = array();
1605  foreach ( $choices as $choice ) {
1606  if ( is_array( $choice ) ) {
1607  $_choices_array = array_merge( $_choices_array, $choice );
1608  } else {
1609  $_choices_array []= $choice;
1610  }
1611  }
1612  $choices = array_unique( $_choices_array );
1613  }
1614 
1615  break;
1616  endswitch;
1617 
1618  $filter_choices = array();
1619  foreach ( $filter['choices'] as $choice ) {
1620  if ( in_array( $choice['text'], $choices, true ) || in_array( $choice['value'], $choices, true ) ) {
1621  $filter_choices[] = $choice;
1622  }
1623  }
1624 
1625  return $filter_choices;
1626  }
1627 
1628  /**
1629  * Calculate the search choices for the users
1630  *
1631  * @param \GV\View $view The view
1632  * @since develop
1633  *
1634  * @since 1.8
1635  *
1636  * @return array Array of user choices (value = ID, text = display name)
1637  */
1638  private static function get_created_by_choices( $view ) {
1639 
1640  /**
1641  * filter gravityview/get_users/search_widget
1642  * @see \GVCommon::get_users
1643  */
1644  $users = GVCommon::get_users( 'search_widget', array( 'fields' => array( 'ID', 'display_name' ) ) );
1645 
1646  $choices = array();
1647  foreach ( $users as $user ) {
1648  /**
1649  * @filter `gravityview/search/created_by/text` Filter the display text in created by search choices
1650  * @since develop
1651  * @param string[in,out] The text. Default: $user->display_name
1652  * @param \WP_User $user The user.
1653  * @param \GV\View $view The view.
1654  */
1655  $text = apply_filters( 'gravityview/search/created_by/text', $user->display_name, $user, $view );
1656  $choices[] = array(
1657  'value' => $user->ID,
1658  'text' => $text,
1659  );
1660  }
1661 
1662  return $choices;
1663  }
1664 
1665  /**
1666  * Calculate the search checkbox choices for approval status
1667  *
1668  * @since develop
1669  *
1670  * @return array Array of approval status choices (value = status, text = display name)
1671  */
1672  private static function get_is_approved_choices() {
1673 
1674  $choices = array();
1675  foreach ( GravityView_Entry_Approval_Status::get_all() as $status ) {
1676  $choices[] = array(
1677  'value' => $status['value'],
1678  'text' => $status['label'],
1679  );
1680  }
1681 
1682  return $choices;
1683  }
1684 
1685  /**
1686  * Output the Clear Search Results button
1687  * @since 1.5.4
1688  */
1689  public static function the_clear_search_button() {
1691 
1692  if ( $gravityview_view->search_clear ) {
1693 
1694  $url = strtok( add_query_arg( array() ), '?' );
1695 
1696  echo gravityview_get_link( $url, esc_html__( 'Clear', 'gravityview' ), 'class=button gv-search-clear' );
1697 
1698  }
1699  }
1700 
1701  /**
1702  * Based on the search method, fetch the value for a specific key
1703  *
1704  * @since 1.16.4
1705  *
1706  * @param string $name Name of the request key to fetch the value for
1707  *
1708  * @return mixed|string Value of request at $name key. Empty string if empty.
1709  */
1710  private function rgget_or_rgpost( $name ) {
1712 
1713  $value = stripslashes_deep( $value );
1714 
1715  $value = gv_map_deep( $value, 'rawurldecode' );
1716 
1717  $value = gv_map_deep( $value, '_wp_specialchars' );
1718 
1719  return $value;
1720  }
1721 
1722 
1723  /**
1724  * Require the datepicker script for the frontend GV script
1725  * @param array $js_dependencies Array of existing required scripts for the fe-views.js script
1726  * @return array Array required scripts, with `jquery-ui-datepicker` added
1727  */
1728  public function add_datepicker_js_dependency( $js_dependencies ) {
1729 
1730  $js_dependencies[] = 'jquery-ui-datepicker';
1731 
1732  return $js_dependencies;
1733  }
1734 
1735  /**
1736  * Modify the array passed to wp_localize_script()
1737  *
1738  * @param array $js_localization The data padded to the Javascript file
1739  * @param array $view_data View data array with View settings
1740  *
1741  * @return array
1742  */
1743  public function add_datepicker_localization( $localizations = array(), $view_data = array() ) {
1744  global $wp_locale;
1745 
1746  /**
1747  * @filter `gravityview_datepicker_settings` Modify the datepicker settings
1748  * @see http://api.jqueryui.com/datepicker/ Learn what settings are available
1749  * @see http://www.renegadetechconsulting.com/tutorials/jquery-datepicker-and-wordpress-i18n Thanks for the helpful information on $wp_locale
1750  * @param array $js_localization The data padded to the Javascript file
1751  * @param array $view_data View data array with View settings
1752  */
1753  $datepicker_settings = apply_filters( 'gravityview_datepicker_settings', array(
1754  'yearRange' => '-5:+5',
1755  'changeMonth' => true,
1756  'changeYear' => true,
1757  'closeText' => esc_attr_x( 'Close', 'Close calendar', 'gravityview' ),
1758  'prevText' => esc_attr_x( 'Prev', 'Previous month in calendar', 'gravityview' ),
1759  'nextText' => esc_attr_x( 'Next', 'Next month in calendar', 'gravityview' ),
1760  'currentText' => esc_attr_x( 'Today', 'Today in calendar', 'gravityview' ),
1761  'weekHeader' => esc_attr_x( 'Week', 'Week in calendar', 'gravityview' ),
1762  'monthStatus' => __( 'Show a different month', 'gravityview' ),
1763  'monthNames' => array_values( $wp_locale->month ),
1764  'monthNamesShort' => array_values( $wp_locale->month_abbrev ),
1765  'dayNames' => array_values( $wp_locale->weekday ),
1766  'dayNamesShort' => array_values( $wp_locale->weekday_abbrev ),
1767  'dayNamesMin' => array_values( $wp_locale->weekday_initial ),
1768  // get the start of week from WP general setting
1769  'firstDay' => get_option( 'start_of_week' ),
1770  // is Right to left language? default is false
1771  'isRTL' => is_rtl(),
1772  ), $view_data );
1773 
1774  $localizations['datepicker'] = $datepicker_settings;
1775 
1776  return $localizations;
1777 
1778  }
1779 
1780  /**
1781  * Register search widget scripts, including Flexibility
1782  *
1783  * @see https://github.com/10up/flexibility
1784  *
1785  * @since 1.17
1786  *
1787  * @return void
1788  */
1789  public function register_scripts() {
1790  wp_register_script( 'gv-flexibility', plugins_url( 'assets/lib/flexibility/flexibility.js', GRAVITYVIEW_FILE ), array(), \GV\Plugin::$version, true );
1791  }
1792 
1793  /**
1794  * If the current visitor is running IE 8 or 9, enqueue Flexibility
1795  *
1796  * @since 1.17
1797  *
1798  * @return void
1799  */
1800  private function maybe_enqueue_flexibility() {
1801  if ( isset( $_SERVER['HTTP_USER_AGENT'] ) && preg_match( '/MSIE [8-9]/', $_SERVER['HTTP_USER_AGENT'] ) ) {
1802  wp_enqueue_script( 'gv-flexibility' );
1803  }
1804  }
1805 
1806  /**
1807  * Enqueue the datepicker script
1808  *
1809  * It sets the $gravityview->datepicker_class parameter
1810  *
1811  * @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.
1812  * @return void
1813  */
1814  public function enqueue_datepicker() {
1816 
1817  wp_enqueue_script( 'jquery-ui-datepicker' );
1818 
1819  add_filter( 'gravityview_js_dependencies', array( $this, 'add_datepicker_js_dependency' ) );
1820  add_filter( 'gravityview_js_localization', array( $this, 'add_datepicker_localization' ), 10, 2 );
1821 
1822  $scheme = is_ssl() ? 'https://' : 'http://';
1823  wp_enqueue_style( 'jquery-ui-datepicker', $scheme.'ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/smoothness/jquery-ui.css' );
1824 
1825  /**
1826  * @filter `gravityview_search_datepicker_class`
1827  * 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.
1828  * @param string $css_class CSS class to use. Default: `gv-datepicker datepicker mdy` \n
1829  * Options are:
1830  * - `mdy` (mm/dd/yyyy)
1831  * - `dmy` (dd/mm/yyyy)
1832  * - `dmy_dash` (dd-mm-yyyy)
1833  * - `dmy_dot` (dd.mm.yyyy)
1834  * - `ymd_slash` (yyyy/mm/dd)
1835  * - `ymd_dash` (yyyy-mm-dd)
1836  * - `ymd_dot` (yyyy.mm.dd)
1837  */
1838  $datepicker_class = apply_filters( 'gravityview_search_datepicker_class', "gv-datepicker datepicker " . $this->get_datepicker_format() );
1839 
1840  $gravityview_view->datepicker_class = $datepicker_class;
1841  }
1842 
1843  /**
1844  * Retrieve the datepicker format.
1845  *
1846  * @param bool $date_format Whether to return the PHP date format or the datpicker class name. Default: false.
1847  *
1848  * @see https://docs.gravityview.co/article/115-changing-the-format-of-the-search-widgets-date-picker
1849  *
1850  * @return string The datepicker format placeholder, or the PHP date format.
1851  */
1852  private function get_datepicker_format( $date_format = false ) {
1853 
1854  $default_format = 'mdy';
1855 
1856  /**
1857  * @filter `gravityview/widgets/search/datepicker/format`
1858  * @since 2.1.1
1859  * @param string $format Default: mdy
1860  * Options are:
1861  * - `mdy` (mm/dd/yyyy)
1862  * - `dmy` (dd/mm/yyyy)
1863  * - `dmy_dash` (dd-mm-yyyy)
1864  * - `dmy_dot` (dd.mm.yyyy)
1865  * - `ymd_slash` (yyyy/mm/dd)
1866  * - `ymd_dash` (yyyy-mm-dd)
1867  * - `ymd_dot` (yyyy.mm.dd)
1868  */
1869  $format = apply_filters( 'gravityview/widgets/search/datepicker/format', $default_format );
1870 
1871  $gf_date_formats = array(
1872  'mdy' => 'm/d/Y',
1873 
1874  'dmy_dash' => 'd-m-Y',
1875  'dmy_dot' => 'd.m.Y',
1876  'dmy' => 'd/m/Y',
1877 
1878  'ymd_slash' => 'Y/m/d',
1879  'ymd_dash' => 'Y-m-d',
1880  'ymd_dot' => 'Y.m.d',
1881  );
1882 
1883  if ( ! $date_format ) {
1884  // If the format key isn't valid, return default format key
1885  return isset( $gf_date_formats[ $format ] ) ? $format : $default_format;
1886  }
1887 
1888  // If the format key isn't valid, return default format value
1889  return \GV\Utils::get( $gf_date_formats, $format, $gf_date_formats[ $default_format ] );
1890  }
1891 
1892  /**
1893  * If previewing a View or page with embedded Views, make the search work properly by adding hidden fields with query vars
1894  *
1895  * @since 2.2.1
1896  *
1897  * @return void
1898  */
1899  public function add_preview_inputs() {
1900  global $wp;
1901 
1902  if ( ! is_preview() || ! current_user_can( 'publish_gravityviews') ) {
1903  return;
1904  }
1905 
1906  // Outputs `preview` and `post_id` variables
1907  foreach ( $wp->query_vars as $key => $value ) {
1908  printf( '<input type="hidden" name="%s" value="%s" />', esc_attr( $key ), esc_attr( $value ) );
1909  }
1910 
1911  }
1912 
1913  /**
1914  * Get an operator URL override.
1915  *
1916  * @param array $get Where to look for the operator.
1917  * @param string $key The filter key to look for.
1918  * @param array $allowed The allowed operators (whitelist).
1919  * @param string $default The default operator.
1920  *
1921  * @return string The operator.
1922  */
1923  private function get_operator( $get, $key, $allowed, $default ) {
1924  $operator = \GV\Utils::get( $get, "$key|op", $default );
1925 
1926  /**
1927  * @filter `gravityview/search/operator_whitelist` An array of allowed operators for a field.
1928  * @param[in,out] string[] A whitelist of allowed operators.
1929  * @param string The filter name.
1930  */
1931  $allowed = apply_filters( 'gravityview/search/operator_whitelist', $allowed, $key );
1932 
1933  if ( ! in_array( $operator, $allowed, true ) ) {
1934  $operator = $default;
1935  }
1936 
1937  return $operator;
1938  }
1939 
1940 
1941 } // end class
1942 
1944 
1945 if ( ! gravityview()->plugin->supports( \GV\Plugin::FEATURE_GFQUERY ) ) {
1946  return;
1947 }
1948 
1949 /**
1950  * A GF_Query condition that allows user data searches.
1951  */
1952 class GravityView_Widget_Search_Author_GF_Query_Condition extends \GF_Query_Condition {
1953  public function __construct( $filter, $view ) {
1954  $this->value = $filter['value'];
1955  $this->view = $view;
1956  }
1957 
1958  public function sql( $query ) {
1959  global $wpdb;
1960 
1961  $user_meta_fields = array(
1962  'nickname', 'first_name', 'last_name',
1963  );
1964 
1965  /**
1966  * @filter `gravityview/widgets/search/created_by/user_meta_fields` Filter the user meta fields to search.
1967  * @param[in,out] array The user meta fields.
1968  * @param \GV\View $view The view.
1969  */
1970  $user_meta_fields = apply_filters( 'gravityview/widgets/search/created_by/user_meta_fields', $user_meta_fields, $this->view );
1971 
1972  $user_fields = array(
1973  'user_nicename', 'user_login', 'display_name', 'user_email',
1974  );
1975 
1976  /**
1977  * @filter `gravityview/widgets/search/created_by/user_fields` Filter the user fields to search.
1978  * @param[in,out] array The user fields.
1979  * @param \GV\View $view The view.
1980  */
1981  $user_fields = apply_filters( 'gravityview/widgets/search/created_by/user_fields', $user_fields, $this->view );
1982 
1983  $conditions = array();
1984 
1985  foreach ( $user_fields as $user_field ) {
1986  $conditions[] = $wpdb->prepare( "`u`.`$user_field` LIKE %s", '%' . $wpdb->esc_like( $this->value ) . '%' );
1987  }
1988 
1989  foreach ( $user_meta_fields as $meta_field ) {
1990  $conditions[] = $wpdb->prepare( "(`um`.`meta_key` = %s AND `um`.`meta_value` LIKE %s)", $meta_field, '%' . $wpdb->esc_like( $this->value ) . '%' );
1991  }
1992 
1993  $conditions = '(' . implode( ' OR ', $conditions ) . ')';
1994 
1995  $alias = $query->_alias( null );
1996 
1997  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)))";
1998  }
1999 }
new GravityView_Widget_Search
rgget_or_rgpost( $name)
Based on the search method, fetch the value for a specific key.
$url
Definition: post_image.php:25
sieve_filter_choices( $filter, $context)
Sieve filter choices to only ones that are used.
static get_field_label( $field, $form_field=array())
Get the label for a search form field.
all()
Get all the settings.
$labels
static get_searchable_fields()
Ajax Returns the form fields ( only the searchable ones )
static _GET( $name, $default=null)
Grab a value from the _GET superglobal or default.
$forms
Definition: data-source.php:19
add_datepicker_localization( $localizations=array(), $view_data=array())
Modify the array passed to wp_localize_script()
static getInstance( $passed_post=NULL)
set_search_method()
Sets the search method to GET (default) or POST.
get_search_method()
Returns the search method.
if(empty( $value)) $user
static _REQUEST( $name, $default=null)
Grab a value from the _REQUEST superglobal or default.
if(gv_empty( $field['value'], false, false)) $format
register_no_conflict( $allowed)
Add admin script to the no-conflict scripts whitelist.
static get_created_by_choices( $view)
Calculate the search choices for the users.
static get_formatted_date( $value='', $format='Y-m-d', $value_format='Y-m-d')
Format a date value.
add_preview_inputs()
If previewing a View or page with embedded Views, make the search work properly by adding hidden fiel...
get_search_filter_details( $field, $context)
Prepare search fields to frontend render with other details (label, field type, searched values) ...
enqueue_datepicker()
Enqueue the datepicker script.
render_frontend( $widget_args, $content='', $context='')
Frontend logic.
gravityview_get_link( $href='', $anchor_text='', $atts=array())
Generate an HTML anchor tag with a list of supported attributes.
static get_search_form_action()
Calculate the search form action.
gravityview()
Definition: _stubs.php:26
get( $key, $default=null)
Retrieve a setting.
gravityview_get_input_id_from_id( $field_id='')
Very commonly needed: get the # of the input based on a full field ID.
If this file is called directly, abort.
if(empty( $field_settings['content'])) $content
Definition: custom.php:37
static get_search_input_label( $input_type)
static get_date_field_format(GF_Field_Date $field)
Get the Field Format form GravityForms.
gv_map_deep( $value, $callback)
Maps a function to all non-iterable elements of an array or an object.
has_date_field( $search_fields)
Check whether the configured search fields have a date field.
gravityview_get_field( $form, $field_id)
Returns the field details array of a specific form given the field id.
gravityview_get_form_fields( $form='', $add_default_properties=false, $include_parent_field=true)
Return array of fields&#39; id and label, for a given Form ID.
static pre_get_form_fields( $template_id='')
Get the the form fields for a preset (no form created yet)
Definition: class-ajax.php:304
register_scripts()
Register search widget scripts, including Flexibility.
add_scripts_and_styles( $hook)
Add script to Views edit screen (admin)
static by_id( $post_id)
Construct a instance from a post ID.
static get_users( $context='change_entry_creator', $args=array())
Get WordPress users with reasonable limits set.
is_registered()
Whether this Widget&#39;s been registered already or not.
gravityview_get_form_id( $view_id)
Get the connected form ID from a View ID.
const GRAVITYVIEW_FILE
Full path to the GravityView file "GRAVITYVIEW_FILE" "./gravityview.php".
Definition: gravityview.php:31
$field_id
Definition: time.php:17
static get_search_class( $custom_class='')
Get the search class for a search form.
gf_query_filter(&$query, $view, $request)
Filters the with advanced logic.
maybe_enqueue_flexibility()
If the current visitor is running IE 8 or 9, enqueue Flexibility.
add_datepicker_js_dependency( $js_dependencies)
Require the datepicker script for the frontend GV script.
static get_search_input_types( $field_id='', $field_type=null)
Assign an input type according to the form field type.
if(empty( $created_by)) $form_id
gravityview_get_terms_choices( $args=array())
Get categories formatted in a way used by GravityView and Gravity Forms input choices.
const UNAPPROVED
get_datepicker_format( $date_format=false)
Retrieve the datepicker format.
prepare_field_filter( $filter_key, $value, $view, $searchable_fields, $get=array())
Prepare the field filters to GFAPI.
convert_request_key_to_filter_key( $key)
Convert $_GET/$_POST key to the field/meta ID.
filter_entries( $search_criteria, $form_id=null, $args=array(), $force_search_criteria=false)
— Frontend —
get_operator( $get, $key, $allowed, $default)
Get an operator URL override.
static get( $array, $key, $default=null)
Grab a value from an array or an object or default.
A GF_Query condition that allows user data searches.
static render_searchable_fields( $form_id=null, $current='')
Generates html for the available Search Fields dropdown.
gv_empty( $value, $zero_is_empty=true, $allow_string_booleans=true)
Is the value empty?
static get_all()
Return array of status options.
static get_is_approved_choices()
Calculate the search checkbox choices for approval status.
gravityview_get_permalink_query_args( $id=0)
Get get_permalink() without the home_url() prepended to it.
add_no_permalink_fields( $search_fields, $object, $widget_args=array())
Display hidden fields to add support for sites using Default permalink structure. ...
add_template_path( $file_paths)
Include this extension templates path.
get_view_searchable_fields( $view, $with_full_field=false)
Get the fields that are searchable for a View.
$field
Definition: gquiz_grade.php:11
static the_clear_search_button()
Output the Clear Search Results button.
static getInstance()
Get the one true instantiated self.