diff options
Diffstat (limited to 'plugins/jetpack/modules/widgets/search.php')
-rw-r--r-- | plugins/jetpack/modules/widgets/search.php | 483 |
1 files changed, 370 insertions, 113 deletions
diff --git a/plugins/jetpack/modules/widgets/search.php b/plugins/jetpack/modules/widgets/search.php index 5cff02f4..291649f2 100644 --- a/plugins/jetpack/modules/widgets/search.php +++ b/plugins/jetpack/modules/widgets/search.php @@ -1,4 +1,4 @@ -<?php +<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName /** * Jetpack Search: Jetpack_Search_Widget class * @@ -7,21 +7,26 @@ * @since 5.0.0 */ -use Automattic\Jetpack\Constants; +use Automattic\Jetpack\Redirect; +use Automattic\Jetpack\Search\Helper; +use Automattic\Jetpack\Search\Options; use Automattic\Jetpack\Status; +use Automattic\Jetpack\Tracking; add_action( 'widgets_init', 'jetpack_search_widget_init' ); +/** + * Register the widget if Jetpack Search is available and enabled. + */ function jetpack_search_widget_init() { if ( - ! Jetpack::is_active() + ! Jetpack::is_connection_ready() || ( method_exists( 'Jetpack_Plan', 'supports' ) && ! Jetpack_Plan::supports( 'search' ) ) + || ! Jetpack::is_module_active( 'search' ) ) { return; } - require_once JETPACK__PLUGIN_DIR . 'modules/search/class.jetpack-search-helpers.php'; - register_widget( 'Jetpack_Search_Widget' ); } @@ -62,23 +67,25 @@ class Jetpack_Search_Widget extends WP_Widget { * Jetpack_Search_Widget constructor. * * @since 5.0.0 + * + * @param string $name Widget name. */ public function __construct( $name = null ) { if ( empty( $name ) ) { $name = esc_html__( 'Search', 'jetpack' ); } parent::__construct( - Jetpack_Search_Helpers::FILTER_WIDGET_BASE, + Helper::FILTER_WIDGET_BASE, /** This filter is documented in modules/widgets/facebook-likebox.php */ apply_filters( 'jetpack_widget_name', $name ), array( 'classname' => 'jetpack-filters widget_search', - 'description' => __( 'Replaces the default search with an Elasticsearch-powered search interface and filters.', 'jetpack' ), + 'description' => __( 'Instant search and filtering to help visitors quickly find relevant answers and explore your site.', 'jetpack' ), ) ); if ( - Jetpack_Search_Helpers::is_active_widget( $this->id ) && + Helper::is_active_widget( $this->id ) && ! $this->is_search_active() ) { $this->activate_search(); @@ -90,8 +97,12 @@ class Jetpack_Search_Widget extends WP_Widget { add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_scripts' ) ); } - add_action( 'jetpack_search_render_filters_widget_title', array( 'Jetpack_Search_Template_Tags', 'render_widget_title' ), 10, 3 ); - add_action( 'jetpack_search_render_filters', array( 'Jetpack_Search_Template_Tags', 'render_available_filters' ), 10, 2 ); + add_action( 'jetpack_search_render_filters_widget_title', array( 'Automattic\Jetpack\Search\Template_Tags', 'render_widget_title' ), 10, 3 ); + if ( Options::is_instant_enabled() ) { + add_action( 'jetpack_search_render_filters', array( 'Automattic\Jetpack\Search\Template_Tags', 'render_instant_filters' ), 10, 2 ); + } else { + add_action( 'jetpack_search_render_filters', array( 'Automattic\Jetpack\Search\Template_Tags', 'render_available_filters' ), 10, 2 ); + } } /** @@ -112,51 +123,44 @@ class Jetpack_Search_Widget extends WP_Widget { Jetpack::activate_module( 'search', false, false ); } - /** * Enqueues the scripts and styles needed for the customizer. * * @since 5.7.0 */ public function widget_admin_setup() { - wp_enqueue_style( 'widget-jetpack-search-filters', plugins_url( 'search/css/search-widget-admin-ui.css', __FILE__ ) ); - - // Required for Tracks - wp_register_script( - 'jp-tracks', - '//stats.wp.com/w.js', + wp_enqueue_style( + 'widget-jetpack-search-filters', + plugins_url( 'search/css/search-widget-admin-ui.css', __FILE__ ), array(), - gmdate( 'YW' ), - true + JETPACK__VERSION ); - wp_register_script( - 'jp-tracks-functions', - plugins_url( '_inc/lib/tracks/tracks-callables.js', JETPACK__PLUGIN_FILE ), - array(), - JETPACK__VERSION, - false - ); + // Register jp-tracks and jp-tracks-functions. + Tracking::register_tracks_functions_scripts(); wp_register_script( 'jetpack-search-widget-admin', plugins_url( 'search/js/search-widget-admin.js', __FILE__ ), - array( 'jquery', 'jquery-ui-sortable', 'jp-tracks', 'jp-tracks-functions' ), - JETPACK__VERSION + array( 'jquery', 'jquery-ui-sortable', 'jp-tracks-functions' ), + JETPACK__VERSION, + false ); wp_localize_script( - 'jetpack-search-widget-admin', 'jetpack_search_filter_admin', array( + 'jetpack-search-widget-admin', + 'jetpack_search_filter_admin', + array( 'defaultFilterCount' => self::DEFAULT_FILTER_COUNT, 'tracksUserData' => Jetpack_Tracks_Client::get_connected_user_tracks_identity(), 'tracksEventData' => array( 'is_customizer' => (int) is_customize_preview(), ), 'i18n' => array( - 'month' => Jetpack_Search_Helpers::get_date_filter_type_name( 'month', false ), - 'year' => Jetpack_Search_Helpers::get_date_filter_type_name( 'year', false ), - 'monthUpdated' => Jetpack_Search_Helpers::get_date_filter_type_name( 'month', true ), - 'yearUpdated' => Jetpack_Search_Helpers::get_date_filter_type_name( 'year', true ), + 'month' => Helper::get_date_filter_type_name( 'month', false ), + 'year' => Helper::get_date_filter_type_name( 'year', false ), + 'monthUpdated' => Helper::get_date_filter_type_name( 'month', true ), + 'yearUpdated' => Helper::get_date_filter_type_name( 'year', true ), ), ) ); @@ -170,19 +174,24 @@ class Jetpack_Search_Widget extends WP_Widget { * @since 5.8.0 */ public function enqueue_frontend_scripts() { - if ( ! is_active_widget( false, false, $this->id_base, true ) || Constants::is_true( 'JETPACK_SEARCH_PROTOTYPE' ) ) { + if ( ! is_active_widget( false, false, $this->id_base, true ) || Options::is_instant_enabled() ) { return; } wp_enqueue_script( 'jetpack-search-widget', plugins_url( 'search/js/search-widget.js', __FILE__ ), - array( 'jquery' ), + array(), JETPACK__VERSION, true ); - wp_enqueue_style( 'jetpack-search-widget', plugins_url( 'search/css/search-widget-frontend.css', __FILE__ ) ); + wp_enqueue_style( + 'jetpack-search-widget', + plugins_url( 'search/css/search-widget-frontend.css', __FILE__ ), + array(), + JETPACK__VERSION + ); } /** @@ -211,8 +220,8 @@ class Jetpack_Search_Widget extends WP_Widget { * * @return bool Whether the current filter item is for the current widget. */ - function is_for_current_widget( $item ) { - return isset( $item['widget_id'] ) && $this->id == $item['widget_id']; + public function is_for_current_widget( $item ) { + return isset( $item['widget_id'] ) && $this->id == $item['widget_id']; // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison } /** @@ -228,12 +237,12 @@ class Jetpack_Search_Widget extends WP_Widget { public function should_display_sitewide_filters() { $filter_widgets = get_option( 'widget_jetpack-search-filters' ); - // This shouldn't be empty, but just for sanity + // This shouldn't be empty, but just for sanity. if ( empty( $filter_widgets ) ) { return false; } - // If any widget has any filters, return false + // If any widget has any filters, return false. foreach ( $filter_widgets as $number => $widget ) { $widget_id = sprintf( '%s-%d', $this->id_base, $number ); if ( ! empty( $widget['filters'] ) && is_active_widget( false, $widget_id, $this->id_base ) ) { @@ -244,9 +253,15 @@ class Jetpack_Search_Widget extends WP_Widget { return true; } + /** + * Widget defaults. + * + * @param array $instance Previously saved values from database. + */ public function jetpack_search_populate_defaults( $instance ) { $instance = wp_parse_args( - (array) $instance, array( + (array) $instance, + array( 'title' => '', 'search_box_enabled' => true, 'user_sort_enabled' => true, @@ -260,6 +275,23 @@ class Jetpack_Search_Widget extends WP_Widget { } /** + * Populates the instance array with appropriate default values. + * + * @since 8.6.0 + * @param array $instance Previously saved values from database. + * @return array Instance array with default values approprate for instant search + */ + public function populate_defaults_for_instant_search( $instance ) { + return wp_parse_args( + (array) $instance, + array( + 'title' => '', + 'filters' => array(), + ) + ); + } + + /** * Responsible for rendering the widget on the frontend. * * @since 5.0.0 @@ -270,29 +302,50 @@ class Jetpack_Search_Widget extends WP_Widget { public function widget( $args, $instance ) { $instance = $this->jetpack_search_populate_defaults( $instance ); - $display_filters = false; - - if ( ( new Status() )->is_development_mode() ) { - echo $args['before_widget']; + if ( ( new Status() )->is_offline_mode() ) { + echo $args['before_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?><div id="<?php echo esc_attr( $this->id ); ?>-wrapper"> <div class="jetpack-search-sort-wrapper"> <label> - <?php esc_html_e( 'Jetpack Search not supported in Development Mode', 'jetpack' ); ?> + <?php esc_html_e( 'Jetpack Search not supported in Offline Mode', 'jetpack' ); ?> </label> </div> - </div><?php - echo $args['after_widget']; + </div> + <?php + echo $args['after_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped return; } + if ( Options::is_instant_enabled() ) { + if ( array_key_exists( 'id', $args ) && 'jetpack-instant-search-sidebar' === $args['id'] ) { + $this->widget_empty_instant( $args, $instance ); + } else { + $this->widget_instant( $args, $instance ); + } + } else { + $this->widget_non_instant( $args, $instance ); + } + } + + /** + * Render the non-instant frontend widget. + * + * @since 8.3.0 + * + * @param array $args Widgets args supplied by the theme. + * @param array $instance The current widget instance. + */ + public function widget_non_instant( $args, $instance ) { + $display_filters = false; + if ( is_search() ) { - if ( Jetpack_Search_Helpers::should_rerun_search_in_customizer_preview() ) { + if ( Helper::should_rerun_search_in_customizer_preview() ) { Jetpack_Search::instance()->update_search_results_aggregations(); } $filters = Jetpack_Search::instance()->get_filters(); - if ( ! Jetpack_Search_Helpers::are_filters_by_widget_disabled() && ! $this->should_display_sitewide_filters() ) { + if ( ! Helper::are_filters_by_widget_disabled() && ! $this->should_display_sitewide_filters() ) { $filters = array_filter( $filters, array( $this, 'is_for_current_widget' ) ); } @@ -305,18 +358,14 @@ class Jetpack_Search_Widget extends WP_Widget { return; } - $title = isset( $instance['title'] ) ? $instance['title'] : ''; - - if ( empty( $title ) ) { - $title = ''; - } + $title = ! empty( $instance['title'] ) ? $instance['title'] : ''; /** This filter is documented in core/src/wp-includes/default-widgets.php */ $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); - echo $args['before_widget']; - ?><div id="<?php echo esc_attr( $this->id ); ?>-wrapper" class="<?php - echo Constants::is_true( 'JETPACK_SEARCH_PROTOTYPE' ) ? 'jetpack-instant-search-wrapper' : '' ?>"> + echo $args['before_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ?> + <div id="<?php echo esc_attr( $this->id ); ?>-wrapper" > <?php if ( ! empty( $title ) ) { @@ -341,11 +390,11 @@ class Jetpack_Search_Widget extends WP_Widget { // we need to dynamically inject the sort field into the search box when the search box is enabled, and display // it separately when it's not. if ( ! empty( $instance['search_box_enabled'] ) ) { - Jetpack_Search_Template_Tags::render_widget_search_form( $instance['post_types'], $orderby, $order ); + Automattic\Jetpack\Search\Template_Tags::render_widget_search_form( $instance['post_types'], $orderby, $order ); } if ( ! empty( $instance['search_box_enabled'] ) && ! empty( $instance['user_sort_enabled'] ) ) : - ?> + ?> <div class="jetpack-search-sort-wrapper"> <label> <?php esc_html_e( 'Sort by', 'jetpack' ); ?> @@ -358,7 +407,7 @@ class Jetpack_Search_Widget extends WP_Widget { </select> </label> </div> - <?php + <?php endif; if ( $display_filters ) { @@ -382,7 +431,118 @@ class Jetpack_Search_Widget extends WP_Widget { $this->maybe_render_sort_javascript( $instance, $order, $orderby ); echo '</div>'; - echo $args['after_widget']; + echo $args['after_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + /** + * Render the instant frontend widget. + * + * @since 8.3.0 + * + * @param array $args Widgets args supplied by the theme. + * @param array $instance The current widget instance. + */ + public function widget_instant( $args, $instance ) { + if ( Helper::should_rerun_search_in_customizer_preview() ) { + Jetpack_Search::instance()->update_search_results_aggregations(); + } + + $filters = Jetpack_Search::instance()->get_filters(); + if ( ! Helper::are_filters_by_widget_disabled() && ! $this->should_display_sitewide_filters() ) { + $filters = array_filter( $filters, array( $this, 'is_for_current_widget' ) ); + } + + $display_filters = ! empty( $filters ); + + $title = ! empty( $instance['title'] ) ? $instance['title'] : ''; + + /** This filter is documented in core/src/wp-includes/default-widgets.php */ + $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); + + echo $args['before_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ?> + <div id="<?php echo esc_attr( $this->id ); ?>-wrapper" class="jetpack-instant-search-wrapper"> + <?php + + if ( ! empty( $title ) ) { + /** + * Responsible for displaying the title of the Jetpack Search filters widget. + * + * @module search + * + * @since 5.7.0 + * + * @param string $title The widget's title + * @param string $args['before_title'] The HTML tag to display before the title + * @param string $args['after_title'] The HTML tag to display after the title + */ + do_action( 'jetpack_search_render_filters_widget_title', $title, $args['before_title'], $args['after_title'] ); + } + + Automattic\Jetpack\Search\Template_Tags::render_widget_search_form( array(), '', '' ); + + if ( $display_filters ) { + /** + * Responsible for rendering filters to narrow down search results. + * + * @module search + * + * @since 5.8.0 + * + * @param array $filters The possible filters for the current query. + * @param array $post_types An array of post types to limit filtering to. + */ + do_action( + 'jetpack_search_render_filters', + $filters, + null + ); + } + + echo '</div>'; + echo $args['after_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + } + + /** + * Render the instant widget for the overlay. + * + * @since 8.3.0 + * + * @param array $args Widgets args supplied by the theme. + * @param array $instance The current widget instance. + */ + public function widget_empty_instant( $args, $instance ) { + $title = isset( $instance['title'] ) ? $instance['title'] : ''; + + if ( empty( $title ) ) { + $title = ''; + } + + /** This filter is documented in core/src/wp-includes/default-widgets.php */ + $title = apply_filters( 'widget_title', $title, $instance, $this->id_base ); + + echo $args['before_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ?> + <div id="<?php echo esc_attr( $this->id ); ?>-wrapper" class="jetpack-instant-search-wrapper"> + <?php + + if ( ! empty( $title ) ) { + /** + * Responsible for displaying the title of the Jetpack Search filters widget. + * + * @module search + * + * @since 5.7.0 + * + * @param string $title The widget's title + * @param string $args['before_title'] The HTML tag to display before the title + * @param string $args['after_title'] The HTML tag to display after the title + */ + do_action( 'jetpack_search_render_filters_widget_title', $title, $args['before_title'], $args['after_title'] ); + } + + echo '</div>'; + echo $args['after_widget']; //phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } /** @@ -400,48 +560,55 @@ class Jetpack_Search_Widget extends WP_Widget { * @param string $orderby The orderby to initialize the select with. */ private function maybe_render_sort_javascript( $instance, $order, $orderby ) { - if ( Constants::is_true( 'JETPACK_SEARCH_PROTOTYPE' ) ) { + if ( Options::is_instant_enabled() ) { return; } if ( ! empty( $instance['user_sort_enabled'] ) ) : - ?> + ?> <script type="text/javascript"> - jQuery( document ).ready( function( $ ) { - var orderByDefault = '<?php echo 'date' === $orderby ? 'date' : 'relevance'; ?>', - orderDefault = '<?php echo 'ASC' === $order ? 'ASC' : 'DESC'; ?>', - widgetId = decodeURIComponent( '<?php echo rawurlencode( $this->id ); ?>' ), - searchQuery = decodeURIComponent( '<?php echo rawurlencode( get_query_var( 's', '' ) ); ?>' ), - isSearch = <?php echo (int) is_search(); ?>; - - var container = $( '#' + widgetId + '-wrapper' ), - form = container.find('.jetpack-search-form form'), - orderBy = form.find( 'input[name=orderby]'), - order = form.find( 'input[name=order]'), - searchInput = form.find( 'input[name="s"]' ); - - orderBy.val( orderByDefault ); - order.val( orderDefault ); - - // Some themes don't set the search query, which results in the query being lost - // when doing a sort selection. So, if the query isn't set, let's set it now. This approach - // is chosen over running a regex over HTML for every search query performed. - if ( isSearch && ! searchInput.val() ) { - searchInput.val( searchQuery ); - } - - searchInput.addClass( 'show-placeholder' ); - - container.find( '.jetpack-search-sort' ).change( function( event ) { - var values = event.target.value.split( '|' ); - orderBy.val( values[0] ); - order.val( values[1] ); - - form.submit(); - }); + var jetpackSearchModuleSorting = function() { + var orderByDefault = '<?php echo 'date' === $orderby ? 'date' : 'relevance'; ?>', + orderDefault = '<?php echo 'ASC' === $order ? 'ASC' : 'DESC'; ?>', + widgetId = decodeURIComponent( '<?php echo rawurlencode( $this->id ); ?>' ), + searchQuery = decodeURIComponent( '<?php echo rawurlencode( get_query_var( 's', '' ) ); ?>' ), + isSearch = <?php echo (int) is_search(); ?>; + + var container = document.getElementById( widgetId + '-wrapper' ), + form = container.querySelector( '.jetpack-search-form form' ), + orderBy = form.querySelector( 'input[name=orderby]' ), + order = form.querySelector( 'input[name=order]' ), + searchInput = form.querySelector( 'input[name="s"]' ), + sortSelectInput = container.querySelector( '.jetpack-search-sort' ); + + orderBy.value = orderByDefault; + order.value = orderDefault; + + // Some themes don't set the search query, which results in the query being lost + // when doing a sort selection. So, if the query isn't set, let's set it now. This approach + // is chosen over running a regex over HTML for every search query performed. + if ( isSearch && ! searchInput.value ) { + searchInput.value = searchQuery; + } + + searchInput.classList.add( 'show-placeholder' ); + + sortSelectInput.addEventListener( 'change', function( event ) { + var values = event.target.value.split( '|' ); + orderBy.value = values[0]; + order.value = values[1]; + + form.submit(); } ); + } + + if ( document.readyState === 'interactive' || document.readyState === 'complete' ) { + jetpackSearchModuleSorting(); + } else { + document.addEventListener( 'DOMContentLoaded', jetpackSearchModuleSorting ); + } </script> - <?php + <?php endif; } @@ -455,6 +622,7 @@ class Jetpack_Search_Widget extends WP_Widget { * @return array Order by and order. */ private function sorting_to_wp_query_param( $sort ) { + // phpcs:disable WordPress.Security.NonceVerification.Recommended $parts = explode( '|', $sort ); $orderby = isset( $_GET['orderby'] ) ? $_GET['orderby'] @@ -464,6 +632,8 @@ class Jetpack_Search_Widget extends WP_Widget { ? strtoupper( $_GET['order'] ) : ( ( isset( $parts[1] ) && 'ASC' === strtoupper( $parts[1] ) ) ? 'ASC' : 'DESC' ); + // phpcs:enable WordPress.Security.NonceVerification.Recommended + return array( $orderby, $order ); } @@ -477,8 +647,9 @@ class Jetpack_Search_Widget extends WP_Widget { * * @return array Settings to save. */ - public function update( $new_instance, $old_instance ) { - $instance = array(); + public function update( $new_instance, $old_instance ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable + $new_instance = $this->maybe_reformat_widget( $new_instance ); + $instance = array(); $instance['title'] = sanitize_text_field( $new_instance['title'] ); $instance['search_box_enabled'] = empty( $new_instance['search_box_enabled'] ) ? '0' : '1'; @@ -491,7 +662,7 @@ class Jetpack_Search_Widget extends WP_Widget { $filters = array(); if ( isset( $new_instance['filter_type'] ) ) { foreach ( (array) $new_instance['filter_type'] as $index => $type ) { - $count = intval( $new_instance['num_filters'][ $index ] ); + $count = (int) $new_instance['num_filters'][ $index ]; $count = min( 50, $count ); // Set max boundary at 50. $count = max( 1, $count ); // Set min boundary at 1. @@ -532,18 +703,48 @@ class Jetpack_Search_Widget extends WP_Widget { } /** + * Reformats the widget instance array to one that is recognized by the `update` function. + * This is only necessary when handling changes from the block-based widget editor. + * + * @param array $widget_instance - Jetpack Search widget instance. + * + * @return array - Potentially reformatted instance compatible with the save function. + */ + protected function maybe_reformat_widget( $widget_instance ) { + if ( isset( $widget_instance['filter_type'] ) || ! isset( $widget_instance['filters'] ) || ! is_array( $widget_instance['filters'] ) ) { + return $widget_instance; + } + + $instance = $widget_instance; + foreach ( $widget_instance['filters'] as $filter ) { + $instance['filter_type'][] = isset( $filter['type'] ) ? $filter['type'] : ''; + $instance['taxonomy_type'][] = isset( $filter['taxonomy'] ) ? $filter['taxonomy'] : ''; + $instance['filter_name'][] = isset( $filter['name'] ) ? $filter['name'] : ''; + $instance['num_filters'][] = isset( $filter['count'] ) ? $filter['count'] : 5; + $instance['date_histogram_field'][] = isset( $filter['field'] ) ? $filter['field'] : ''; + $instance['date_histogram_interval'][] = isset( $filter['interval'] ) ? $filter['interval'] : ''; + } + unset( $instance['filters'] ); + return $instance; + } + + /** * Outputs the settings update form. * * @since 5.0.0 * - * @param array $instance Current settings. + * @param array $instance Previously saved values from database. */ public function form( $instance ) { + if ( Options::is_instant_enabled() ) { + return $this->form_for_instant_search( $instance ); + } + $instance = $this->jetpack_search_populate_defaults( $instance ); - $title = strip_tags( $instance['title'] ); + $title = wp_strip_all_tags( $instance['title'] ); - $hide_filters = Jetpack_Search_Helpers::are_filters_by_widget_disabled(); + $hide_filters = Helper::are_filters_by_widget_disabled(); $classes = sprintf( 'jetpack-search-filters-widget %s %s %s', @@ -577,6 +778,7 @@ class Jetpack_Search_Widget extends WP_Widget { <?php esc_html_e( 'Show search box', 'jetpack' ); ?> </label> </p> + <p> <label> <input @@ -598,7 +800,7 @@ class Jetpack_Search_Widget extends WP_Widget { type="checkbox" value="<?php echo esc_attr( $post_type->name ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'post_types' ) ); ?>[]" - <?php checked( empty( $instance['post_types'] ) || in_array( $post_type->name, $instance['post_types'] ) ); ?> + <?php checked( empty( $instance['post_types'] ) || in_array( $post_type->name, $instance['post_types'], true ) ); ?> /> <?php echo esc_html( $post_type->label ); ?> </label> @@ -622,7 +824,9 @@ class Jetpack_Search_Widget extends WP_Widget { <?php if ( ! $hide_filters ) : ?> <script class="jetpack-search-filters-widget__filter-template" type="text/template"> - <?php echo $this->render_widget_edit_filter( array(), true ); ?> + <?php + echo $this->render_widget_edit_filter( array(), true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped + ?> </script> <div class="jetpack-search-filters-widget__filters"> <?php foreach ( (array) $instance['filters'] as $filter ) : ?> @@ -641,7 +845,7 @@ class Jetpack_Search_Widget extends WP_Widget { </noscript> <?php if ( is_customize_preview() ) : ?> <p class="jetpack-search-filters-help"> - <a href="https://jetpack.com/support/search/#filters-not-showing-up" target="_blank"> + <a href="<?php echo esc_url( Redirect::get_url( 'jetpack-support-search', array( 'anchor' => 'filters-not-showing-up' ) ) ); ?>" target="_blank"> <?php esc_html_e( "Why aren't my filters appearing?", 'jetpack' ); ?> </a> </p> @@ -652,6 +856,58 @@ class Jetpack_Search_Widget extends WP_Widget { } /** + * Outputs the widget update form to be used in the Customizer for Instant Search. + * + * @since 8.6.0 + * + * @param array $instance Previously saved values from database. + */ + private function form_for_instant_search( $instance ) { + $instance = $this->populate_defaults_for_instant_search( $instance ); + $classes = sprintf( 'jetpack-search-filters-widget %s', $this->id ); + + ?> + <div class="<?php echo esc_attr( $classes ); ?>"> + <!-- Title control --> + <p> + <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"> + <?php esc_html_e( 'Title (optional):', 'jetpack' ); ?> + </label> + <input + class="widefat" + id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" + name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" + type="text" + value="<?php echo esc_attr( wp_strip_all_tags( $instance['title'] ) ); ?>" + /> + </p> + + <!-- Filters control --> + <?php if ( ! Helper::are_filters_by_widget_disabled() ) : ?> + <div class="jetpack-search-filters-widget__filters"> + <?php foreach ( (array) $instance['filters'] as $filter ) : ?> + <?php $this->render_widget_edit_filter( $filter ); ?> + <?php endforeach; ?> + </div> + <p class="jetpack-search-filters-widget__add-filter-wrapper"> + <a class="button jetpack-search-filters-widget__add-filter" href="#"> + <?php esc_html_e( 'Add a filter', 'jetpack' ); ?> + </a> + </p> + <script class="jetpack-search-filters-widget__filter-template" type="text/template"> + <?php $this->render_widget_edit_filter( array(), true ); ?> + </script> + <noscript> + <p class="jetpack-search-filters-help"> + <?php echo esc_html_e( 'Adding filters requires JavaScript!', 'jetpack' ); ?> + </p> + </noscript> + <?php endif; ?> + </div> + <?php + } + + /** * We need to render HTML in two formats: an Underscore template (client-side) * and native PHP (server-side). This helper function allows for easy rendering * of attributes in both formats. @@ -663,7 +919,7 @@ class Jetpack_Search_Widget extends WP_Widget { * @param bool $is_template Whether this is for an Underscore template or not. */ private function render_widget_attr( $name, $value, $is_template ) { - echo $is_template ? "<%= $name %>" : esc_attr( $value ); + echo $is_template ? "<%= $name %>" : esc_attr( $value ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } /** @@ -680,7 +936,7 @@ class Jetpack_Search_Widget extends WP_Widget { */ private function render_widget_option_selected( $name, $value, $compare, $is_template ) { $compare_js = rawurlencode( $compare ); - echo $is_template ? "<%= decodeURIComponent( '$compare_js' ) === $name ? 'selected=\"selected\"' : '' %>" : selected( $value, $compare ); + echo $is_template ? "<%= decodeURIComponent( '$compare_js' ) === $name ? 'selected=\"selected\"' : '' %>" : selected( $value, $compare ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped } /** @@ -695,7 +951,8 @@ class Jetpack_Search_Widget extends WP_Widget { */ public function render_widget_edit_filter( $filter, $is_template = false ) { $args = wp_parse_args( - $filter, array( + $filter, + array( 'name' => '', 'type' => 'taxonomy', 'taxonomy' => '', @@ -706,7 +963,7 @@ class Jetpack_Search_Widget extends WP_Widget { ) ); - $args['name_placeholder'] = Jetpack_Search_Helpers::generate_widget_filter_name( $args ); + $args['name_placeholder'] = Helper::generate_widget_filter_name( $args ); ?> <div class="jetpack-search-filters-widget__filter is-<?php $this->render_widget_attr( 'type', $args['type'], $is_template ); ?>"> @@ -737,7 +994,7 @@ class Jetpack_Search_Widget extends WP_Widget { <?php foreach ( get_taxonomies( array( 'public' => true ), 'objects' ) as $taxonomy ) : ?> <option value="<?php echo esc_attr( $taxonomy->name ); ?>" <?php $this->render_widget_option_selected( 'taxonomy', $args['taxonomy'], $taxonomy->name, $is_template ); ?>> <?php - $label = in_array( $taxonomy->label, $seen_taxonomy_labels ) + $label = in_array( $taxonomy->label, $seen_taxonomy_labels, true ) ? sprintf( /* 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. */ _x( '%1$s (%2$s)', 'A label for a taxonomy selector option', 'jetpack' ), @@ -776,7 +1033,7 @@ class Jetpack_Search_Widget extends WP_Widget { <p class="jetpack-search-filters-widget__date-histogram-select"> <label> - <?php esc_html_e( 'Choose an interval:' ); ?> + <?php esc_html_e( 'Choose an interval:', 'jetpack' ); ?> <select name="<?php echo esc_attr( $this->get_field_name( 'date_histogram_interval' ) ); ?>[]" class="widefat date-interval-select"> <option value="month" <?php $this->render_widget_option_selected( 'interval', $args['interval'], 'month', $is_template ); ?>> <?php esc_html_e( 'Month', 'jetpack' ); ?> @@ -821,6 +1078,6 @@ class Jetpack_Search_Widget extends WP_Widget { <a href="#" class="delete"><?php esc_html_e( 'Remove', 'jetpack' ); ?></a> </p> </div> - <?php + <?php } } |