| @@ -7,9 +7,9 @@ | ||
| 7 | 7 | */ |
| 8 | 8 | |
| 9 | 9 | namespace ElasticPress\Screen; |
| 10 | 10 | |
| 11 | -use \ElasticPress\Utils; | |
| 11 | +use ElasticPress\Utils; | |
| 12 | 12 | |
| 13 | 13 | defined( 'ABSPATH' ) || exit; |
| 14 | 14 | |
| 15 | 15 | /** |
| @@ -18,12 +18,22 @@ | ||
| 18 | 18 | * @package ElasticPress |
| 19 | 19 | */ |
| 20 | 20 | class StatusReport { |
| 21 | 21 | /** |
| 22 | + * The formatted/processed reports. | |
| 23 | + * | |
| 24 | + * @since 4.5.0 | |
| 25 | + * @var array | |
| 26 | + */ | |
| 27 | + protected $formatted_reports = []; | |
| 28 | + | |
| 29 | + /** | |
| 22 | 30 | * Initialize class |
| 23 | 31 | */ |
| 24 | 32 | public function setup() { |
| 25 | 33 | add_action( 'admin_enqueue_scripts', [ $this, 'admin_enqueue_scripts' ] ); |
| 34 | + add_action( 'admin_head', array( $this, 'admin_menu_count' ), 11 ); | |
| 35 | + add_action( 'wp_ajax_ep_load_groups', array( $this, 'action_wp_ajax_ep_load_groups' ) ); | |
| 26 | 36 | } |
| 27 | 37 | |
| 28 | 38 | /** |
| 29 | 39 | * Enqueue script. |
| @@ -34,72 +44,107 @@ | ||
| 34 | 44 | if ( 'status-report' !== \ElasticPress\Screen::factory()->get_current_screen() ) { |
| 35 | 45 | return; |
| 36 | 46 | } |
| 37 | 47 | |
| 38 | - $script_deps = Utils\get_asset_info( 'status-report-script', 'dependencies' ); | |
| 39 | - | |
| 40 | 48 | wp_enqueue_script( |
| 41 | 49 | 'ep_admin_status_report_scripts', |
| 42 | 50 | EP_URL . 'dist/js/status-report-script.js', |
| 43 | - array_merge( $script_deps, [ 'clipboard' ] ), | |
| 51 | + Utils\get_asset_info( 'status-report-script', 'dependencies' ), | |
| 44 | 52 | Utils\get_asset_info( 'status-report-script', 'version' ), |
| 45 | 53 | true |
| 46 | 54 | ); |
| 47 | 55 | |
| 48 | - $reports = $this->get_reports(); | |
| 49 | - $reports = array_map( | |
| 50 | - function( $report ) { | |
| 51 | - return [ | |
| 52 | - 'actions' => $report->get_actions(), | |
| 53 | - 'groups' => $report->get_groups(), | |
| 54 | - 'title' => $report->get_title(), | |
| 55 | - ]; | |
| 56 | - }, | |
| 57 | - $reports | |
| 58 | - ); | |
| 56 | + $reports = $this->get_formatted_reports(); | |
| 59 | 57 | |
| 58 | + $plain_text_reports = []; | |
| 59 | + | |
| 60 | + foreach ( $reports as $report ) { | |
| 61 | + $title = $report['title']; | |
| 62 | + $groups = $report['groups']; | |
| 63 | + $plain_text_reports[] = $this->render_copy_paste_report( $title, $groups, $report['isAjaxReport'] ); | |
| 64 | + } | |
| 65 | + | |
| 66 | + $plain_text_report = implode( "\n\n", $plain_text_reports ); | |
| 67 | + | |
| 60 | 68 | wp_localize_script( |
| 61 | 69 | 'ep_admin_status_report_scripts', |
| 62 | 70 | 'epStatusReport', |
| 63 | - $reports | |
| 71 | + [ | |
| 72 | + 'plainTextReport' => $plain_text_report, | |
| 73 | + 'reports' => $reports, | |
| 74 | + 'nonce' => wp_create_nonce( 'ep-status-report-nonce' ), | |
| 75 | + ] | |
| 64 | 76 | ); |
| 65 | 77 | |
| 66 | - $style_deps = Utils\get_asset_info( 'status-report-styles', 'dependencies' ); | |
| 67 | - | |
| 68 | 78 | wp_enqueue_style( |
| 69 | 79 | 'ep_status_report_styles', |
| 70 | - EP_URL . 'dist/css/status-report-styles.css', | |
| 71 | - array_merge( $style_deps, [ 'wp-edit-post' ] ), | |
| 72 | - Utils\get_asset_info( 'status-report-styles', 'version' ) | |
| 80 | + EP_URL . 'dist/css/status-report-script.css', | |
| 81 | + [ 'wp-components', 'wp-edit-post' ], | |
| 82 | + Utils\get_asset_info( 'status-report-script', 'version' ) | |
| 73 | 83 | ); |
| 74 | 84 | } |
| 75 | 85 | |
| 76 | 86 | /** |
| 87 | + * AJAX action to load an individual report group. | |
| 88 | + * | |
| 89 | + * @since 5.2.0 | |
| 90 | + * | |
| 91 | + * @return void | |
| 92 | + */ | |
| 93 | + public function action_wp_ajax_ep_load_groups(): void { | |
| 94 | + if ( ! isset( $_POST['ep-status-report-nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['ep-status-report-nonce'] ) ), 'ep-status-report-nonce' ) ) { | |
| 95 | + wp_send_json_error( [ 'message' => __( 'Nonce is not present.', 'elasticpress' ) ], 403 ); | |
| 96 | + } | |
| 97 | + | |
| 98 | + if ( empty( $this->formatted_reports ) ) { | |
| 99 | + $this->formatted_reports = $this->get_reports(); | |
| 100 | + } | |
| 101 | + | |
| 102 | + $post = wp_unslash( $_POST ); | |
| 103 | + | |
| 104 | + if ( empty( $this->formatted_reports[ $post['report'] ] ) ) { | |
| 105 | + wp_send_json_error( [ 'message' => __( 'Status report not found.', 'elasticpress' ) ], 404 ); | |
| 106 | + } | |
| 107 | + | |
| 108 | + $report = $this->formatted_reports[ $post['report'] ]; | |
| 109 | + | |
| 110 | + if ( ! $report instanceof \ElasticPress\StatusReport\AjaxReport ) { | |
| 111 | + wp_send_json_error( [ 'message' => __( 'Report is not an AJAX report.', 'elasticpress' ) ], 403 ); | |
| 112 | + } | |
| 113 | + | |
| 114 | + wp_send_json_success( | |
| 115 | + [ | |
| 116 | + 'groups' => $report->get_groups_ajax(), | |
| 117 | + 'messages' => $report->get_messages(), | |
| 118 | + ], | |
| 119 | + 200 | |
| 120 | + ); | |
| 121 | + } | |
| 122 | + | |
| 123 | + /** | |
| 77 | 124 | * Return all reports available |
| 78 | 125 | * |
| 79 | 126 | * @return array |
| 80 | 127 | */ |
| 81 | - public function get_reports() : array { | |
| 128 | + public function get_reports(): array { | |
| 82 | 129 | $reports = []; |
| 83 | 130 | |
| 84 | - $reports['wordpress'] = new \ElasticPress\StatusReport\WordPress(); | |
| 85 | - $reports['indexable'] = new \ElasticPress\StatusReport\IndexableContent(); | |
| 86 | - $reports['elasticpress'] = new \ElasticPress\StatusReport\ElasticPress(); | |
| 131 | + $query_logger = \ElasticPress\get_container()->get( '\ElasticPress\QueryLogger' ); | |
| 87 | 132 | |
| 88 | - /* this filter is documented in elasticpress.php */ | |
| 89 | - $query_logger = apply_filters( 'ep_query_logger', new \ElasticPress\QueryLogger() ); | |
| 90 | 133 | if ( $query_logger ) { |
| 91 | 134 | $reports['failed-queries'] = new \ElasticPress\StatusReport\FailedQueries( $query_logger ); |
| 92 | 135 | } |
| 93 | 136 | |
| 94 | - $reports['indices'] = new \ElasticPress\StatusReport\Indices(); | |
| 95 | - | |
| 96 | 137 | if ( Utils\is_epio() ) { |
| 97 | 138 | $reports['autosuggest'] = new \ElasticPress\StatusReport\ElasticPressIo(); |
| 98 | 139 | } |
| 99 | 140 | |
| 100 | - $reports['last-sync'] = new \ElasticPress\StatusReport\LastSync(); | |
| 101 | - $reports['features'] = new \ElasticPress\StatusReport\Features(); | |
| 141 | + $reports['wordpress'] = new \ElasticPress\StatusReport\WordPress(); | |
| 142 | + $reports['indexable'] = new \ElasticPress\StatusReport\IndexableContent(); | |
| 143 | + $reports['elasticpress'] = new \ElasticPress\StatusReport\ElasticPress(); | |
| 144 | + $reports['indices'] = new \ElasticPress\StatusReport\Indices(); | |
| 145 | + $reports['last-sync'] = new \ElasticPress\StatusReport\LastSync(); | |
| 146 | + $reports['features'] = new \ElasticPress\StatusReport\Features(); | |
| 102 | 147 | |
| 103 | 148 | /** |
| 104 | 149 | * Filter the reports executed in the Status Report page. |
| 105 | 150 | * |
| @@ -109,14 +154,17 @@ | ||
| 109 | 154 | * @return {array<Report>} New array of reports |
| 110 | 155 | */ |
| 111 | 156 | $filtered_reports = apply_filters( 'ep_status_report_reports', $reports ); |
| 112 | 157 | |
| 113 | - $skipped_reports = ! empty( $_GET['ep-skip-reports'] ) ? (array) $_GET['ep-skip-reports'] : []; // phpcs:ignore WordPress.Security.NonceVerification | |
| 114 | - $skipped_reports = array_map( 'sanitize_text_field', $skipped_reports ); | |
| 158 | + // phpcs:disable WordPress.Security.NonceVerification | |
| 159 | + $skipped_reports = isset( $_GET['ep-skip-reports'] ) ? | |
| 160 | + array_map( 'sanitize_text_field', (array) wp_unslash( $_GET['ep-skip-reports'] ) ) : | |
| 161 | + []; | |
| 162 | + // phpcs:enable WordPress.Security.NonceVerification | |
| 115 | 163 | |
| 116 | 164 | $filtered_reports = array_filter( |
| 117 | 165 | $filtered_reports, |
| 118 | - function( $report_slug ) use ( $skipped_reports ) { | |
| 166 | + function ( $report_slug ) use ( $skipped_reports ) { | |
| 119 | 167 | return ! in_array( $report_slug, $skipped_reports, true ); |
| 120 | 168 | }, |
| 121 | 169 | ARRAY_FILTER_USE_KEY |
| 122 | 170 | ); |
| @@ -124,33 +172,31 @@ | ||
| 124 | 172 | return $filtered_reports; |
| 125 | 173 | } |
| 126 | 174 | |
| 127 | 175 | /** |
| 128 | - * Render all reports (HTML and Copy & Paste button) | |
| 176 | + * Process and format the reports, then store them in the `formatted_reports` attribute. | |
| 177 | + * | |
| 178 | + * @since 4.5.0 | |
| 179 | + * @return array | |
| 129 | 180 | */ |
| 130 | - public function render_reports() { | |
| 131 | - $reports = $this->get_reports(); | |
| 181 | + protected function get_formatted_reports(): array { | |
| 182 | + if ( empty( $this->formatted_reports ) ) { | |
| 183 | + $reports = $this->get_reports(); | |
| 132 | 184 | |
| 133 | - $copy_paste_output = []; | |
| 134 | - | |
| 135 | - foreach ( $reports as $report ) { | |
| 136 | - $title = $report->get_title(); | |
| 137 | - $groups = $report->get_groups(); | |
| 138 | - | |
| 139 | - $copy_paste_output[] = $this->render_copy_paste_report( $title, $groups ); | |
| 185 | + $this->formatted_reports = array_map( | |
| 186 | + function ( $report ) { | |
| 187 | + return [ | |
| 188 | + 'actions' => $report->get_actions(), | |
| 189 | + 'groups' => $report->get_groups(), | |
| 190 | + 'messages' => $report->get_messages(), | |
| 191 | + 'title' => $report->get_title(), | |
| 192 | + 'isAjaxReport' => $report instanceof \ElasticPress\StatusReport\AjaxReport, | |
| 193 | + ]; | |
| 194 | + }, | |
| 195 | + $reports | |
| 196 | + ); | |
| 140 | 197 | } |
| 141 | - | |
| 142 | - ?> | |
| 143 | - <p><?php esc_html_e( 'This screen provides a list of information related to ElasticPress and synced content that can be helpful during troubleshooting. This list can also be copy/pasted and shared as needed.', 'elasticpress' ); ?></p> | |
| 144 | - <p class="ep-copy-button-wrapper"> | |
| 145 | - <button class="button" data-clipboard-text="<?php echo esc_attr( implode( "\n\n", $copy_paste_output ) ); ?>" id="ep-copy-report" type="button"> | |
| 146 | - <?php esc_html_e( 'Copy status report to clipboard', 'elasticpress' ); ?> | |
| 147 | - </button> | |
| 148 | - <span class="ep-copy-button-wrapper__success"> | |
| 149 | - <?php esc_html_e( 'Copied!', 'elasticpress' ); ?> | |
| 150 | - </span> | |
| 151 | - </p> | |
| 152 | - <?php | |
| 198 | + return $this->formatted_reports; | |
| 153 | 199 | } |
| 154 | 200 | |
| 155 | 201 | /** |
| 156 | 202 | * Render the copy & paste report |
| @@ -156,13 +202,20 @@ | ||
| 156 | 202 | * Render the copy & paste report |
| 157 | 203 | * |
| 158 | 204 | * @param string $title Report title |
| 159 | 205 | * @param array $groups Report groups |
| 206 | + * @param bool $is_ajax_report Whether the report is an AJAX report | |
| 207 | + * | |
| 160 | 208 | * @return string |
| 161 | 209 | */ |
| 162 | - protected function render_copy_paste_report( string $title, array $groups ) : string { | |
| 210 | + protected function render_copy_paste_report( string $title, array $groups, bool $is_ajax_report = false ): string { | |
| 163 | 211 | $output = "## {$title} ##\n\n"; |
| 164 | 212 | |
| 213 | + if ( $is_ajax_report ) { | |
| 214 | + $output .= $this->render_pending_generation(); | |
| 215 | + return $output; | |
| 216 | + } | |
| 217 | + | |
| 165 | 218 | foreach ( $groups as $group ) { |
| 166 | 219 | $output .= "### {$group['title']} ###\n"; |
| 167 | 220 | foreach ( $group['fields'] as $slug => $field ) { |
| 168 | 221 | $value = $field['value'] ?? ''; |
| @@ -192,6 +245,62 @@ | ||
| 192 | 245 | return $value ? 'true' : 'false'; |
| 193 | 246 | } |
| 194 | 247 | |
| 195 | 248 | return (string) $value; |
| 249 | + } | |
| 250 | + | |
| 251 | + /** | |
| 252 | + * Render a message when the report is pending generation | |
| 253 | + * | |
| 254 | + * @return string | |
| 255 | + */ | |
| 256 | + protected function render_pending_generation() { | |
| 257 | + return __( 'Please generate a full report to see the content of this group.', 'elasticpress' ); | |
| 258 | + } | |
| 259 | + | |
| 260 | + /** | |
| 261 | + * Display a badge in the admin menu if there's admin notices from | |
| 262 | + * ElasticPress.io. | |
| 263 | + * | |
| 264 | + * @return void | |
| 265 | + */ | |
| 266 | + public function admin_menu_count() { | |
| 267 | + global $menu, $submenu; | |
| 268 | + | |
| 269 | + $messages = \ElasticPress\get_container()->get( '\ElasticPress\ElasticPressIo' )->get_endpoint_messages(); | |
| 270 | + | |
| 271 | + if ( empty( $messages ) ) { | |
| 272 | + return; | |
| 273 | + } | |
| 274 | + | |
| 275 | + $count = count( $messages ); | |
| 276 | + $title = sprintf( | |
| 277 | + /* translators: %d: Number of messages. */ | |
| 278 | + _n( '%s message from ElasticPress.io', '%s messages from ElasticPress.io', $count, 'elasticpress' ), | |
| 279 | + $count | |
| 280 | + ); | |
| 281 | + | |
| 282 | + foreach ( $menu as $key => $value ) { | |
| 283 | + if ( 'elasticpress' === $value[2] ) { | |
| 284 | + $menu[ $key ][0] .= sprintf( | |
| 285 | + ' <span class="update-plugins"><span aria-hidden="true">%1$s</span><span class="screen-reader-text">%2$s</span></span>', | |
| 286 | + esc_html( $count ), | |
| 287 | + esc_attr( $title ) | |
| 288 | + ); | |
| 289 | + } | |
| 290 | + } | |
| 291 | + | |
| 292 | + if ( ! isset( $submenu['elasticpress'] ) ) { | |
| 293 | + return; | |
| 294 | + } | |
| 295 | + | |
| 296 | + foreach ( $submenu['elasticpress'] as $key => $value ) { | |
| 297 | + if ( 'elasticpress-status-report' === $value[2] ) { | |
| 298 | + $submenu['elasticpress'][ $key ][0] .= sprintf( | |
| 299 | + ' <span class="menu-counter"><span aria-hidden="true">%1$s</span><span class="screen-reader-text">%2$s</span></span>', | |
| 300 | + esc_html( $count ), | |
| 301 | + esc_attr( $title ) | |
| 302 | + ); | |
| 303 | + } | |
| 304 | + } | |
| 196 | 305 | } |
| 197 | 306 | } |