Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 79
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
Jetpack_Search_Debug_Bar
0.00% covered (danger)
0.00%
0 / 77
0.00% covered (danger)
0.00%
0 / 6
306
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 instance
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 enqueue_scripts
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
12
 is_visible
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 render
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 1
90
 render_json_toggle
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2/**
3 * Adds a Jetpack Search debug panel to Debug Bar.
4 *
5 * @package automattic/jetpack
6 */
7
8use Automattic\Jetpack\Search as Jetpack_Search;
9
10if ( ! defined( 'ABSPATH' ) ) {
11    exit( 0 );
12}
13
14/**
15 * Singleton class instantiated by Jetpack_Searc_Debug_Bar::instance() that handles
16 * rendering the Jetpack Search debug bar menu item and panel.
17 */
18class Jetpack_Search_Debug_Bar extends Debug_Bar_Panel {
19    /**
20     * Holds singleton instance
21     *
22     * @var Jetpack_Search_Debug_Bar
23     */
24    protected static $instance = null;
25
26    /**
27     * The title to use in the debug bar navigation
28     *
29     * @var string
30     */
31    public $title;
32
33    /**
34     * Constructor
35     */
36    public function __construct() {
37        /** "Search" is a product name, do not translate. */
38        $this->title( 'Jetpack Search' );
39        add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
40        add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
41        add_action( 'login_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
42        add_action( 'enqueue_embed_scripts', array( $this, 'enqueue_scripts' ) );
43    }
44
45    /**
46     * Returns the singleton instance of Jetpack_Search_Debug_Bar
47     *
48     * @return Jetpack_Search_Debug_Bar
49     */
50    public static function instance() {
51        if ( self::$instance === null ) {
52            self::$instance = new Jetpack_Search_Debug_Bar();
53        }
54        return self::$instance;
55    }
56
57    /**
58     * Enqueues styles for our panel in the debug bar
59     *
60     * @return void
61     */
62    public function enqueue_scripts() {
63        // Do not enqueue scripts if we haven't already enqueued Debug Bar or Query Monitor styles.
64        if ( ! wp_style_is( 'debug-bar' ) && ! wp_style_is( 'query-monitor' ) ) {
65            return;
66        }
67
68        wp_enqueue_style(
69            'jetpack-search-debug-bar',
70            plugins_url( '3rd-party/debug-bar/debug-bar.css', JETPACK__PLUGIN_FILE ),
71            array(),
72            JETPACK__VERSION
73        );
74        wp_enqueue_script(
75            'jetpack-search-debug-bar',
76            plugins_url( '3rd-party/debug-bar/debug-bar.js', JETPACK__PLUGIN_FILE ),
77            array( 'jquery' ),
78            JETPACK__VERSION,
79            true
80        );
81    }
82
83    /**
84     * Should the Jetpack Search Debug Bar show?
85     *
86     * Since we've previously done a check for the search module being activated, let's just return true.
87     * Later on, we can update this to only show when `is_search()` is true.
88     *
89     * @return boolean
90     */
91    public function is_visible() {
92        return true;
93    }
94
95    /**
96     * Renders the panel content
97     *
98     * @return void
99     */
100    public function render() {
101        $jetpack_search = (
102            Jetpack_Search\Options::is_instant_enabled() ?
103            Jetpack_Search\Instant_Search::instance() :
104            Jetpack_Search\Classic_Search::instance()
105        );
106
107        // Search hasn't been initialized. Exit early and do not display the debug bar.
108        if ( ! method_exists( $jetpack_search, 'get_last_query_info' ) ) {
109            return;
110        }
111
112        $last_query_info = $jetpack_search->get_last_query_info();
113
114        // If not empty, let's reshuffle the order of some things.
115        if ( ! empty( $last_query_info ) ) {
116            $args          = $last_query_info['args'];
117            $response      = $last_query_info['response'];
118            $response_code = $last_query_info['response_code'];
119
120            unset( $last_query_info['args'] );
121            unset( $last_query_info['response'] );
122            unset( $last_query_info['response_code'] );
123
124            if ( $last_query_info['es_time'] === null ) {
125                $last_query_info['es_time'] = esc_html_x(
126                    'cache hit',
127                    'displayed in search results when results are cached',
128                    'jetpack'
129                );
130            }
131
132            $temp = array_merge(
133                array( 'response_code' => $response_code ),
134                array( 'args' => $args ),
135                $last_query_info,
136                array( 'response' => $response )
137            );
138
139            $last_query_info = $temp;
140        }
141        ?>
142        <div class="jetpack-search-debug-bar">
143            <h2><?php esc_html_e( 'Last query information:', 'jetpack' ); ?></h2>
144            <?php if ( empty( $last_query_info ) ) : ?>
145                    <?php echo esc_html_x( 'None', 'Text displayed when there is no information', 'jetpack' ); ?>
146                <?php
147                else :
148                    foreach ( $last_query_info as $key => $info ) :
149                        ?>
150                        <h3><?php echo esc_html( $key ); ?></h3>
151                        <?php
152                        if ( 'response' !== $key && 'args' !== $key ) :
153                            ?>
154                        <pre><?php print_r( esc_html( $info ) ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions ?></pre>
155                            <?php
156                    else :
157                        $this->render_json_toggle( $info );
158                    endif;
159                    ?>
160                        <?php
161                    endforeach;
162            endif;
163                ?>
164        </div><!-- Closes .jetpack-search-debug-bar -->
165        <?php
166    }
167
168    /**
169     * Responsible for rendering the HTML necessary for the JSON toggle
170     *
171     * @param array $value The resonse from the API as an array.
172     * @return void
173     */
174    public function render_json_toggle( $value ) {
175        ?>
176        <div class="json-toggle-wrap">
177            <pre class="json">
178            <?php
179                // esc_html() will not double-encode entities (&amp; -> &amp;amp;).
180                // If any entities are part of the JSON blob, we want to re-encoode them
181                // (double-encode them) so that they are displayed correctly in the debug
182                // bar.
183                // Use _wp_specialchars() "manually" to ensure entities are encoded correctly.
184                echo _wp_specialchars( // phpcs:ignore WordPress.Security.EscapeOutput
185                    wp_json_encode( $value, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ),
186                    ENT_NOQUOTES, // Don't need to encode quotes (output is for a text node).
187                    'UTF-8',         // wp_json_encode() outputs UTF-8, not the blog's charset.
188                    true       // Do "double-encode" existing HTML entities.
189                );
190            ?>
191            </pre>
192            <span class="pretty toggle"><?php echo esc_html_x( 'Pretty', 'label for formatting JSON', 'jetpack' ); ?></span>
193            <span class="ugly toggle"><?php echo esc_html_x( 'Minify', 'label for formatting JSON', 'jetpack' ); ?></span>
194        </div>
195        <?php
196    }
197}