Completed
Push — add/search-toggle-sort-control ( 5a13e5...af4439 )
by
unknown
07:11
created

Jetpack_Search_Widget   F

Complexity

Total Complexity 101

Size/Duplication

Total Lines 1043
Duplicated Lines 4.12 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
dl 43
loc 1043
rs 1.428
c 0
b 0
f 0
wmc 101
lcom 1
cbo 8

22 Methods

Rating   Name   Duplication   Size   Complexity  
B __construct() 0 34 6
A is_search_active() 0 3 1
A activate_search() 0 3 1
A widget_admin_setup() 0 45 1
A enqueue_frontend_scripts() 0 15 3
A get_sort_types() 0 7 2
A is_for_current_widget() 0 3 2
A should_display_sitewide_filters() 0 18 5
A jetpack_search_populate_defaults() 0 14 1
A populate_defaults_for_instant_search() 0 11 1
A widget() 0 26 4
F widget_non_instant() 8 98 18
B widget_instant() 3 60 7
A widget_empty_instant() 0 33 4
B maybe_render_sort_javascript() 0 52 5
A sorting_to_wp_query_param() 0 12 5
C update() 0 57 12
C form() 16 117 10
B form_for_instant_search() 16 76 6
A render_widget_attr() 0 3 2
A render_widget_option_selected() 0 4 2
B render_widget_edit_filter() 0 130 3

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Jetpack_Search_Widget often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Jetpack_Search_Widget, and based on these observations, apply Extract Interface, too.

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
	 * Populates the instance array with appropriate default values.
270
	 *
271
	 * @since 8.6.0
272
	 * @param array $instance Previously saved values from database.
273
	 * @return array Instance array with default values approprate for instant search
274
	 */
275
	public function populate_defaults_for_instant_search( $instance ) {
276
		return wp_parse_args(
277
			(array) $instance,
278
			array(
0 ignored issues
show
Documentation introduced by
array('title' => '', 'so...post_types' => array()) is of type array<string,string|arra...,"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...
279
				'title'      => '',
280
				'sort'       => self::DEFAULT_SORT,
281
				'filters'    => array(),
282
				'post_types' => array(),
283
			)
284
		);
285
	}
286
287
	/**
288
	 * Responsible for rendering the widget on the frontend.
289
	 *
290
	 * @since 5.0.0
291
	 *
292
	 * @param array $args     Widgets args supplied by the theme.
293
	 * @param array $instance The current widget instance.
294
	 */
295
	public function widget( $args, $instance ) {
296
		$instance = $this->jetpack_search_populate_defaults( $instance );
297
298
		if ( ( new Status() )->is_development_mode() ) {
299
			echo $args['before_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
300
			?><div id="<?php echo esc_attr( $this->id ); ?>-wrapper">
301
				<div class="jetpack-search-sort-wrapper">
302
					<label>
303
						<?php esc_html_e( 'Jetpack Search not supported in Development Mode', 'jetpack' ); ?>
304
					</label>
305
				</div>
306
			</div><?php
307
			echo $args['after_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
308
			return;
309
		}
310
311
		if ( Jetpack_Search_Options::is_instant_enabled() ) {
312
			if ( 'jetpack-instant-search-sidebar' === $args['id'] ) {
313
				$this->widget_empty_instant( $args, $instance );
314
			} else {
315
				$this->widget_instant( $args, $instance );
316
			}
317
		} else {
318
			$this->widget_non_instant( $args, $instance );
319
		}
320
	}
321
322
	/**
323
	 * Render the non-instant frontend widget.
324
	 *
325
	 * @since 8.3.0
326
	 *
327
	 * @param array $args     Widgets args supplied by the theme.
328
	 * @param array $instance The current widget instance.
329
	 */
330
	public function widget_non_instant( $args, $instance ) {
331
		$display_filters = false;
332
333
		if ( is_search() ) {
334
			if ( Jetpack_Search_Helpers::should_rerun_search_in_customizer_preview() ) {
335
				Jetpack_Search::instance()->update_search_results_aggregations();
336
			}
337
338
			$filters = Jetpack_Search::instance()->get_filters();
339
340 View Code Duplication
			if ( ! Jetpack_Search_Helpers::are_filters_by_widget_disabled() && ! $this->should_display_sitewide_filters() ) {
341
				$filters = array_filter( $filters, array( $this, 'is_for_current_widget' ) );
342
			}
343
344
			if ( ! empty( $filters ) ) {
345
				$display_filters = true;
346
			}
347
		}
348
349
		if ( ! $display_filters && empty( $instance['search_box_enabled'] ) && empty( $instance['user_sort_enabled'] ) ) {
350
			return;
351
		}
352
353
		$title = ! empty( $instance['title'] ) ? $instance['title'] : '';
354
355
		/** This filter is documented in core/src/wp-includes/default-widgets.php */
356
		$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...
357
358
		echo $args['before_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
359
		?>
360
			<div id="<?php echo esc_attr( $this->id ); ?>-wrapper" >
361
		<?php
362
363
		if ( ! empty( $title ) ) {
364
			/**
365
			 * Responsible for displaying the title of the Jetpack Search filters widget.
366
			 *
367
			 * @module search
368
			 *
369
			 * @since  5.7.0
370
			 *
371
			 * @param string $title                The widget's title
372
			 * @param string $args['before_title'] The HTML tag to display before the title
373
			 * @param string $args['after_title']  The HTML tag to display after the title
374
			 */
375
			do_action( 'jetpack_search_render_filters_widget_title', $title, $args['before_title'], $args['after_title'] );
376
		}
377
378
		$default_sort            = isset( $instance['sort'] ) ? $instance['sort'] : self::DEFAULT_SORT;
379
		list( $orderby, $order ) = $this->sorting_to_wp_query_param( $default_sort );
380
		$current_sort            = "{$orderby}|{$order}";
381
382
		// we need to dynamically inject the sort field into the search box when the search box is enabled, and display
383
		// it separately when it's not.
384
		if ( ! empty( $instance['search_box_enabled'] ) ) {
385
			Jetpack_Search_Template_Tags::render_widget_search_form( $instance['post_types'], $orderby, $order );
386
		}
387
388
		if ( ! empty( $instance['search_box_enabled'] ) && ! empty( $instance['user_sort_enabled'] ) ) :
389
				?>
390
					<div class="jetpack-search-sort-wrapper">
391
				<label>
392
					<?php esc_html_e( 'Sort by', 'jetpack' ); ?>
393
					<select class="jetpack-search-sort">
394 View Code Duplication
						<?php foreach ( $this->get_sort_types() as $sort => $label ) { ?>
395
							<option value="<?php echo esc_attr( $sort ); ?>" <?php selected( $current_sort, $sort ); ?>>
396
								<?php echo esc_html( $label ); ?>
397
							</option>
398
						<?php } ?>
399
					</select>
400
				</label>
401
			</div>
402
		<?php
403
		endif;
404
405
		if ( $display_filters ) {
406
			/**
407
			 * Responsible for rendering filters to narrow down search results.
408
			 *
409
			 * @module search
410
			 *
411
			 * @since  5.8.0
412
			 *
413
			 * @param array $filters    The possible filters for the current query.
414
			 * @param array $post_types An array of post types to limit filtering to.
415
			 */
416
			do_action(
417
				'jetpack_search_render_filters',
418
				$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...
419
				isset( $instance['post_types'] ) ? $instance['post_types'] : null
420
			);
421
		}
422
423
		$this->maybe_render_sort_javascript( $instance, $order, $orderby );
424
425
		echo '</div>';
426
		echo $args['after_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
427
	}
428
429
	/**
430
	 * Render the instant frontend widget.
431
	 *
432
	 * @since 8.3.0
433
	 *
434
	 * @param array $args     Widgets args supplied by the theme.
435
	 * @param array $instance The current widget instance.
436
	 */
437
	public function widget_instant( $args, $instance ) {
438
		if ( Jetpack_Search_Helpers::should_rerun_search_in_customizer_preview() ) {
439
			Jetpack_Search::instance()->update_search_results_aggregations();
440
		}
441
442
		$filters = Jetpack_Search::instance()->get_filters();
443 View Code Duplication
		if ( ! Jetpack_Search_Helpers::are_filters_by_widget_disabled() && ! $this->should_display_sitewide_filters() ) {
444
			$filters = array_filter( $filters, array( $this, 'is_for_current_widget' ) );
445
		}
446
447
		$display_filters = ! empty( $filters );
448
449
		$title = ! empty( $instance['title'] ) ? $instance['title'] : '';
450
451
		/** This filter is documented in core/src/wp-includes/default-widgets.php */
452
		$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...
453
454
		echo $args['before_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
455
		?>
456
			<div id="<?php echo esc_attr( $this->id ); ?>-wrapper" class="jetpack-instant-search-wrapper">
457
		<?php
458
459
		if ( ! empty( $title ) ) {
460
			/**
461
			 * Responsible for displaying the title of the Jetpack Search filters widget.
462
			 *
463
			 * @module search
464
			 *
465
			 * @since  5.7.0
466
			 *
467
			 * @param string $title                The widget's title
468
			 * @param string $args['before_title'] The HTML tag to display before the title
469
			 * @param string $args['after_title']  The HTML tag to display after the title
470
			 */
471
			do_action( 'jetpack_search_render_filters_widget_title', $title, $args['before_title'], $args['after_title'] );
472
		}
473
474
		Jetpack_Search_Template_Tags::render_widget_search_form( array(), '', '' );
475
476
		if ( $display_filters ) {
477
			/**
478
			 * Responsible for rendering filters to narrow down search results.
479
			 *
480
			 * @module search
481
			 *
482
			 * @since  5.8.0
483
			 *
484
			 * @param array $filters    The possible filters for the current query.
485
			 * @param array $post_types An array of post types to limit filtering to.
486
			 */
487
			do_action(
488
				'jetpack_search_render_filters',
489
				$filters,
490
				null
491
			);
492
		}
493
494
		echo '</div>';
495
		echo $args['after_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
496
	}
497
498
	/**
499
	 * Render the instant widget for the overlay.
500
	 *
501
	 * @since 8.3.0
502
	 *
503
	 * @param array $args     Widgets args supplied by the theme.
504
	 * @param array $instance The current widget instance.
505
	 */
506
	public function widget_empty_instant( $args, $instance ) {
507
		$title = isset( $instance['title'] ) ? $instance['title'] : '';
508
509
		if ( empty( $title ) ) {
510
			$title = '';
511
		}
512
513
		/** This filter is documented in core/src/wp-includes/default-widgets.php */
514
		$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...
515
516
		echo $args['before_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
517
		?>
518
			<div id="<?php echo esc_attr( $this->id ); ?>-wrapper" class="jetpack-instant-search-wrapper">
519
		<?php
520
521
		if ( ! empty( $title ) ) {
522
			/**
523
			 * Responsible for displaying the title of the Jetpack Search filters widget.
524
			 *
525
			 * @module search
526
			 *
527
			 * @since  5.7.0
528
			 *
529
			 * @param string $title                The widget's title
530
			 * @param string $args['before_title'] The HTML tag to display before the title
531
			 * @param string $args['after_title']  The HTML tag to display after the title
532
			 */
533
			do_action( 'jetpack_search_render_filters_widget_title', $title, $args['before_title'], $args['after_title'] );
534
		}
535
536
		echo '</div>';
537
		echo $args['after_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
538
	}
539
540
541
	/**
542
	 * Renders JavaScript for the sorting controls on the frontend.
543
	 *
544
	 * This JS is a bit complicated, but here's what it's trying to do:
545
	 * - find the search form
546
	 * - find the orderby/order fields and set default values
547
	 * - detect changes to the sort field, if it exists, and use it to set the order field values
548
	 *
549
	 * @since 5.8.0
550
	 *
551
	 * @param array  $instance The current widget instance.
552
	 * @param string $order    The order to initialize the select with.
553
	 * @param string $orderby  The orderby to initialize the select with.
554
	 */
555
	private function maybe_render_sort_javascript( $instance, $order, $orderby ) {
556
		if ( Jetpack_Search_Options::is_instant_enabled() ) {
557
			return;
558
		}
559
560
		if ( ! empty( $instance['user_sort_enabled'] ) ) :
561
		?>
562
		<script type="text/javascript">
563
			var jetpackSearchModuleSorting = function() {
564
				var orderByDefault = '<?php echo 'date' === $orderby ? 'date' : 'relevance'; ?>',
565
					orderDefault   = '<?php echo 'ASC' === $order ? 'ASC' : 'DESC'; ?>',
566
					widgetId       = decodeURIComponent( '<?php echo rawurlencode( $this->id ); ?>' ),
567
					searchQuery    = decodeURIComponent( '<?php echo rawurlencode( get_query_var( 's', '' ) ); ?>' ),
568
					isSearch       = <?php echo (int) is_search(); ?>;
569
570
				var container = document.getElementById( widgetId + '-wrapper' ),
571
					form = container.querySelector( '.jetpack-search-form form' ),
572
					orderBy = form.querySelector( 'input[name=orderby]' ),
573
					order = form.querySelector( 'input[name=order]' ),
574
					searchInput = form.querySelector( 'input[name="s"]' ),
575
					sortSelectInput = container.querySelector( '.jetpack-search-sort' );
576
577
				orderBy.value = orderByDefault;
578
				order.value = orderDefault;
579
580
				// Some themes don't set the search query, which results in the query being lost
581
				// when doing a sort selection. So, if the query isn't set, let's set it now. This approach
582
				// is chosen over running a regex over HTML for every search query performed.
583
				if ( isSearch && ! searchInput.value ) {
584
					searchInput.value = searchQuery;
585
				}
586
587
				searchInput.classList.add( 'show-placeholder' );
588
589
				sortSelectInput.addEventListener( 'change', function( event ) {
590
					var values  = event.target.value.split( '|' );
591
					orderBy.value = values[0];
592
					order.value = values[1];
593
594
					form.submit();
595
				} );
596
			}
597
598
			if ( document.readyState === 'interactive' || document.readyState === 'complete' ) {
599
				jetpackSearchModuleSorting();
600
			} else {
601
				document.addEventListener( 'DOMContentLoaded', jetpackSearchModuleSorting );
602
			}
603
			</script>
604
		<?php
605
		endif;
606
	}
607
608
	/**
609
	 * Convert a sort string into the separate order by and order parts.
610
	 *
611
	 * @since 5.8.0
612
	 *
613
	 * @param string $sort A sort string.
614
	 *
615
	 * @return array Order by and order.
616
	 */
617
	private function sorting_to_wp_query_param( $sort ) {
618
		$parts   = explode( '|', $sort );
619
		$orderby = isset( $_GET['orderby'] )
620
			? $_GET['orderby']
621
			: $parts[0];
622
623
		$order = isset( $_GET['order'] )
624
			? strtoupper( $_GET['order'] )
625
			: ( ( isset( $parts[1] ) && 'ASC' === strtoupper( $parts[1] ) ) ? 'ASC' : 'DESC' );
626
627
		return array( $orderby, $order );
628
	}
629
630
	/**
631
	 * Updates a particular instance of the widget. Validates and sanitizes the options.
632
	 *
633
	 * @since 5.0.0
634
	 *
635
	 * @param array $new_instance New settings for this instance as input by the user via Jetpack_Search_Widget::form().
636
	 * @param array $old_instance Old settings for this instance.
637
	 *
638
	 * @return array Settings to save.
639
	 */
640
	public function update( $new_instance, $old_instance ) {
641
		$instance = array();
642
643
		$instance['title']      = sanitize_text_field( $new_instance['title'] );
644
		$instance['sort']       = $new_instance['sort'];
645
		$instance['post_types'] = empty( $new_instance['post_types'] )
646
			? array()
647
			: array_map( 'sanitize_key', $new_instance['post_types'] );
648
649
		if ( ! Jetpack_Search_Options::is_instant_enabled() ) {
650
			$instance['search_box_enabled'] = empty( $new_instance['search_box_enabled'] ) ? '0' : '1';
651
			$instance['user_sort_enabled']  = empty( $new_instance['user_sort_enabled'] ) ? '0' : '1';
652
			$instance['post_types']         = empty( $instance['search_box_enabled'] ) ? array() : $instance['post_types'];
653
		}
654
655
		$filters = array();
656
		if ( isset( $new_instance['filter_type'] ) ) {
657
			foreach ( (array) $new_instance['filter_type'] as $index => $type ) {
658
				$count = intval( $new_instance['num_filters'][ $index ] );
659
				$count = min( 50, $count ); // Set max boundary at 50.
660
				$count = max( 1, $count );  // Set min boundary at 1.
661
662
				switch ( $type ) {
663
					case 'taxonomy':
664
						$filters[] = array(
665
							'name'     => sanitize_text_field( $new_instance['filter_name'][ $index ] ),
666
							'type'     => 'taxonomy',
667
							'taxonomy' => sanitize_key( $new_instance['taxonomy_type'][ $index ] ),
668
							'count'    => $count,
669
						);
670
						break;
671
					case 'post_type':
672
						$filters[] = array(
673
							'name'  => sanitize_text_field( $new_instance['filter_name'][ $index ] ),
674
							'type'  => 'post_type',
675
							'count' => $count,
676
						);
677
						break;
678
					case 'date_histogram':
679
						$filters[] = array(
680
							'name'     => sanitize_text_field( $new_instance['filter_name'][ $index ] ),
681
							'type'     => 'date_histogram',
682
							'count'    => $count,
683
							'field'    => sanitize_key( $new_instance['date_histogram_field'][ $index ] ),
684
							'interval' => sanitize_key( $new_instance['date_histogram_interval'][ $index ] ),
685
						);
686
						break;
687
				}
688
			}
689
		}
690
691
		if ( ! empty( $filters ) ) {
692
			$instance['filters'] = $filters;
693
		}
694
695
		return $instance;
696
	}
697
698
	/**
699
	 * Outputs the settings update form.
700
	 *
701
	 * @since 5.0.0
702
	 *
703
	 * @param array $instance Previously saved values from database.
704
	 */
705
	public function form( $instance ) {
706
		if ( Jetpack_Search_Options::is_instant_enabled() ) {
707
			return $this->form_for_instant_search( $instance );
708
		}
709
710
		$instance = $this->jetpack_search_populate_defaults( $instance );
711
712
		$title = strip_tags( $instance['title'] );
713
714
		$hide_filters = Jetpack_Search_Helpers::are_filters_by_widget_disabled();
715
716
		$classes = sprintf(
717
			'jetpack-search-filters-widget %s %s %s',
718
			$hide_filters ? 'hide-filters' : '',
719
			$instance['search_box_enabled'] ? '' : 'hide-post-types',
720
			$this->id
721
		);
722
		?>
723
		<div class="<?php echo esc_attr( $classes ); ?>">
724
			<p>
725
				<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">
726
					<?php esc_html_e( 'Title (optional):', 'jetpack' ); ?>
727
				</label>
728
				<input
729
					class="widefat"
730
					id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
731
					name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
732
					type="text"
733
					value="<?php echo esc_attr( $title ); ?>"
734
				/>
735
			</p>
736
737
			<p>
738
				<label>
739
					<input
740
						type="checkbox"
741
						class="jetpack-search-filters-widget__search-box-enabled"
742
						name="<?php echo esc_attr( $this->get_field_name( 'search_box_enabled' ) ); ?>"
743
						<?php checked( $instance['search_box_enabled'] ); ?>
744
					/>
745
					<?php esc_html_e( 'Show search box', 'jetpack' ); ?>
746
				</label>
747
			</p>
748
749
			<p>
750
				<label>
751
					<input
752
						type="checkbox"
753
						class="jetpack-search-filters-widget__sort-controls-enabled"
754
						name="<?php echo esc_attr( $this->get_field_name( 'user_sort_enabled' ) ); ?>"
755
						<?php checked( $instance['user_sort_enabled'] ); ?>
756
						<?php disabled( ! $instance['search_box_enabled'] ); ?>
757
					/>
758
					<?php esc_html_e( 'Show sort selection dropdown', 'jetpack' ); ?>
759
				</label>
760
			</p>
761
762
			<p class="jetpack-search-filters-widget__post-types-select">
763
				<label><?php esc_html_e( 'Post types to search (minimum of 1):', 'jetpack' ); ?></label>
764 View Code Duplication
				<?php foreach ( get_post_types( array( 'exclude_from_search' => false ), 'objects' ) as $post_type ) : ?>
765
					<label>
766
						<input
767
							type="checkbox"
768
							value="<?php echo esc_attr( $post_type->name ); ?>"
769
							name="<?php echo esc_attr( $this->get_field_name( 'post_types' ) ); ?>[]"
770
							<?php checked( empty( $instance['post_types'] ) || in_array( $post_type->name, $instance['post_types'] ) ); ?>
771
						/>&nbsp;
772
						<?php echo esc_html( $post_type->label ); ?>
773
					</label>
774
				<?php endforeach; ?>
775
			</p>
776
777
			<p>
778
				<label>
779
					<?php esc_html_e( 'Default sort order:', 'jetpack' ); ?>
780
					<select
781
						name="<?php echo esc_attr( $this->get_field_name( 'sort' ) ); ?>"
782
						class="widefat jetpack-search-filters-widget__sort-order">
783 View Code Duplication
						<?php foreach ( $this->get_sort_types() as $sort_type => $label ) { ?>
784
							<option value="<?php echo esc_attr( $sort_type ); ?>" <?php selected( $instance['sort'], $sort_type ); ?>>
785
								<?php echo esc_html( $label ); ?>
786
							</option>
787
						<?php } ?>
788
					</select>
789
				</label>
790
			</p>
791
792
			<?php if ( ! $hide_filters ) : ?>
793
				<script class="jetpack-search-filters-widget__filter-template" type="text/template">
794
					<?php echo $this->render_widget_edit_filter( array(), true ); ?>
795
				</script>
796
				<div class="jetpack-search-filters-widget__filters">
797
					<?php foreach ( (array) $instance['filters'] as $filter ) : ?>
798
						<?php $this->render_widget_edit_filter( $filter ); ?>
799
					<?php endforeach; ?>
800
				</div>
801
				<p class="jetpack-search-filters-widget__add-filter-wrapper">
802
					<a class="button jetpack-search-filters-widget__add-filter" href="#">
803
						<?php esc_html_e( 'Add a filter', 'jetpack' ); ?>
804
					</a>
805
				</p>
806
				<noscript>
807
					<p class="jetpack-search-filters-help">
808
						<?php echo esc_html_e( 'Adding filters requires JavaScript!', 'jetpack' ); ?>
809
					</p>
810
				</noscript>
811
				<?php if ( is_customize_preview() ) : ?>
812
					<p class="jetpack-search-filters-help">
813
						<a href="<?php echo esc_url( Redirect::get_url( 'jetpack-support-search', array( 'anchor' => 'filters-not-showing-up' ) ) ); ?>" target="_blank">
814
							<?php esc_html_e( "Why aren't my filters appearing?", 'jetpack' ); ?>
815
						</a>
816
					</p>
817
				<?php endif; ?>
818
			<?php endif; ?>
819
		</div>
820
		<?php
821
	}
822
823
	/**
824
	 * Outputs the widget update form to be used in the Customizer for Instant Search.
825
	 *
826
	 * @since 8.6.0
827
	 *
828
	 * @param array $instance Previously saved values from database.
829
	 */
830
	private function form_for_instant_search( $instance ) {
831
		$instance = $this->populate_defaults_for_instant_search( $instance );
832
		$classes  = sprintf( 'jetpack-search-filters-widget %s %s', $this->id );
833
834
		?>
835
		<div class="<?php echo esc_attr( $classes ); ?>">
836
			<!-- Title control -->
837
			<p>
838
				<label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">
839
					<?php esc_html_e( 'Title (optional):', 'jetpack' ); ?>
840
				</label>
841
				<input
842
					class="widefat"
843
					id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
844
					name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
845
					type="text"
846
					value="<?php echo esc_attr( wp_strip_all_tags( $instance['title'] ) ); ?>"
847
				/>
848
			</p>
849
850
			<!-- Post types control -->
851
			<p class="jetpack-search-filters-widget__post-types-select">
852
				<label><?php esc_html_e( 'Post types to search (minimum of 1):', 'jetpack' ); ?></label>
853 View Code Duplication
				<?php foreach ( get_post_types( array( 'exclude_from_search' => false ), 'objects' ) as $post_type ) : ?>
854
					<label>
855
						<input
856
							type="checkbox"
857
							value="<?php echo esc_attr( $post_type->name ); ?>"
858
							name="<?php echo esc_attr( $this->get_field_name( 'post_types' ) ); ?>[]"
859
							<?php checked( empty( $instance['post_types'] ) || in_array( $post_type->name, $instance['post_types'], true ) ); ?>
860
						/>&nbsp;
861
						<?php echo esc_html( $post_type->label ); ?>
862
					</label>
863
				<?php endforeach; ?>
864
			</p>
865
866
			<!-- Default sort order control -->
867
			<p>
868
				<label>
869
					<?php esc_html_e( 'Default sort order:', 'jetpack' ); ?>
870
					<select
871
						name="<?php echo esc_attr( $this->get_field_name( 'sort' ) ); ?>"
872
						class="widefat jetpack-search-filters-widget__sort-order">
873 View Code Duplication
						<?php foreach ( $this->get_sort_types() as $sort_type => $label ) { ?>
874
							<option value="<?php echo esc_attr( $sort_type ); ?>" <?php selected( $instance['sort'], $sort_type ); ?>>
875
								<?php echo esc_html( $label ); ?>
876
							</option>
877
						<?php } ?>
878
					</select>
879
				</label>
880
			</p>
881
882
			<!-- Filters control -->
883
			<?php if ( ! Jetpack_Search_Helpers::are_filters_by_widget_disabled() ) : ?>
884
				<div class="jetpack-search-filters-widget__filters">
885
					<?php foreach ( (array) $instance['filters'] as $filter ) : ?>
886
						<?php $this->render_widget_edit_filter( $filter ); ?>
887
					<?php endforeach; ?>
888
				</div>
889
				<p class="jetpack-search-filters-widget__add-filter-wrapper">
890
					<a class="button jetpack-search-filters-widget__add-filter" href="#">
891
						<?php esc_html_e( 'Add a filter', 'jetpack' ); ?>
892
					</a>
893
				</p>
894
				<script class="jetpack-search-filters-widget__filter-template" type="text/template">
895
					<?php $this->render_widget_edit_filter( array(), true ); ?>
896
				</script>
897
				<noscript>
898
					<p class="jetpack-search-filters-help">
899
						<?php echo esc_html_e( 'Adding filters requires JavaScript!', 'jetpack' ); ?>
900
					</p>
901
				</noscript>
902
			<?php endif; ?>
903
		</div>
904
		<?php
905
	}
906
907
	/**
908
	 * We need to render HTML in two formats: an Underscore template (client-side)
909
	 * and native PHP (server-side). This helper function allows for easy rendering
910
	 * of attributes in both formats.
911
	 *
912
	 * @since 5.8.0
913
	 *
914
	 * @param string $name        Attribute name.
915
	 * @param string $value       Attribute value.
916
	 * @param bool   $is_template Whether this is for an Underscore template or not.
917
	 */
918
	private function render_widget_attr( $name, $value, $is_template ) {
919
		echo $is_template ? "<%= $name %>" : esc_attr( $value );
920
	}
921
922
	/**
923
	 * We need to render HTML in two formats: an Underscore template (client-size)
924
	 * and native PHP (server-side). This helper function allows for easy rendering
925
	 * of the "selected" attribute in both formats.
926
	 *
927
	 * @since 5.8.0
928
	 *
929
	 * @param string $name        Attribute name.
930
	 * @param string $value       Attribute value.
931
	 * @param string $compare     Value to compare to the attribute value to decide if it should be selected.
932
	 * @param bool   $is_template Whether this is for an Underscore template or not.
933
	 */
934
	private function render_widget_option_selected( $name, $value, $compare, $is_template ) {
935
		$compare_js = rawurlencode( $compare );
936
		echo $is_template ? "<%= decodeURIComponent( '$compare_js' ) === $name ? 'selected=\"selected\"' : '' %>" : selected( $value, $compare );
937
	}
938
939
	/**
940
	 * Responsible for rendering a single filter in the customizer or the widget administration screen in wp-admin.
941
	 *
942
	 * We use this method for two purposes - rendering the fields server-side, and also rendering a script template for Underscore.
943
	 *
944
	 * @since 5.7.0
945
	 *
946
	 * @param array $filter      The filter to render.
947
	 * @param bool  $is_template Whether this is for an Underscore template or not.
948
	 */
949
	public function render_widget_edit_filter( $filter, $is_template = false ) {
950
		$args = wp_parse_args(
951
			$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...
952
				'name'      => '',
953
				'type'      => 'taxonomy',
954
				'taxonomy'  => '',
955
				'post_type' => '',
956
				'field'     => '',
957
				'interval'  => '',
958
				'count'     => self::DEFAULT_FILTER_COUNT,
959
			)
960
		);
961
962
		$args['name_placeholder'] = Jetpack_Search_Helpers::generate_widget_filter_name( $args );
963
964
		?>
965
		<div class="jetpack-search-filters-widget__filter is-<?php $this->render_widget_attr( 'type', $args['type'], $is_template ); ?>">
966
			<p class="jetpack-search-filters-widget__type-select">
967
				<label>
968
					<?php esc_html_e( 'Filter Type:', 'jetpack' ); ?>
969
					<select name="<?php echo esc_attr( $this->get_field_name( 'filter_type' ) ); ?>[]" class="widefat filter-select">
970
						<option value="taxonomy" <?php $this->render_widget_option_selected( 'type', $args['type'], 'taxonomy', $is_template ); ?>>
971
							<?php esc_html_e( 'Taxonomy', 'jetpack' ); ?>
972
						</option>
973
						<option value="post_type" <?php $this->render_widget_option_selected( 'type', $args['type'], 'post_type', $is_template ); ?>>
974
							<?php esc_html_e( 'Post Type', 'jetpack' ); ?>
975
						</option>
976
						<option value="date_histogram" <?php $this->render_widget_option_selected( 'type', $args['type'], 'date_histogram', $is_template ); ?>>
977
							<?php esc_html_e( 'Date', 'jetpack' ); ?>
978
						</option>
979
					</select>
980
				</label>
981
			</p>
982
983
			<p class="jetpack-search-filters-widget__taxonomy-select">
984
				<label>
985
					<?php
986
						esc_html_e( 'Choose a taxonomy:', 'jetpack' );
987
						$seen_taxonomy_labels = array();
988
					?>
989
					<select name="<?php echo esc_attr( $this->get_field_name( 'taxonomy_type' ) ); ?>[]" class="widefat taxonomy-select">
990
						<?php foreach ( get_taxonomies( array( 'public' => true ), 'objects' ) as $taxonomy ) : ?>
991
							<option value="<?php echo esc_attr( $taxonomy->name ); ?>" <?php $this->render_widget_option_selected( 'taxonomy', $args['taxonomy'], $taxonomy->name, $is_template ); ?>>
992
								<?php
993
									$label = in_array( $taxonomy->label, $seen_taxonomy_labels )
994
										? sprintf(
995
											/* 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. */
996
											_x( '%1$s (%2$s)', 'A label for a taxonomy selector option', 'jetpack' ),
997
											$taxonomy->label,
998
											$taxonomy->name
999
										)
1000
										: $taxonomy->label;
1001
									echo esc_html( $label );
1002
									$seen_taxonomy_labels[] = $taxonomy->label;
1003
								?>
1004
							</option>
1005
						<?php endforeach; ?>
1006
					</select>
1007
				</label>
1008
			</p>
1009
1010
			<p class="jetpack-search-filters-widget__date-histogram-select">
1011
				<label>
1012
					<?php esc_html_e( 'Choose a field:', 'jetpack' ); ?>
1013
					<select name="<?php echo esc_attr( $this->get_field_name( 'date_histogram_field' ) ); ?>[]" class="widefat date-field-select">
1014
						<option value="post_date" <?php $this->render_widget_option_selected( 'field', $args['field'], 'post_date', $is_template ); ?>>
1015
							<?php esc_html_e( 'Date', 'jetpack' ); ?>
1016
						</option>
1017
						<option value="post_date_gmt" <?php $this->render_widget_option_selected( 'field', $args['field'], 'post_date_gmt', $is_template ); ?>>
1018
							<?php esc_html_e( 'Date GMT', 'jetpack' ); ?>
1019
						</option>
1020
						<option value="post_modified" <?php $this->render_widget_option_selected( 'field', $args['field'], 'post_modified', $is_template ); ?>>
1021
							<?php esc_html_e( 'Modified', 'jetpack' ); ?>
1022
						</option>
1023
						<option value="post_modified_gmt" <?php $this->render_widget_option_selected( 'field', $args['field'], 'post_modified_gmt', $is_template ); ?>>
1024
							<?php esc_html_e( 'Modified GMT', 'jetpack' ); ?>
1025
						</option>
1026
					</select>
1027
				</label>
1028
			</p>
1029
1030
			<p class="jetpack-search-filters-widget__date-histogram-select">
1031
				<label>
1032
					<?php esc_html_e( 'Choose an interval:', 'jetpack' ); ?>
1033
					<select name="<?php echo esc_attr( $this->get_field_name( 'date_histogram_interval' ) ); ?>[]" class="widefat date-interval-select">
1034
						<option value="month" <?php $this->render_widget_option_selected( 'interval', $args['interval'], 'month', $is_template ); ?>>
1035
							<?php esc_html_e( 'Month', 'jetpack' ); ?>
1036
						</option>
1037
						<option value="year" <?php $this->render_widget_option_selected( 'interval', $args['interval'], 'year', $is_template ); ?>>
1038
							<?php esc_html_e( 'Year', 'jetpack' ); ?>
1039
						</option>
1040
					</select>
1041
				</label>
1042
			</p>
1043
1044
			<p class="jetpack-search-filters-widget__title">
1045
				<label>
1046
					<?php esc_html_e( 'Title:', 'jetpack' ); ?>
1047
					<input
1048
						class="widefat"
1049
						type="text"
1050
						name="<?php echo esc_attr( $this->get_field_name( 'filter_name' ) ); ?>[]"
1051
						value="<?php $this->render_widget_attr( 'name', $args['name'], $is_template ); ?>"
1052
						placeholder="<?php $this->render_widget_attr( 'name_placeholder', $args['name_placeholder'], $is_template ); ?>"
1053
					/>
1054
				</label>
1055
			</p>
1056
1057
			<p>
1058
				<label>
1059
					<?php esc_html_e( 'Maximum number of filters (1-50):', 'jetpack' ); ?>
1060
					<input
1061
						class="widefat filter-count"
1062
						name="<?php echo esc_attr( $this->get_field_name( 'num_filters' ) ); ?>[]"
1063
						type="number"
1064
						value="<?php $this->render_widget_attr( 'count', $args['count'], $is_template ); ?>"
1065
						min="1"
1066
						max="50"
1067
						step="1"
1068
						required
1069
					/>
1070
				</label>
1071
			</p>
1072
1073
			<p class="jetpack-search-filters-widget__controls">
1074
				<a href="#" class="delete"><?php esc_html_e( 'Remove', 'jetpack' ); ?></a>
1075
			</p>
1076
		</div>
1077
	<?php
1078
	}
1079
}
1080