Completed
Push — update/jetpack-search-move-con... ( 5a716f...42a6a0 )
by
unknown
07:14
created

Jetpack_Search_Widget::enqueue_frontend_scripts()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 2
nop 0
dl 0
loc 15
rs 9.7666
c 0
b 0
f 0
1
<?php
2
/**
3
 * Jetpack Search: Jetpack_Search_Widget class
4
 *
5
 * @package    Jetpack
6
 * @subpackage Jetpack Search
7
 * @since      5.0.0
8
 */
9
10
use Automattic\Jetpack\Constants;
11
use Automattic\Jetpack\Status;
12
use Automattic\Jetpack\Redirect;
13
14
add_action( 'widgets_init', 'jetpack_search_widget_init' );
15
16
function jetpack_search_widget_init() {
17
	if (
18
		! Jetpack::is_active()
19
		|| ( method_exists( 'Jetpack_Plan', 'supports' ) && ! Jetpack_Plan::supports( 'search' ) )
20
	) {
21
		return;
22
	}
23
24
	require_once JETPACK__PLUGIN_DIR . 'modules/search/class.jetpack-search-helpers.php';
25
	require_once JETPACK__PLUGIN_DIR . 'modules/search/class-jetpack-search-options.php';
26
27
	register_widget( 'Jetpack_Search_Widget' );
28
}
29
30
/**
31
 * Provides a widget to show available/selected filters on searches.
32
 *
33
 * @since 5.0.0
34
 *
35
 * @see   WP_Widget
36
 */
37
class Jetpack_Search_Widget extends WP_Widget {
38
39
	/**
40
	 * The Jetpack_Search instance.
41
	 *
42
	 * @since 5.7.0
43
	 * @var Jetpack_Search
44
	 */
45
	protected $jetpack_search;
46
47
	/**
48
	 * Number of aggregations (filters) to show by default.
49
	 *
50
	 * @since 5.8.0
51
	 * @var int
52
	 */
53
	const DEFAULT_FILTER_COUNT = 5;
54
55
	/**
56
	 * Default sort order for search results.
57
	 *
58
	 * @since 5.8.0
59
	 * @var string
60
	 */
61
	const DEFAULT_SORT = 'relevance_desc';
62
63
	/**
64
	 * Jetpack_Search_Widget constructor.
65
	 *
66
	 * @since 5.0.0
67
	 */
68
	public function __construct( $name = null ) {
69
		if ( empty( $name ) ) {
70
			$name = esc_html__( 'Search', 'jetpack' );
71
		}
72
		parent::__construct(
73
			Jetpack_Search_Helpers::FILTER_WIDGET_BASE,
74
			/** This filter is documented in modules/widgets/facebook-likebox.php */
75
			apply_filters( 'jetpack_widget_name', $name ),
76
			array(
77
				'classname'   => 'jetpack-filters widget_search',
78
				'description' => __( 'Instant search and filtering to help visitors quickly find relevant answers and explore your site.', 'jetpack' ),
79
			)
80
		);
81
82
		if (
83
			Jetpack_Search_Helpers::is_active_widget( $this->id ) &&
84
			! $this->is_search_active()
85
		) {
86
			$this->activate_search();
87
		}
88
89
		if ( is_admin() ) {
90
			add_action( 'sidebar_admin_setup', array( $this, 'widget_admin_setup' ) );
91
		} else {
92
			add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_scripts' ) );
93
		}
94
95
		add_action( 'jetpack_search_render_filters_widget_title', array( 'Jetpack_Search_Template_Tags', 'render_widget_title' ), 10, 3 );
96
		if ( Jetpack_Search_Options::is_instant_enabled() ) {
97
			add_action( 'jetpack_search_render_filters', array( 'Jetpack_Search_Template_Tags', 'render_instant_filters' ), 10, 2 );
98
		} else {
99
			add_action( 'jetpack_search_render_filters', array( 'Jetpack_Search_Template_Tags', 'render_available_filters' ), 10, 2 );
100
		}
101
	}
102
103
	/**
104
	 * Check whether search is currently active
105
	 *
106
	 * @since 6.3
107
	 */
108
	public function is_search_active() {
109
		return Jetpack::is_module_active( 'search' );
110
	}
111
112
	/**
113
	 * Activate search
114
	 *
115
	 * @since 6.3
116
	 */
117
	public function activate_search() {
118
		Jetpack::activate_module( 'search', false, false );
119
	}
120
121
122
	/**
123
	 * Enqueues the scripts and styles needed for the customizer.
124
	 *
125
	 * @since 5.7.0
126
	 */
127
	public function widget_admin_setup() {
128
		wp_enqueue_style( 'widget-jetpack-search-filters', plugins_url( 'search/css/search-widget-admin-ui.css', __FILE__ ) );
129
130
		// Required for Tracks
131
		wp_register_script(
132
			'jp-tracks',
133
			'//stats.wp.com/w.js',
134
			array(),
135
			gmdate( 'YW' ),
136
			true
137
		);
138
139
		wp_register_script(
140
			'jp-tracks-functions',
141
			plugins_url( '_inc/lib/tracks/tracks-callables.js', JETPACK__PLUGIN_FILE ),
142
			array(),
143
			JETPACK__VERSION,
144
			false
145
		);
146
147
		wp_register_script(
148
			'jetpack-search-widget-admin',
149
			plugins_url( 'search/js/search-widget-admin.js', __FILE__ ),
150
			array( 'jquery', 'jquery-ui-sortable', 'jp-tracks', 'jp-tracks-functions' ),
151
			JETPACK__VERSION
152
		);
153
154
		wp_localize_script(
155
			'jetpack-search-widget-admin', 'jetpack_search_filter_admin', array(
156
				'defaultFilterCount' => self::DEFAULT_FILTER_COUNT,
157
				'tracksUserData'     => Jetpack_Tracks_Client::get_connected_user_tracks_identity(),
158
				'tracksEventData'    => array(
159
					'is_customizer' => (int) is_customize_preview(),
160
				),
161
				'i18n'               => array(
162
					'month'        => Jetpack_Search_Helpers::get_date_filter_type_name( 'month', false ),
163
					'year'         => Jetpack_Search_Helpers::get_date_filter_type_name( 'year', false ),
164
					'monthUpdated' => Jetpack_Search_Helpers::get_date_filter_type_name( 'month', true ),
165
					'yearUpdated'  => Jetpack_Search_Helpers::get_date_filter_type_name( 'year', true ),
166
				),
167
			)
168
		);
169
170
		wp_enqueue_script( 'jetpack-search-widget-admin' );
171
	}
172
173
	/**
174
	 * Enqueue scripts and styles for the frontend.
175
	 *
176
	 * @since 5.8.0
177
	 */
178
	public function enqueue_frontend_scripts() {
179
		if ( ! is_active_widget( false, false, $this->id_base, true ) || Jetpack_Search_Options::is_instant_enabled() ) {
180
			return;
181
		}
182
183
		wp_enqueue_script(
184
			'jetpack-search-widget',
185
			plugins_url( 'search/js/search-widget.js', __FILE__ ),
186
			array(),
187
			JETPACK__VERSION,
188
			true
189
		);
190
191
		wp_enqueue_style( 'jetpack-search-widget', plugins_url( 'search/css/search-widget-frontend.css', __FILE__ ) );
192
	}
193
194
	/**
195
	 * Get the list of valid sort types/orders.
196
	 *
197
	 * @since 5.8.0
198
	 *
199
	 * @return array The sort orders.
200
	 */
201
	private function get_sort_types() {
202
		return array(
203
			'relevance|DESC' => is_admin() ? esc_html__( 'Relevance (recommended)', 'jetpack' ) : esc_html__( 'Relevance', 'jetpack' ),
204
			'date|DESC'      => esc_html__( 'Newest first', 'jetpack' ),
205
			'date|ASC'       => esc_html__( 'Oldest first', 'jetpack' ),
206
		);
207
	}
208
209
	/**
210
	 * Callback for an array_filter() call in order to only get filters for the current widget.
211
	 *
212
	 * @see   Jetpack_Search_Widget::widget()
213
	 *
214
	 * @since 5.7.0
215
	 *
216
	 * @param array $item Filter item.
217
	 *
218
	 * @return bool Whether the current filter item is for the current widget.
219
	 */
220
	function is_for_current_widget( $item ) {
221
		return isset( $item['widget_id'] ) && $this->id == $item['widget_id'];
222
	}
223
224
	/**
225
	 * This method returns a boolean for whether the widget should show site-wide filters for the site.
226
	 *
227
	 * This is meant to provide backwards-compatibility for VIP, and other professional plan users, that manually
228
	 * configured filters via `Jetpack_Search::set_filters()`.
229
	 *
230
	 * @since 5.7.0
231
	 *
232
	 * @return bool Whether the widget should display site-wide filters or not.
233
	 */
234
	public function should_display_sitewide_filters() {
235
		$filter_widgets = get_option( 'widget_jetpack-search-filters' );
236
237
		// This shouldn't be empty, but just for sanity
238
		if ( empty( $filter_widgets ) ) {
239
			return false;
240
		}
241
242
		// If any widget has any filters, return false
243
		foreach ( $filter_widgets as $number => $widget ) {
244
			$widget_id = sprintf( '%s-%d', $this->id_base, $number );
245
			if ( ! empty( $widget['filters'] ) && is_active_widget( false, $widget_id, $this->id_base ) ) {
246
				return false;
247
			}
248
		}
249
250
		return true;
251
	}
252
253
	public function jetpack_search_populate_defaults( $instance ) {
254
		$instance = wp_parse_args(
255
			(array) $instance, array(
0 ignored issues
show
Documentation introduced by
array('title' => '', 'se...post_types' => array()) is of type array<string,string|bool...,"post_types":"array"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
256
				'title'              => '',
257
				'search_box_enabled' => true,
258
				'user_sort_enabled'  => true,
259
				'sort'               => self::DEFAULT_SORT,
260
				'filters'            => array( array() ),
261
				'post_types'         => array(),
262
			)
263
		);
264
265
		return $instance;
266
	}
267
268
	/**
269
	 * Responsible for rendering the widget on the frontend.
270
	 *
271
	 * @since 5.0.0
272
	 *
273
	 * @param array $args     Widgets args supplied by the theme.
274
	 * @param array $instance The current widget instance.
275
	 */
276
	public function widget( $args, $instance ) {
277
		$instance = $this->jetpack_search_populate_defaults( $instance );
278
279
		if ( ( new Status() )->is_development_mode() ) {
280
			echo $args['before_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
281
			?><div id="<?php echo esc_attr( $this->id ); ?>-wrapper">
282
				<div class="jetpack-search-sort-wrapper">
283
					<label>
284
						<?php esc_html_e( 'Jetpack Search not supported in Development Mode', 'jetpack' ); ?>
285
					</label>
286
				</div>
287
			</div><?php
288
			echo $args['after_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
289
			return;
290
		}
291
292
		if ( Jetpack_Search_Options::is_instant_enabled() ) {
293
			if ( 'jetpack-instant-search-sidebar' === $args['id'] ) {
294
				$this->widget_empty_instant( $args, $instance );
295
			} else {
296
				$this->widget_instant( $args, $instance );
297
			}
298
		} else {
299
			$this->widget_non_instant( $args, $instance );
300
		}
301
	}
302
303
	/**
304
	 * Render the non-instant frontend widget.
305
	 *
306
	 * @since 8.3.0
307
	 *
308
	 * @param array $args     Widgets args supplied by the theme.
309
	 * @param array $instance The current widget instance.
310
	 */
311
	public function widget_non_instant( $args, $instance ) {
312
		$display_filters = false;
313
314
		if ( is_search() ) {
315
			if ( Jetpack_Search_Helpers::should_rerun_search_in_customizer_preview() ) {
316
				Jetpack_Search::instance()->update_search_results_aggregations();
317
			}
318
319
			$filters = Jetpack_Search::instance()->get_filters();
320
321 View Code Duplication
			if ( ! Jetpack_Search_Helpers::are_filters_by_widget_disabled() && ! $this->should_display_sitewide_filters() ) {
322
				$filters = array_filter( $filters, array( $this, 'is_for_current_widget' ) );
323
			}
324
325
			if ( ! empty( $filters ) ) {
326
				$display_filters = true;
327
			}
328
		}
329
330
		if ( ! $display_filters && empty( $instance['search_box_enabled'] ) && empty( $instance['user_sort_enabled'] ) ) {
331
			return;
332
		}
333
334
		$title = ! empty( $instance['title'] ) ? $instance['title'] : '';
335
336
		/** This filter is documented in core/src/wp-includes/default-widgets.php */
337
		$title = apply_filters( 'widget_title', $title, $instance, $this->id_base );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $instance.

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

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

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

Loading history...
338
339
		echo $args['before_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
340
		?>
341
			<div id="<?php echo esc_attr( $this->id ); ?>-wrapper" >
342
		<?php
343
344
		if ( ! empty( $title ) ) {
345
			/**
346
			 * Responsible for displaying the title of the Jetpack Search filters widget.
347
			 *
348
			 * @module search
349
			 *
350
			 * @since  5.7.0
351
			 *
352
			 * @param string $title                The widget's title
353
			 * @param string $args['before_title'] The HTML tag to display before the title
354
			 * @param string $args['after_title']  The HTML tag to display after the title
355
			 */
356
			do_action( 'jetpack_search_render_filters_widget_title', $title, $args['before_title'], $args['after_title'] );
357
		}
358
359
		$default_sort            = isset( $instance['sort'] ) ? $instance['sort'] : self::DEFAULT_SORT;
360
		list( $orderby, $order ) = $this->sorting_to_wp_query_param( $default_sort );
361
		$current_sort            = "{$orderby}|{$order}";
362
363
		// we need to dynamically inject the sort field into the search box when the search box is enabled, and display
364
		// it separately when it's not.
365
		if ( ! empty( $instance['search_box_enabled'] ) ) {
366
			Jetpack_Search_Template_Tags::render_widget_search_form( $instance['post_types'], $orderby, $order );
367
		}
368
369
		if ( ! empty( $instance['search_box_enabled'] ) && ! empty( $instance['user_sort_enabled'] ) ) :
370
				?>
371
					<div class="jetpack-search-sort-wrapper">
372
				<label>
373
					<?php esc_html_e( 'Sort by', 'jetpack' ); ?>
374
					<select class="jetpack-search-sort">
375
						<?php foreach ( $this->get_sort_types() as $sort => $label ) { ?>
376
							<option value="<?php echo esc_attr( $sort ); ?>" <?php selected( $current_sort, $sort ); ?>>
377
								<?php echo esc_html( $label ); ?>
378
							</option>
379
						<?php } ?>
380
					</select>
381
				</label>
382
			</div>
383
		<?php
384
		endif;
385
386
		if ( $display_filters ) {
387
			/**
388
			 * Responsible for rendering filters to narrow down search results.
389
			 *
390
			 * @module search
391
			 *
392
			 * @since  5.8.0
393
			 *
394
			 * @param array $filters    The possible filters for the current query.
395
			 * @param array $post_types An array of post types to limit filtering to.
396
			 */
397
			do_action(
398
				'jetpack_search_render_filters',
399
				$filters,
0 ignored issues
show
Bug introduced by
The variable $filters does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
400
				isset( $instance['post_types'] ) ? $instance['post_types'] : null
401
			);
402
		}
403
404
		$this->maybe_render_sort_javascript( $instance, $order, $orderby );
405
406
		echo '</div>';
407
		echo $args['after_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
408
	}
409
410
	/**
411
	 * Render the instant frontend widget.
412
	 *
413
	 * @since 8.3.0
414
	 *
415
	 * @param array $args     Widgets args supplied by the theme.
416
	 * @param array $instance The current widget instance.
417
	 */
418
	public function widget_instant( $args, $instance ) {
419
		if ( Jetpack_Search_Helpers::should_rerun_search_in_customizer_preview() ) {
420
			Jetpack_Search::instance()->update_search_results_aggregations();
421
		}
422
423
		$filters = Jetpack_Search::instance()->get_filters();
424
425 View Code Duplication
		if ( ! Jetpack_Search_Helpers::are_filters_by_widget_disabled() && ! $this->should_display_sitewide_filters() ) {
426
			$filters = array_filter( $filters, array( $this, 'is_for_current_widget' ) );
427
		}
428
429
		$display_filters = ! empty( $filters );
430
431
		if ( ! $display_filters && empty( $instance['search_box_enabled'] ) ) {
432
			return;
433
		}
434
435
		$title = isset( $instance['title'] ) ? $instance['title'] : '';
436
437
		if ( empty( $title ) ) {
438
			$title = '';
439
		}
440
441
		/** This filter is documented in core/src/wp-includes/default-widgets.php */
442
		$title = apply_filters( 'widget_title', $title, $instance, $this->id_base );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $instance.

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

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

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

Loading history...
443
444
		echo $args['before_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
445
		?>
446
			<div id="<?php echo esc_attr( $this->id ); ?>-wrapper" class="jetpack-instant-search-wrapper">
447
		<?php
448
449
		if ( ! empty( $title ) ) {
450
			/**
451
			 * Responsible for displaying the title of the Jetpack Search filters widget.
452
			 *
453
			 * @module search
454
			 *
455
			 * @since  5.7.0
456
			 *
457
			 * @param string $title                The widget's title
458
			 * @param string $args['before_title'] The HTML tag to display before the title
459
			 * @param string $args['after_title']  The HTML tag to display after the title
460
			 */
461
			do_action( 'jetpack_search_render_filters_widget_title', $title, $args['before_title'], $args['after_title'] );
462
		}
463
464
		// TODO: create new search box?
465
		if ( ! empty( $instance['search_box_enabled'] ) ) {
466
			Jetpack_Search_Template_Tags::render_widget_search_form( array(), '', '' );
467
		}
468
469
		if ( $display_filters ) {
470
			/**
471
			 * Responsible for rendering filters to narrow down search results.
472
			 *
473
			 * @module search
474
			 *
475
			 * @since  5.8.0
476
			 *
477
			 * @param array $filters    The possible filters for the current query.
478
			 * @param array $post_types An array of post types to limit filtering to.
479
			 */
480
			do_action(
481
				'jetpack_search_render_filters',
482
				$filters,
483
				null
484
			);
485
		}
486
487
		echo '</div>';
488
		echo $args['after_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
489
	}
490
491
	/**
492
	 * Render the instant widget for the overlay.
493
	 *
494
	 * @since 8.3.0
495
	 *
496
	 * @param array $args     Widgets args supplied by the theme.
497
	 * @param array $instance The current widget instance.
498
	 */
499
	public function widget_empty_instant( $args, $instance ) {
500
		$title = isset( $instance['title'] ) ? $instance['title'] : '';
501
502
		if ( empty( $title ) ) {
503
			$title = '';
504
		}
505
506
		/** This filter is documented in core/src/wp-includes/default-widgets.php */
507
		$title = apply_filters( 'widget_title', $title, $instance, $this->id_base );
0 ignored issues
show
Unused Code introduced by
The call to apply_filters() has too many arguments starting with $instance.

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

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

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

Loading history...
508
509
		echo $args['before_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
510
		?>
511
			<div id="<?php echo esc_attr( $this->id ); ?>-wrapper" class="jetpack-instant-search-wrapper">
512
		<?php
513
514
		if ( ! empty( $title ) ) {
515
			/**
516
			 * Responsible for displaying the title of the Jetpack Search filters widget.
517
			 *
518
			 * @module search
519
			 *
520
			 * @since  5.7.0
521
			 *
522
			 * @param string $title                The widget's title
523
			 * @param string $args['before_title'] The HTML tag to display before the title
524
			 * @param string $args['after_title']  The HTML tag to display after the title
525
			 */
526
			do_action( 'jetpack_search_render_filters_widget_title', $title, $args['before_title'], $args['after_title'] );
527
		}
528
529
		echo '</div>';
530
		echo $args['after_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
531
	}
532
533
534
	/**
535
	 * Renders JavaScript for the sorting controls on the frontend.
536
	 *
537
	 * This JS is a bit complicated, but here's what it's trying to do:
538
	 * - find the search form
539
	 * - find the orderby/order fields and set default values
540
	 * - detect changes to the sort field, if it exists, and use it to set the order field values
541
	 *
542
	 * @since 5.8.0
543
	 *
544
	 * @param array  $instance The current widget instance.
545
	 * @param string $order    The order to initialize the select with.
546
	 * @param string $orderby  The orderby to initialize the select with.
547
	 */
548
	private function maybe_render_sort_javascript( $instance, $order, $orderby ) {
549
		if ( Jetpack_Search_Options::is_instant_enabled() ) {
550
			return;
551
		}
552
553
		if ( ! empty( $instance['user_sort_enabled'] ) ) :
554
		?>
555
		<script type="text/javascript">
556
			var jetpackSearchModuleSorting = function() {
557
				var orderByDefault = '<?php echo 'date' === $orderby ? 'date' : 'relevance'; ?>',
558
					orderDefault   = '<?php echo 'ASC' === $order ? 'ASC' : 'DESC'; ?>',
559
					widgetId       = decodeURIComponent( '<?php echo rawurlencode( $this->id ); ?>' ),
560
					searchQuery    = decodeURIComponent( '<?php echo rawurlencode( get_query_var( 's', '' ) ); ?>' ),
561
					isSearch       = <?php echo (int) is_search(); ?>;
562
563
				var container = document.getElementById( widgetId + '-wrapper' ),
564
					form = container.querySelector( '.jetpack-search-form form' ),
565
					orderBy = form.querySelector( 'input[name=orderby]' ),
566
					order = form.querySelector( 'input[name=order]' ),
567
					searchInput = form.querySelector( 'input[name="s"]' ),
568
					sortSelectInput = container.querySelector( '.jetpack-search-sort' );
569
570
				orderBy.value = orderByDefault;
571
				order.value = orderDefault;
572
573
				// Some themes don't set the search query, which results in the query being lost
574
				// when doing a sort selection. So, if the query isn't set, let's set it now. This approach
575
				// is chosen over running a regex over HTML for every search query performed.
576
				if ( isSearch && ! searchInput.value ) {
577
					searchInput.value = searchQuery;
578
				}
579
580
				searchInput.classList.add( 'show-placeholder' );
581
582
				sortSelectInput.addEventListener( 'change', function( event ) {
583
					var values  = event.target.value.split( '|' );
584
					orderBy.value = values[0];
585
					order.value = values[1];
586
587
					form.submit();
588
				} );
589
			}
590
591
			if ( document.readyState === 'interactive' || document.readyState === 'complete' ) {
592
				jetpackSearchModuleSorting();
593
			} else {
594
				document.addEventListener( 'DOMContentLoaded', jetpackSearchModuleSorting );
595
			}
596
			</script>
597
		<?php
598
		endif;
599
	}
600
601
	/**
602
	 * Convert a sort string into the separate order by and order parts.
603
	 *
604
	 * @since 5.8.0
605
	 *
606
	 * @param string $sort A sort string.
607
	 *
608
	 * @return array Order by and order.
609
	 */
610
	private function sorting_to_wp_query_param( $sort ) {
611
		$parts   = explode( '|', $sort );
612
		$orderby = isset( $_GET['orderby'] )
613
			? $_GET['orderby']
614
			: $parts[0];
615
616
		$order = isset( $_GET['order'] )
617
			? strtoupper( $_GET['order'] )
618
			: ( ( isset( $parts[1] ) && 'ASC' === strtoupper( $parts[1] ) ) ? 'ASC' : 'DESC' );
619
620
		return array( $orderby, $order );
621
	}
622
623
	/**
624
	 * Checks whether or not the widget exists in the Jetpack Search overlay sidebar.
625
	 *
626
	 * @since 8.6.0
627
	 *
628
	 * @return boolean Whether the widget is within the overlay sidebar.
629
	 */
630
	public function is_within_overlay() {
631
		return in_array(
632
			$this->id,
633
			array_key_exists( 'jetpack-instant-search-sidebar', get_option( 'sidebars_widgets', array() ) ) ?
634
				get_option( 'sidebars_widgets', array() )['jetpack-instant-search-sidebar'] : array(),
635
			true
636
		);
637
	}
638
639
	/**
640
	 * Updates a particular instance of the widget. Validates and sanitizes the options.
641
	 *
642
	 * @since 5.0.0
643
	 *
644
	 * @param array $new_instance New settings for this instance as input by the user via Jetpack_Search_Widget::form().
645
	 * @param array $old_instance Old settings for this instance.
646
	 *
647
	 * @return array Settings to save.
648
	 */
649
	public function update( $new_instance, $old_instance ) {
650
		$instance = array();
651
652
		$instance['title']              = sanitize_text_field( $new_instance['title'] );
653
		$instance['search_box_enabled'] = empty( $new_instance['search_box_enabled'] ) ? '0' : '1';
654
		$instance['user_sort_enabled']  = empty( $new_instance['user_sort_enabled'] ) ? '0' : '1';
655
		$instance['sort']               = $new_instance['sort'];
656
		$instance['post_types']         = empty( $new_instance['post_types'] ) || empty( $instance['search_box_enabled'] )
657
			? array()
658
			: array_map( 'sanitize_key', $new_instance['post_types'] );
659
660
		$filters = array();
661
		if ( isset( $new_instance['filter_type'] ) ) {
662
			foreach ( (array) $new_instance['filter_type'] as $index => $type ) {
663
				$count = intval( $new_instance['num_filters'][ $index ] );
664
				$count = min( 50, $count ); // Set max boundary at 50.
665
				$count = max( 1, $count );  // Set min boundary at 1.
666
667
				switch ( $type ) {
668
					case 'taxonomy':
669
						$filters[] = array(
670
							'name'     => sanitize_text_field( $new_instance['filter_name'][ $index ] ),
671
							'type'     => 'taxonomy',
672
							'taxonomy' => sanitize_key( $new_instance['taxonomy_type'][ $index ] ),
673
							'count'    => $count,
674
						);
675
						break;
676
					case 'post_type':
677
						$filters[] = array(
678
							'name'  => sanitize_text_field( $new_instance['filter_name'][ $index ] ),
679
							'type'  => 'post_type',
680
							'count' => $count,
681
						);
682
						break;
683
					case 'date_histogram':
684
						$filters[] = array(
685
							'name'     => sanitize_text_field( $new_instance['filter_name'][ $index ] ),
686
							'type'     => 'date_histogram',
687
							'count'    => $count,
688
							'field'    => sanitize_key( $new_instance['date_histogram_field'][ $index ] ),
689
							'interval' => sanitize_key( $new_instance['date_histogram_interval'][ $index ] ),
690
						);
691
						break;
692
				}
693
			}
694
		}
695
696
		if ( ! empty( $filters ) ) {
697
			$instance['filters'] = $filters;
698
		}
699
700
		return $instance;
701
	}
702
703
	/**
704
	 * Outputs the settings update form.
705
	 *
706
	 * @since 5.0.0
707
	 *
708
	 * @param array $instance Current settings.
709
	 */
710
	public function form( $instance ) {
711
		$instance = $this->jetpack_search_populate_defaults( $instance );
712
713
		$title = strip_tags( $instance['title'] );
714
715
		$hide_filters = Jetpack_Search_Helpers::are_filters_by_widget_disabled();
716
717
		$classes = sprintf(
718
			'jetpack-search-filters-widget %s %s %s',
719
			$hide_filters ? 'hide-filters' : '',
720
			$instance['search_box_enabled'] ? '' : 'hide-post-types',
721
			$this->id
722
		);
723
		?>
724
		<div class="<?php echo esc_attr( $classes ); ?>">
725
			<p>
726
				<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">
727
					<?php esc_html_e( 'Title (optional):', 'jetpack' ); ?>
728
				</label>
729
				<input
730
					class="widefat"
731
					id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
732
					name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
733
					type="text"
734
					value="<?php echo esc_attr( $title ); ?>"
735
				/>
736
			</p>
737
738
			<?php if ( ! $this->is_within_overlay() ) { ?>
739
				<p>
740
					<label>
741
						<input
742
							type="checkbox"
743
							class="jetpack-search-filters-widget__search-box-enabled"
744
							name="<?php echo esc_attr( $this->get_field_name( 'search_box_enabled' ) ); ?>"
745
							<?php checked( $instance['search_box_enabled'] ); ?>
746
						/>
747
						<?php esc_html_e( 'Show search box', 'jetpack' ); ?>
748
					</label>
749
				</p>
750
751
				<p class="jetpack-search-filters-widget__post-types-select">
752
					<label><?php esc_html_e( 'Post types to search (minimum of 1):', 'jetpack' ); ?></label>
753
					<?php foreach ( get_post_types( array( 'exclude_from_search' => false ), 'objects' ) as $post_type ) : ?>
754
						<label>
755
							<input
756
								type="checkbox"
757
								value="<?php echo esc_attr( $post_type->name ); ?>"
758
								name="<?php echo esc_attr( $this->get_field_name( 'post_types' ) ); ?>[]"
759
								<?php checked( empty( $instance['post_types'] ) || in_array( $post_type->name, $instance['post_types'], true ) ); ?>
760
							/>&nbsp;
761
							<?php echo esc_html( $post_type->label ); ?>
762
						</label>
763
					<?php endforeach; ?>
764
				</p>
765
			<?php } ?>
766
767
			<?php if ( ! $hide_filters ) : ?>
768
				<script class="jetpack-search-filters-widget__filter-template" type="text/template">
769
					<?php echo $this->render_widget_edit_filter( array(), true ); ?>
770
				</script>
771
				<div class="jetpack-search-filters-widget__filters">
772
					<?php foreach ( (array) $instance['filters'] as $filter ) : ?>
773
						<?php $this->render_widget_edit_filter( $filter ); ?>
774
					<?php endforeach; ?>
775
				</div>
776
				<p class="jetpack-search-filters-widget__add-filter-wrapper">
777
					<a class="button jetpack-search-filters-widget__add-filter" href="#">
778
						<?php esc_html_e( 'Add a filter', 'jetpack' ); ?>
779
					</a>
780
				</p>
781
				<noscript>
782
					<p class="jetpack-search-filters-help">
783
						<?php echo esc_html_e( 'Adding filters requires JavaScript!', 'jetpack' ); ?>
784
					</p>
785
				</noscript>
786
				<?php if ( is_customize_preview() ) : ?>
787
					<p class="jetpack-search-filters-help">
788
						<a href="<?php echo esc_url( Redirect::get_url( 'jetpack-support-search', array( 'anchor' => 'filters-not-showing-up' ) ) ); ?>" target="_blank">
789
							<?php esc_html_e( "Why aren't my filters appearing?", 'jetpack' ); ?>
790
						</a>
791
					</p>
792
				<?php endif; ?>
793
			<?php endif; ?>
794
		</div>
795
		<?php
796
	}
797
798
	/**
799
	 * We need to render HTML in two formats: an Underscore template (client-side)
800
	 * and native PHP (server-side). This helper function allows for easy rendering
801
	 * of attributes in both formats.
802
	 *
803
	 * @since 5.8.0
804
	 *
805
	 * @param string $name        Attribute name.
806
	 * @param string $value       Attribute value.
807
	 * @param bool   $is_template Whether this is for an Underscore template or not.
808
	 */
809
	private function render_widget_attr( $name, $value, $is_template ) {
810
		echo $is_template ? "<%= $name %>" : esc_attr( $value );
811
	}
812
813
	/**
814
	 * We need to render HTML in two formats: an Underscore template (client-size)
815
	 * and native PHP (server-side). This helper function allows for easy rendering
816
	 * of the "selected" attribute in both formats.
817
	 *
818
	 * @since 5.8.0
819
	 *
820
	 * @param string $name        Attribute name.
821
	 * @param string $value       Attribute value.
822
	 * @param string $compare     Value to compare to the attribute value to decide if it should be selected.
823
	 * @param bool   $is_template Whether this is for an Underscore template or not.
824
	 */
825
	private function render_widget_option_selected( $name, $value, $compare, $is_template ) {
826
		$compare_js = rawurlencode( $compare );
827
		echo $is_template ? "<%= decodeURIComponent( '$compare_js' ) === $name ? 'selected=\"selected\"' : '' %>" : selected( $value, $compare );
828
	}
829
830
	/**
831
	 * Responsible for rendering a single filter in the customizer or the widget administration screen in wp-admin.
832
	 *
833
	 * We use this method for two purposes - rendering the fields server-side, and also rendering a script template for Underscore.
834
	 *
835
	 * @since 5.7.0
836
	 *
837
	 * @param array $filter      The filter to render.
838
	 * @param bool  $is_template Whether this is for an Underscore template or not.
839
	 */
840
	public function render_widget_edit_filter( $filter, $is_template = false ) {
841
		$args = wp_parse_args(
842
			$filter, array(
0 ignored issues
show
Documentation introduced by
array('name' => '', 'typ...::DEFAULT_FILTER_COUNT) is of type array<string,string|inte...ng","count":"integer"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
843
				'name'      => '',
844
				'type'      => 'taxonomy',
845
				'taxonomy'  => '',
846
				'post_type' => '',
847
				'field'     => '',
848
				'interval'  => '',
849
				'count'     => self::DEFAULT_FILTER_COUNT,
850
			)
851
		);
852
853
		$args['name_placeholder'] = Jetpack_Search_Helpers::generate_widget_filter_name( $args );
854
855
		?>
856
		<div class="jetpack-search-filters-widget__filter is-<?php $this->render_widget_attr( 'type', $args['type'], $is_template ); ?>">
857
			<p class="jetpack-search-filters-widget__type-select">
858
				<label>
859
					<?php esc_html_e( 'Filter Type:', 'jetpack' ); ?>
860
					<select name="<?php echo esc_attr( $this->get_field_name( 'filter_type' ) ); ?>[]" class="widefat filter-select">
861
						<option value="taxonomy" <?php $this->render_widget_option_selected( 'type', $args['type'], 'taxonomy', $is_template ); ?>>
862
							<?php esc_html_e( 'Taxonomy', 'jetpack' ); ?>
863
						</option>
864
						<option value="post_type" <?php $this->render_widget_option_selected( 'type', $args['type'], 'post_type', $is_template ); ?>>
865
							<?php esc_html_e( 'Post Type', 'jetpack' ); ?>
866
						</option>
867
						<option value="date_histogram" <?php $this->render_widget_option_selected( 'type', $args['type'], 'date_histogram', $is_template ); ?>>
868
							<?php esc_html_e( 'Date', 'jetpack' ); ?>
869
						</option>
870
					</select>
871
				</label>
872
			</p>
873
874
			<p class="jetpack-search-filters-widget__taxonomy-select">
875
				<label>
876
					<?php
877
						esc_html_e( 'Choose a taxonomy:', 'jetpack' );
878
						$seen_taxonomy_labels = array();
879
					?>
880
					<select name="<?php echo esc_attr( $this->get_field_name( 'taxonomy_type' ) ); ?>[]" class="widefat taxonomy-select">
881
						<?php foreach ( get_taxonomies( array( 'public' => true ), 'objects' ) as $taxonomy ) : ?>
882
							<option value="<?php echo esc_attr( $taxonomy->name ); ?>" <?php $this->render_widget_option_selected( 'taxonomy', $args['taxonomy'], $taxonomy->name, $is_template ); ?>>
883
								<?php
884
									$label = in_array( $taxonomy->label, $seen_taxonomy_labels )
885
										? sprintf(
886
											/* translators: %1$s is the taxonomy name, %2s is the name of its type to help distinguish between several taxonomies with the same name, e.g. category and tag. */
887
											_x( '%1$s (%2$s)', 'A label for a taxonomy selector option', 'jetpack' ),
888
											$taxonomy->label,
889
											$taxonomy->name
890
										)
891
										: $taxonomy->label;
892
									echo esc_html( $label );
893
									$seen_taxonomy_labels[] = $taxonomy->label;
894
								?>
895
							</option>
896
						<?php endforeach; ?>
897
					</select>
898
				</label>
899
			</p>
900
901
			<p class="jetpack-search-filters-widget__date-histogram-select">
902
				<label>
903
					<?php esc_html_e( 'Choose a field:', 'jetpack' ); ?>
904
					<select name="<?php echo esc_attr( $this->get_field_name( 'date_histogram_field' ) ); ?>[]" class="widefat date-field-select">
905
						<option value="post_date" <?php $this->render_widget_option_selected( 'field', $args['field'], 'post_date', $is_template ); ?>>
906
							<?php esc_html_e( 'Date', 'jetpack' ); ?>
907
						</option>
908
						<option value="post_date_gmt" <?php $this->render_widget_option_selected( 'field', $args['field'], 'post_date_gmt', $is_template ); ?>>
909
							<?php esc_html_e( 'Date GMT', 'jetpack' ); ?>
910
						</option>
911
						<option value="post_modified" <?php $this->render_widget_option_selected( 'field', $args['field'], 'post_modified', $is_template ); ?>>
912
							<?php esc_html_e( 'Modified', 'jetpack' ); ?>
913
						</option>
914
						<option value="post_modified_gmt" <?php $this->render_widget_option_selected( 'field', $args['field'], 'post_modified_gmt', $is_template ); ?>>
915
							<?php esc_html_e( 'Modified GMT', 'jetpack' ); ?>
916
						</option>
917
					</select>
918
				</label>
919
			</p>
920
921
			<p class="jetpack-search-filters-widget__date-histogram-select">
922
				<label>
923
					<?php esc_html_e( 'Choose an interval:' ); ?>
924
					<select name="<?php echo esc_attr( $this->get_field_name( 'date_histogram_interval' ) ); ?>[]" class="widefat date-interval-select">
925
						<option value="month" <?php $this->render_widget_option_selected( 'interval', $args['interval'], 'month', $is_template ); ?>>
926
							<?php esc_html_e( 'Month', 'jetpack' ); ?>
927
						</option>
928
						<option value="year" <?php $this->render_widget_option_selected( 'interval', $args['interval'], 'year', $is_template ); ?>>
929
							<?php esc_html_e( 'Year', 'jetpack' ); ?>
930
						</option>
931
					</select>
932
				</label>
933
			</p>
934
935
			<p class="jetpack-search-filters-widget__title">
936
				<label>
937
					<?php esc_html_e( 'Title:', 'jetpack' ); ?>
938
					<input
939
						class="widefat"
940
						type="text"
941
						name="<?php echo esc_attr( $this->get_field_name( 'filter_name' ) ); ?>[]"
942
						value="<?php $this->render_widget_attr( 'name', $args['name'], $is_template ); ?>"
943
						placeholder="<?php $this->render_widget_attr( 'name_placeholder', $args['name_placeholder'], $is_template ); ?>"
944
					/>
945
				</label>
946
			</p>
947
948
			<p>
949
				<label>
950
					<?php esc_html_e( 'Maximum number of filters (1-50):', 'jetpack' ); ?>
951
					<input
952
						class="widefat filter-count"
953
						name="<?php echo esc_attr( $this->get_field_name( 'num_filters' ) ); ?>[]"
954
						type="number"
955
						value="<?php $this->render_widget_attr( 'count', $args['count'], $is_template ); ?>"
956
						min="1"
957
						max="50"
958
						step="1"
959
						required
960
					/>
961
				</label>
962
			</p>
963
964
			<p class="jetpack-search-filters-widget__controls">
965
				<a href="#" class="delete"><?php esc_html_e( 'Remove', 'jetpack' ); ?></a>
966
			</p>
967
		</div>
968
	<?php
969
	}
970
}
971