PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 8.2.6
Jetpack – WP Security, Backup, Speed, & Growth v8.2.6
16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 All 501 releases
jetpack / modules / widgets / search.php
search.php
827 lines 27.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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
13 add_action( 'widgets_init', 'jetpack_search_widget_init' );
14
15 function jetpack_search_widget_init() {
16 if (
17 ! Jetpack::is_active()
18 || ( method_exists( 'Jetpack_Plan', 'supports' ) && ! Jetpack_Plan::supports( 'search' ) )
19 ) {
20 return;
21 }
22
23 require_once JETPACK__PLUGIN_DIR . 'modules/search/class.jetpack-search-helpers.php';
24
25 register_widget( 'Jetpack_Search_Widget' );
26 }
27
28 /**
29 * Provides a widget to show available/selected filters on searches.
30 *
31 * @since 5.0.0
32 *
33 * @see WP_Widget
34 */
35 class Jetpack_Search_Widget extends WP_Widget {
36
37 /**
38 * The Jetpack_Search instance.
39 *
40 * @since 5.7.0
41 * @var Jetpack_Search
42 */
43 protected $jetpack_search;
44
45 /**
46 * Number of aggregations (filters) to show by default.
47 *
48 * @since 5.8.0
49 * @var int
50 */
51 const DEFAULT_FILTER_COUNT = 5;
52
53 /**
54 * Default sort order for search results.
55 *
56 * @since 5.8.0
57 * @var string
58 */
59 const DEFAULT_SORT = 'relevance_desc';
60
61 /**
62 * Jetpack_Search_Widget constructor.
63 *
64 * @since 5.0.0
65 */
66 public function __construct( $name = null ) {
67 if ( empty( $name ) ) {
68 $name = esc_html__( 'Search', 'jetpack' );
69 }
70 parent::__construct(
71 Jetpack_Search_Helpers::FILTER_WIDGET_BASE,
72 /** This filter is documented in modules/widgets/facebook-likebox.php */
73 apply_filters( 'jetpack_widget_name', $name ),
74 array(
75 'classname' => 'jetpack-filters widget_search',
76 'description' => __( 'Replaces the default search with an Elasticsearch-powered search interface and filters.', 'jetpack' ),
77 )
78 );
79
80 if (
81 Jetpack_Search_Helpers::is_active_widget( $this->id ) &&
82 ! $this->is_search_active()
83 ) {
84 $this->activate_search();
85 }
86
87 if ( is_admin() ) {
88 add_action( 'sidebar_admin_setup', array( $this, 'widget_admin_setup' ) );
89 } else {
90 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_scripts' ) );
91 }
92
93 add_action( 'jetpack_search_render_filters_widget_title', array( 'Jetpack_Search_Template_Tags', 'render_widget_title' ), 10, 3 );
94 add_action( 'jetpack_search_render_filters', array( 'Jetpack_Search_Template_Tags', 'render_available_filters' ), 10, 2 );
95 }
96
97 /**
98 * Check whether search is currently active
99 *
100 * @since 6.3
101 */
102 public function is_search_active() {
103 return Jetpack::is_module_active( 'search' );
104 }
105
106 /**
107 * Activate search
108 *
109 * @since 6.3
110 */
111 public function activate_search() {
112 Jetpack::activate_module( 'search', false, false );
113 }
114
115
116 /**
117 * Enqueues the scripts and styles needed for the customizer.
118 *
119 * @since 5.7.0
120 */
121 public function widget_admin_setup() {
122 wp_enqueue_style( 'widget-jetpack-search-filters', plugins_url( 'search/css/search-widget-admin-ui.css', __FILE__ ) );
123
124 // Required for Tracks
125 wp_register_script(
126 'jp-tracks',
127 '//stats.wp.com/w.js',
128 array(),
129 gmdate( 'YW' ),
130 true
131 );
132
133 wp_register_script(
134 'jp-tracks-functions',
135 plugins_url( '_inc/lib/tracks/tracks-callables.js', JETPACK__PLUGIN_FILE ),
136 array(),
137 JETPACK__VERSION,
138 false
139 );
140
141 wp_register_script(
142 'jetpack-search-widget-admin',
143 plugins_url( 'search/js/search-widget-admin.js', __FILE__ ),
144 array( 'jquery', 'jquery-ui-sortable', 'jp-tracks', 'jp-tracks-functions' ),
145 JETPACK__VERSION
146 );
147
148 wp_localize_script(
149 'jetpack-search-widget-admin', 'jetpack_search_filter_admin', array(
150 'defaultFilterCount' => self::DEFAULT_FILTER_COUNT,
151 'tracksUserData' => Jetpack_Tracks_Client::get_connected_user_tracks_identity(),
152 'tracksEventData' => array(
153 'is_customizer' => (int) is_customize_preview(),
154 ),
155 'i18n' => array(
156 'month' => Jetpack_Search_Helpers::get_date_filter_type_name( 'month', false ),
157 'year' => Jetpack_Search_Helpers::get_date_filter_type_name( 'year', false ),
158 'monthUpdated' => Jetpack_Search_Helpers::get_date_filter_type_name( 'month', true ),
159 'yearUpdated' => Jetpack_Search_Helpers::get_date_filter_type_name( 'year', true ),
160 ),
161 )
162 );
163
164 wp_enqueue_script( 'jetpack-search-widget-admin' );
165 }
166
167 /**
168 * Enqueue scripts and styles for the frontend.
169 *
170 * @since 5.8.0
171 */
172 public function enqueue_frontend_scripts() {
173 if ( ! is_active_widget( false, false, $this->id_base, true ) || Constants::is_true( 'JETPACK_SEARCH_PROTOTYPE' ) ) {
174 return;
175 }
176
177 wp_enqueue_script(
178 'jetpack-search-widget',
179 plugins_url( 'search/js/search-widget.js', __FILE__ ),
180 array( 'jquery' ),
181 JETPACK__VERSION,
182 true
183 );
184
185 wp_enqueue_style( 'jetpack-search-widget', plugins_url( 'search/css/search-widget-frontend.css', __FILE__ ) );
186 }
187
188 /**
189 * Get the list of valid sort types/orders.
190 *
191 * @since 5.8.0
192 *
193 * @return array The sort orders.
194 */
195 private function get_sort_types() {
196 return array(
197 'relevance|DESC' => is_admin() ? esc_html__( 'Relevance (recommended)', 'jetpack' ) : esc_html__( 'Relevance', 'jetpack' ),
198 'date|DESC' => esc_html__( 'Newest first', 'jetpack' ),
199 'date|ASC' => esc_html__( 'Oldest first', 'jetpack' ),
200 );
201 }
202
203 /**
204 * Callback for an array_filter() call in order to only get filters for the current widget.
205 *
206 * @see Jetpack_Search_Widget::widget()
207 *
208 * @since 5.7.0
209 *
210 * @param array $item Filter item.
211 *
212 * @return bool Whether the current filter item is for the current widget.
213 */
214 function is_for_current_widget( $item ) {
215 return isset( $item['widget_id'] ) && $this->id == $item['widget_id'];
216 }
217
218 /**
219 * This method returns a boolean for whether the widget should show site-wide filters for the site.
220 *
221 * This is meant to provide backwards-compatibility for VIP, and other professional plan users, that manually
222 * configured filters via `Jetpack_Search::set_filters()`.
223 *
224 * @since 5.7.0
225 *
226 * @return bool Whether the widget should display site-wide filters or not.
227 */
228 public function should_display_sitewide_filters() {
229 $filter_widgets = get_option( 'widget_jetpack-search-filters' );
230
231 // This shouldn't be empty, but just for sanity
232 if ( empty( $filter_widgets ) ) {
233 return false;
234 }
235
236 // If any widget has any filters, return false
237 foreach ( $filter_widgets as $number => $widget ) {
238 $widget_id = sprintf( '%s-%d', $this->id_base, $number );
239 if ( ! empty( $widget['filters'] ) && is_active_widget( false, $widget_id, $this->id_base ) ) {
240 return false;
241 }
242 }
243
244 return true;
245 }
246
247 public function jetpack_search_populate_defaults( $instance ) {
248 $instance = wp_parse_args(
249 (array) $instance, array(
250 'title' => '',
251 'search_box_enabled' => true,
252 'user_sort_enabled' => true,
253 'sort' => self::DEFAULT_SORT,
254 'filters' => array( array() ),
255 'post_types' => array(),
256 )
257 );
258
259 return $instance;
260 }
261
262 /**
263 * Responsible for rendering the widget on the frontend.
264 *
265 * @since 5.0.0
266 *
267 * @param array $args Widgets args supplied by the theme.
268 * @param array $instance The current widget instance.
269 */
270 public function widget( $args, $instance ) {
271 $instance = $this->jetpack_search_populate_defaults( $instance );
272
273 $display_filters = false;
274
275 if ( ( new Status() )->is_development_mode() ) {
276 echo $args['before_widget'];
277 ?><div id="<?php echo esc_attr( $this->id ); ?>-wrapper">
278 <div class="jetpack-search-sort-wrapper">
279 <label>
280 <?php esc_html_e( 'Jetpack Search not supported in Development Mode', 'jetpack' ); ?>
281 </label>
282 </div>
283 </div><?php
284 echo $args['after_widget'];
285 return;
286 }
287
288 if ( is_search() ) {
289 if ( Jetpack_Search_Helpers::should_rerun_search_in_customizer_preview() ) {
290 Jetpack_Search::instance()->update_search_results_aggregations();
291 }
292
293 $filters = Jetpack_Search::instance()->get_filters();
294
295 if ( ! Jetpack_Search_Helpers::are_filters_by_widget_disabled() && ! $this->should_display_sitewide_filters() ) {
296 $filters = array_filter( $filters, array( $this, 'is_for_current_widget' ) );
297 }
298
299 if ( ! empty( $filters ) ) {
300 $display_filters = true;
301 }
302 }
303
304 if ( ! $display_filters && empty( $instance['search_box_enabled'] ) && empty( $instance['user_sort_enabled'] ) ) {
305 return;
306 }
307
308 $title = isset( $instance['title'] ) ? $instance['title'] : '';
309
310 if ( empty( $title ) ) {
311 $title = '';
312 }
313
314 /** This filter is documented in core/src/wp-includes/default-widgets.php */
315 $title = apply_filters( 'widget_title', $title, $instance, $this->id_base );
316
317 echo $args['before_widget'];
318 ?><div id="<?php echo esc_attr( $this->id ); ?>-wrapper" class="<?php
319 echo Constants::is_true( 'JETPACK_SEARCH_PROTOTYPE' ) ? 'jetpack-instant-search-wrapper' : '' ?>">
320 <?php
321
322 if ( ! empty( $title ) ) {
323 /**
324 * Responsible for displaying the title of the Jetpack Search filters widget.
325 *
326 * @module search
327 *
328 * @since 5.7.0
329 *
330 * @param string $title The widget's title
331 * @param string $args['before_title'] The HTML tag to display before the title
332 * @param string $args['after_title'] The HTML tag to display after the title
333 */
334 do_action( 'jetpack_search_render_filters_widget_title', $title, $args['before_title'], $args['after_title'] );
335 }
336
337 $default_sort = isset( $instance['sort'] ) ? $instance['sort'] : self::DEFAULT_SORT;
338 list( $orderby, $order ) = $this->sorting_to_wp_query_param( $default_sort );
339 $current_sort = "{$orderby}|{$order}";
340
341 // we need to dynamically inject the sort field into the search box when the search box is enabled, and display
342 // it separately when it's not.
343 if ( ! empty( $instance['search_box_enabled'] ) ) {
344 Jetpack_Search_Template_Tags::render_widget_search_form( $instance['post_types'], $orderby, $order );
345 }
346
347 if ( ! empty( $instance['search_box_enabled'] ) && ! empty( $instance['user_sort_enabled'] ) ) :
348 ?>
349 <div class="jetpack-search-sort-wrapper">
350 <label>
351 <?php esc_html_e( 'Sort by', 'jetpack' ); ?>
352 <select class="jetpack-search-sort">
353 <?php foreach ( $this->get_sort_types() as $sort => $label ) { ?>
354 <option value="<?php echo esc_attr( $sort ); ?>" <?php selected( $current_sort, $sort ); ?>>
355 <?php echo esc_html( $label ); ?>
356 </option>
357 <?php } ?>
358 </select>
359 </label>
360 </div>
361 <?php
362 endif;
363
364 if ( $display_filters ) {
365 /**
366 * Responsible for rendering filters to narrow down search results.
367 *
368 * @module search
369 *
370 * @since 5.8.0
371 *
372 * @param array $filters The possible filters for the current query.
373 * @param array $post_types An array of post types to limit filtering to.
374 */
375 do_action(
376 'jetpack_search_render_filters',
377 $filters,
378 isset( $instance['post_types'] ) ? $instance['post_types'] : null
379 );
380 }
381
382 $this->maybe_render_sort_javascript( $instance, $order, $orderby );
383
384 echo '</div>';
385 echo $args['after_widget'];
386 }
387
388 /**
389 * Renders JavaScript for the sorting controls on the frontend.
390 *
391 * This JS is a bit complicated, but here's what it's trying to do:
392 * - find the search form
393 * - find the orderby/order fields and set default values
394 * - detect changes to the sort field, if it exists, and use it to set the order field values
395 *
396 * @since 5.8.0
397 *
398 * @param array $instance The current widget instance.
399 * @param string $order The order to initialize the select with.
400 * @param string $orderby The orderby to initialize the select with.
401 */
402 private function maybe_render_sort_javascript( $instance, $order, $orderby ) {
403 if ( Constants::is_true( 'JETPACK_SEARCH_PROTOTYPE' ) ) {
404 return;
405 }
406
407 if ( ! empty( $instance['user_sort_enabled'] ) ) :
408 ?>
409 <script type="text/javascript">
410 jQuery( document ).ready( function( $ ) {
411 var orderByDefault = '<?php echo 'date' === $orderby ? 'date' : 'relevance'; ?>',
412 orderDefault = '<?php echo 'ASC' === $order ? 'ASC' : 'DESC'; ?>',
413 widgetId = decodeURIComponent( '<?php echo rawurlencode( $this->id ); ?>' ),
414 searchQuery = decodeURIComponent( '<?php echo rawurlencode( get_query_var( 's', '' ) ); ?>' ),
415 isSearch = <?php echo (int) is_search(); ?>;
416
417 var container = $( '#' + widgetId + '-wrapper' ),
418 form = container.find('.jetpack-search-form form'),
419 orderBy = form.find( 'input[name=orderby]'),
420 order = form.find( 'input[name=order]'),
421 searchInput = form.find( 'input[name="s"]' );
422
423 orderBy.val( orderByDefault );
424 order.val( orderDefault );
425
426 // Some themes don't set the search query, which results in the query being lost
427 // when doing a sort selection. So, if the query isn't set, let's set it now. This approach
428 // is chosen over running a regex over HTML for every search query performed.
429 if ( isSearch && ! searchInput.val() ) {
430 searchInput.val( searchQuery );
431 }
432
433 searchInput.addClass( 'show-placeholder' );
434
435 container.find( '.jetpack-search-sort' ).change( function( event ) {
436 var values = event.target.value.split( '|' );
437 orderBy.val( values[0] );
438 order.val( values[1] );
439
440 form.submit();
441 });
442 } );
443 </script>
444 <?php
445 endif;
446 }
447
448 /**
449 * Convert a sort string into the separate order by and order parts.
450 *
451 * @since 5.8.0
452 *
453 * @param string $sort A sort string.
454 *
455 * @return array Order by and order.
456 */
457 private function sorting_to_wp_query_param( $sort ) {
458 $parts = explode( '|', $sort );
459 $orderby = isset( $_GET['orderby'] )
460 ? $_GET['orderby']
461 : $parts[0];
462
463 $order = isset( $_GET['order'] )
464 ? strtoupper( $_GET['order'] )
465 : ( ( isset( $parts[1] ) && 'ASC' === strtoupper( $parts[1] ) ) ? 'ASC' : 'DESC' );
466
467 return array( $orderby, $order );
468 }
469
470 /**
471 * Updates a particular instance of the widget. Validates and sanitizes the options.
472 *
473 * @since 5.0.0
474 *
475 * @param array $new_instance New settings for this instance as input by the user via Jetpack_Search_Widget::form().
476 * @param array $old_instance Old settings for this instance.
477 *
478 * @return array Settings to save.
479 */
480 public function update( $new_instance, $old_instance ) {
481 $instance = array();
482
483 $instance['title'] = sanitize_text_field( $new_instance['title'] );
484 $instance['search_box_enabled'] = empty( $new_instance['search_box_enabled'] ) ? '0' : '1';
485 $instance['user_sort_enabled'] = empty( $new_instance['user_sort_enabled'] ) ? '0' : '1';
486 $instance['sort'] = $new_instance['sort'];
487 $instance['post_types'] = empty( $new_instance['post_types'] ) || empty( $instance['search_box_enabled'] )
488 ? array()
489 : array_map( 'sanitize_key', $new_instance['post_types'] );
490
491 $filters = array();
492 if ( isset( $new_instance['filter_type'] ) ) {
493 foreach ( (array) $new_instance['filter_type'] as $index => $type ) {
494 $count = intval( $new_instance['num_filters'][ $index ] );
495 $count = min( 50, $count ); // Set max boundary at 50.
496 $count = max( 1, $count ); // Set min boundary at 1.
497
498 switch ( $type ) {
499 case 'taxonomy':
500 $filters[] = array(
501 'name' => sanitize_text_field( $new_instance['filter_name'][ $index ] ),
502 'type' => 'taxonomy',
503 'taxonomy' => sanitize_key( $new_instance['taxonomy_type'][ $index ] ),
504 'count' => $count,
505 );
506 break;
507 case 'post_type':
508 $filters[] = array(
509 'name' => sanitize_text_field( $new_instance['filter_name'][ $index ] ),
510 'type' => 'post_type',
511 'count' => $count,
512 );
513 break;
514 case 'date_histogram':
515 $filters[] = array(
516 'name' => sanitize_text_field( $new_instance['filter_name'][ $index ] ),
517 'type' => 'date_histogram',
518 'count' => $count,
519 'field' => sanitize_key( $new_instance['date_histogram_field'][ $index ] ),
520 'interval' => sanitize_key( $new_instance['date_histogram_interval'][ $index ] ),
521 );
522 break;
523 }
524 }
525 }
526
527 if ( ! empty( $filters ) ) {
528 $instance['filters'] = $filters;
529 }
530
531 return $instance;
532 }
533
534 /**
535 * Outputs the settings update form.
536 *
537 * @since 5.0.0
538 *
539 * @param array $instance Current settings.
540 */
541 public function form( $instance ) {
542 $instance = $this->jetpack_search_populate_defaults( $instance );
543
544 $title = strip_tags( $instance['title'] );
545
546 $hide_filters = Jetpack_Search_Helpers::are_filters_by_widget_disabled();
547
548 $classes = sprintf(
549 'jetpack-search-filters-widget %s %s %s',
550 $hide_filters ? 'hide-filters' : '',
551 $instance['search_box_enabled'] ? '' : 'hide-post-types',
552 $this->id
553 );
554 ?>
555 <div class="<?php echo esc_attr( $classes ); ?>">
556 <p>
557 <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>">
558 <?php esc_html_e( 'Title (optional):', 'jetpack' ); ?>
559 </label>
560 <input
561 class="widefat"
562 id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"
563 name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>"
564 type="text"
565 value="<?php echo esc_attr( $title ); ?>"
566 />
567 </p>
568
569 <p>
570 <label>
571 <input
572 type="checkbox"
573 class="jetpack-search-filters-widget__search-box-enabled"
574 name="<?php echo esc_attr( $this->get_field_name( 'search_box_enabled' ) ); ?>"
575 <?php checked( $instance['search_box_enabled'] ); ?>
576 />
577 <?php esc_html_e( 'Show search box', 'jetpack' ); ?>
578 </label>
579 </p>
580 <p>
581 <label>
582 <input
583 type="checkbox"
584 class="jetpack-search-filters-widget__sort-controls-enabled"
585 name="<?php echo esc_attr( $this->get_field_name( 'user_sort_enabled' ) ); ?>"
586 <?php checked( $instance['user_sort_enabled'] ); ?>
587 <?php disabled( ! $instance['search_box_enabled'] ); ?>
588 />
589 <?php esc_html_e( 'Show sort selection dropdown', 'jetpack' ); ?>
590 </label>
591 </p>
592
593 <p class="jetpack-search-filters-widget__post-types-select">
594 <label><?php esc_html_e( 'Post types to search (minimum of 1):', 'jetpack' ); ?></label>
595 <?php foreach ( get_post_types( array( 'exclude_from_search' => false ), 'objects' ) as $post_type ) : ?>
596 <label>
597 <input
598 type="checkbox"
599 value="<?php echo esc_attr( $post_type->name ); ?>"
600 name="<?php echo esc_attr( $this->get_field_name( 'post_types' ) ); ?>[]"
601 <?php checked( empty( $instance['post_types'] ) || in_array( $post_type->name, $instance['post_types'] ) ); ?>
602 />&nbsp;
603 <?php echo esc_html( $post_type->label ); ?>
604 </label>
605 <?php endforeach; ?>
606 </p>
607
608 <p>
609 <label>
610 <?php esc_html_e( 'Default sort order:', 'jetpack' ); ?>
611 <select
612 name="<?php echo esc_attr( $this->get_field_name( 'sort' ) ); ?>"
613 class="widefat jetpack-search-filters-widget__sort-order">
614 <?php foreach ( $this->get_sort_types() as $sort_type => $label ) { ?>
615 <option value="<?php echo esc_attr( $sort_type ); ?>" <?php selected( $instance['sort'], $sort_type ); ?>>
616 <?php echo esc_html( $label ); ?>
617 </option>
618 <?php } ?>
619 </select>
620 </label>
621 </p>
622
623 <?php if ( ! $hide_filters ) : ?>
624 <script class="jetpack-search-filters-widget__filter-template" type="text/template">
625 <?php echo $this->render_widget_edit_filter( array(), true ); ?>
626 </script>
627 <div class="jetpack-search-filters-widget__filters">
628 <?php foreach ( (array) $instance['filters'] as $filter ) : ?>
629 <?php $this->render_widget_edit_filter( $filter ); ?>
630 <?php endforeach; ?>
631 </div>
632 <p class="jetpack-search-filters-widget__add-filter-wrapper">
633 <a class="button jetpack-search-filters-widget__add-filter" href="#">
634 <?php esc_html_e( 'Add a filter', 'jetpack' ); ?>
635 </a>
636 </p>
637 <noscript>
638 <p class="jetpack-search-filters-help">
639 <?php echo esc_html_e( 'Adding filters requires JavaScript!', 'jetpack' ); ?>
640 </p>
641 </noscript>
642 <?php if ( is_customize_preview() ) : ?>
643 <p class="jetpack-search-filters-help">
644 <a href="https://jetpack.com/support/search/#filters-not-showing-up" target="_blank">
645 <?php esc_html_e( "Why aren't my filters appearing?", 'jetpack' ); ?>
646 </a>
647 </p>
648 <?php endif; ?>
649 <?php endif; ?>
650 </div>
651 <?php
652 }
653
654 /**
655 * We need to render HTML in two formats: an Underscore template (client-side)
656 * and native PHP (server-side). This helper function allows for easy rendering
657 * of attributes in both formats.
658 *
659 * @since 5.8.0
660 *
661 * @param string $name Attribute name.
662 * @param string $value Attribute value.
663 * @param bool $is_template Whether this is for an Underscore template or not.
664 */
665 private function render_widget_attr( $name, $value, $is_template ) {
666 echo $is_template ? "<%= $name %>" : esc_attr( $value );
667 }
668
669 /**
670 * We need to render HTML in two formats: an Underscore template (client-size)
671 * and native PHP (server-side). This helper function allows for easy rendering
672 * of the "selected" attribute in both formats.
673 *
674 * @since 5.8.0
675 *
676 * @param string $name Attribute name.
677 * @param string $value Attribute value.
678 * @param string $compare Value to compare to the attribute value to decide if it should be selected.
679 * @param bool $is_template Whether this is for an Underscore template or not.
680 */
681 private function render_widget_option_selected( $name, $value, $compare, $is_template ) {
682 $compare_js = rawurlencode( $compare );
683 echo $is_template ? "<%= decodeURIComponent( '$compare_js' ) === $name ? 'selected=\"selected\"' : '' %>" : selected( $value, $compare );
684 }
685
686 /**
687 * Responsible for rendering a single filter in the customizer or the widget administration screen in wp-admin.
688 *
689 * We use this method for two purposes - rendering the fields server-side, and also rendering a script template for Underscore.
690 *
691 * @since 5.7.0
692 *
693 * @param array $filter The filter to render.
694 * @param bool $is_template Whether this is for an Underscore template or not.
695 */
696 public function render_widget_edit_filter( $filter, $is_template = false ) {
697 $args = wp_parse_args(
698 $filter, array(
699 'name' => '',
700 'type' => 'taxonomy',
701 'taxonomy' => '',
702 'post_type' => '',
703 'field' => '',
704 'interval' => '',
705 'count' => self::DEFAULT_FILTER_COUNT,
706 )
707 );
708
709 $args['name_placeholder'] = Jetpack_Search_Helpers::generate_widget_filter_name( $args );
710
711 ?>
712 <div class="jetpack-search-filters-widget__filter is-<?php $this->render_widget_attr( 'type', $args['type'], $is_template ); ?>">
713 <p class="jetpack-search-filters-widget__type-select">
714 <label>
715 <?php esc_html_e( 'Filter Type:', 'jetpack' ); ?>
716 <select name="<?php echo esc_attr( $this->get_field_name( 'filter_type' ) ); ?>[]" class="widefat filter-select">
717 <option value="taxonomy" <?php $this->render_widget_option_selected( 'type', $args['type'], 'taxonomy', $is_template ); ?>>
718 <?php esc_html_e( 'Taxonomy', 'jetpack' ); ?>
719 </option>
720 <option value="post_type" <?php $this->render_widget_option_selected( 'type', $args['type'], 'post_type', $is_template ); ?>>
721 <?php esc_html_e( 'Post Type', 'jetpack' ); ?>
722 </option>
723 <option value="date_histogram" <?php $this->render_widget_option_selected( 'type', $args['type'], 'date_histogram', $is_template ); ?>>
724 <?php esc_html_e( 'Date', 'jetpack' ); ?>
725 </option>
726 </select>
727 </label>
728 </p>
729
730 <p class="jetpack-search-filters-widget__taxonomy-select">
731 <label>
732 <?php
733 esc_html_e( 'Choose a taxonomy:', 'jetpack' );
734 $seen_taxonomy_labels = array();
735 ?>
736 <select name="<?php echo esc_attr( $this->get_field_name( 'taxonomy_type' ) ); ?>[]" class="widefat taxonomy-select">
737 <?php foreach ( get_taxonomies( array( 'public' => true ), 'objects' ) as $taxonomy ) : ?>
738 <option value="<?php echo esc_attr( $taxonomy->name ); ?>" <?php $this->render_widget_option_selected( 'taxonomy', $args['taxonomy'], $taxonomy->name, $is_template ); ?>>
739 <?php
740 $label = in_array( $taxonomy->label, $seen_taxonomy_labels )
741 ? sprintf(
742 /* 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. */
743 _x( '%1$s (%2$s)', 'A label for a taxonomy selector option', 'jetpack' ),
744 $taxonomy->label,
745 $taxonomy->name
746 )
747 : $taxonomy->label;
748 echo esc_html( $label );
749 $seen_taxonomy_labels[] = $taxonomy->label;
750 ?>
751 </option>
752 <?php endforeach; ?>
753 </select>
754 </label>
755 </p>
756
757 <p class="jetpack-search-filters-widget__date-histogram-select">
758 <label>
759 <?php esc_html_e( 'Choose a field:', 'jetpack' ); ?>
760 <select name="<?php echo esc_attr( $this->get_field_name( 'date_histogram_field' ) ); ?>[]" class="widefat date-field-select">
761 <option value="post_date" <?php $this->render_widget_option_selected( 'field', $args['field'], 'post_date', $is_template ); ?>>
762 <?php esc_html_e( 'Date', 'jetpack' ); ?>
763 </option>
764 <option value="post_date_gmt" <?php $this->render_widget_option_selected( 'field', $args['field'], 'post_date_gmt', $is_template ); ?>>
765 <?php esc_html_e( 'Date GMT', 'jetpack' ); ?>
766 </option>
767 <option value="post_modified" <?php $this->render_widget_option_selected( 'field', $args['field'], 'post_modified', $is_template ); ?>>
768 <?php esc_html_e( 'Modified', 'jetpack' ); ?>
769 </option>
770 <option value="post_modified_gmt" <?php $this->render_widget_option_selected( 'field', $args['field'], 'post_modified_gmt', $is_template ); ?>>
771 <?php esc_html_e( 'Modified GMT', 'jetpack' ); ?>
772 </option>
773 </select>
774 </label>
775 </p>
776
777 <p class="jetpack-search-filters-widget__date-histogram-select">
778 <label>
779 <?php esc_html_e( 'Choose an interval:' ); ?>
780 <select name="<?php echo esc_attr( $this->get_field_name( 'date_histogram_interval' ) ); ?>[]" class="widefat date-interval-select">
781 <option value="month" <?php $this->render_widget_option_selected( 'interval', $args['interval'], 'month', $is_template ); ?>>
782 <?php esc_html_e( 'Month', 'jetpack' ); ?>
783 </option>
784 <option value="year" <?php $this->render_widget_option_selected( 'interval', $args['interval'], 'year', $is_template ); ?>>
785 <?php esc_html_e( 'Year', 'jetpack' ); ?>
786 </option>
787 </select>
788 </label>
789 </p>
790
791 <p class="jetpack-search-filters-widget__title">
792 <label>
793 <?php esc_html_e( 'Title:', 'jetpack' ); ?>
794 <input
795 class="widefat"
796 type="text"
797 name="<?php echo esc_attr( $this->get_field_name( 'filter_name' ) ); ?>[]"
798 value="<?php $this->render_widget_attr( 'name', $args['name'], $is_template ); ?>"
799 placeholder="<?php $this->render_widget_attr( 'name_placeholder', $args['name_placeholder'], $is_template ); ?>"
800 />
801 </label>
802 </p>
803
804 <p>
805 <label>
806 <?php esc_html_e( 'Maximum number of filters (1-50):', 'jetpack' ); ?>
807 <input
808 class="widefat filter-count"
809 name="<?php echo esc_attr( $this->get_field_name( 'num_filters' ) ); ?>[]"
810 type="number"
811 value="<?php $this->render_widget_attr( 'count', $args['count'], $is_template ); ?>"
812 min="1"
813 max="50"
814 step="1"
815 required
816 />
817 </label>
818 </p>
819
820 <p class="jetpack-search-filters-widget__controls">
821 <a href="#" class="delete"><?php esc_html_e( 'Remove', 'jetpack' ); ?></a>
822 </p>
823 </div>
824 <?php
825 }
826 }
827