Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
2.50% covered (danger)
2.50%
2 / 80
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
Inline_Search_Correction
2.50% covered (danger)
2.50%
2 / 80
0.00% covered (danger)
0.00%
0 / 8
390.74
0.00% covered (danger)
0.00%
0 / 1
 setup_corrected_query_hooks
40.00% covered (danger)
40.00%
2 / 5
0.00% covered (danger)
0.00%
0 / 1
4.94
 enqueue_styles
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 register_corrected_query_script
0.00% covered (danger)
0.00%
0 / 25
0.00% covered (danger)
0.00%
0 / 1
6
 register_corrected_query_style
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
12
 maybe_use_corrected_query
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
20
 get_title_selectors
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 get_corrected_query_html
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
20
 get_search_result
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Inline Search Correction: Handles search query correction display
4 *
5 * @package automattic/jetpack-search
6 */
7
8namespace Automattic\Jetpack\Search;
9
10use Automattic\Jetpack\Assets;
11
12/**
13 * Class for handling search correction display
14 *
15 * @since 0.48.0
16 */
17class Inline_Search_Correction {
18    /**
19     * Setup hooks for displaying corrected query notice.
20     *
21     * @param \WP_Query $query The current query.
22     */
23    public function setup_corrected_query_hooks( $query ) {
24        if ( ! $query->is_search() || ! $query->is_main_query() ) {
25            return;
26        }
27
28        add_filter( 'get_search_query', array( $this, 'maybe_use_corrected_query' ) );
29        add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_styles' ) );
30        add_action( 'wp_footer', array( $this, 'register_corrected_query_script' ) );
31    }
32
33    /**
34     * Enqueue theme-specific styles for the search correction.
35     * This is hooked to wp_enqueue_scripts to ensure styles load properly in the head.
36     *
37     * @since 0.48.0
38     */
39    public function enqueue_styles() {
40        $corrected_query_html = $this->get_corrected_query_html();
41        if ( empty( $corrected_query_html ) ) {
42            return;
43        }
44
45        $handle = 'jetpack-search-inline-corrected-query';
46        $this->register_corrected_query_style( $handle );
47    }
48
49    /**
50     * Register and configure the JavaScript for displaying the corrected query notice.
51     *
52     * @since 0.48.0
53     */
54    public function register_corrected_query_script() {
55        $corrected_query_html = $this->get_corrected_query_html();
56        if ( empty( $corrected_query_html ) ) {
57            return;
58        }
59
60        $handle = 'jetpack-search-inline-corrected-query';
61
62        Assets::register_script(
63            $handle,
64            'build/inline-search/jp-search-inline.js',
65            Package::get_installed_path() . '/src',
66            array(
67                'in_footer'  => true,
68                'textdomain' => 'jetpack-search-pkg',
69                'enqueue'    => true,
70            )
71        );
72
73        wp_localize_script(
74            $handle,
75            'JetpackSearchCorrectedQuery',
76            array(
77                'html'      => $corrected_query_html,
78                'selectors' => $this->get_title_selectors(),
79                'i18n'      => array(
80                    'error' => esc_html__( 'Error displaying search correction', 'jetpack-search-pkg' ),
81                ),
82            )
83        );
84    }
85
86    /**
87     * Register and enqueue theme-specific styles for corrected query.
88     *
89     * @since 0.48.0
90     * @param string $handle The script handle to use for the stylesheet.
91     */
92    private function register_corrected_query_style( $handle ) {
93        $css_path      = 'build/inline-search/';
94        $css_file      = 'corrected-query.css';
95        $full_css_path = $css_path . $css_file;
96        $package_path  = Package::get_installed_path();
97        $css_full_path = $package_path . '/' . $full_css_path;
98
99        // Verify the CSS file exists before trying to enqueue it
100        if ( ! file_exists( $css_full_path ) ) {
101            return;
102        }
103
104        // We need to use plugins_url for reliable URL generation
105        $file_url = plugins_url(
106            $full_css_path,
107            $package_path . '/package.json'
108        );
109
110        // Use the file's modification time for more precise cache busting
111        $file_version = file_exists( $css_full_path ) ? filemtime( $css_full_path ) : Package::VERSION;
112
113        wp_enqueue_style(
114            $handle,
115            $file_url,
116            array(),
117            $file_version // Use file modification time for cache busting
118        );
119    }
120
121    /**
122     * Replaces the search query with the corrected query in the title.
123     *
124     * @param string $query The original search query.
125     * @return string The corrected query if available, otherwise the original query.
126     */
127    public function maybe_use_corrected_query( $query ) {
128        $search_result = $this->get_search_result();
129        if ( is_array( $search_result ) && ! empty( $search_result['corrected_query'] ) && ! empty( $search_result['results'] ) ) {
130            return $search_result['corrected_query'];
131        }
132
133        return $query;
134    }
135
136    /**
137     * Get selectors where corrected query notice will be displayed.
138     *
139     * @since 0.48.0
140     * @return array CSS selectors for search title elements.
141     */
142    private function get_title_selectors() {
143        $default_selectors = array(
144            '.wp-block-query-title',
145            '.page-title',
146            '.archive-title',
147            '.entry-title',
148            '.nv-page-title',
149            '.page-subheading',
150        );
151
152        /**
153         * Filter the selectors where corrected query notice appears.
154         *
155         * @since 0.48.0
156         * @param array $default_selectors CSS selectors for search title elements.
157         */
158        return apply_filters( 'jetpack_search_title_selectors', $default_selectors );
159    }
160
161    /**
162     * Generate the HTML for the corrected query notice.
163     *
164     * @return string The HTML for the corrected query notice or empty string if none.
165     */
166    private function get_corrected_query_html() {
167        global $wp_query;
168        $original_query = $wp_query->get( 's' );
169        $search_result  = $this->get_search_result();
170
171        if ( ! is_array( $search_result ) || empty( $search_result['corrected_query'] ) || empty( $search_result['results'] ) ) {
172            return '';
173        }
174
175        $message = sprintf(
176            /* translators: %s: Original search term the user entered */
177            esc_html__( 'No results for "%s"', 'jetpack-search-pkg' ),
178            esc_html( $original_query )
179        );
180
181        return sprintf(
182            '<p class="jetpack-search-corrected-query">%s</p>',
183            $message
184        );
185    }
186
187    /**
188     * Get the search result from the Inline_Search instance.
189     *
190     * @return array|\WP_Error|null The search result or null if not available.
191     */
192    private function get_search_result() {
193        $inline_search = Inline_Search::instance();
194        return $inline_search->get_search_result();
195    }
196}