| @@ -5,8 +5,9 @@ | ||
| 5 | 5 | * @package WPSEO\Admin |
| 6 | 6 | */ |
| 7 | 7 | |
| 8 | 8 | use Yoast\WP\SEO\Context\Meta_Tags_Context; |
| 9 | +use Yoast\WP\SEO\Helpers\Score_Icon_Helper; | |
| 9 | 10 | use Yoast\WP\SEO\Integrations\Admin\Admin_Columns_Cache_Integration; |
| 10 | 11 | use Yoast\WP\SEO\Surfaces\Values\Meta; |
| 11 | 12 | |
| 12 | 13 | /** |
| @@ -42,8 +43,22 @@ | ||
| 42 | 43 | */ |
| 43 | 44 | private $admin_columns_cache; |
| 44 | 45 | |
| 45 | 46 | /** |
| 47 | + * Holds the Score_Icon_Helper. | |
| 48 | + * | |
| 49 | + * @var Score_Icon_Helper | |
| 50 | + */ | |
| 51 | + private $score_icon_helper; | |
| 52 | + | |
| 53 | + /** | |
| 54 | + * Holds the WPSEO_Admin_Asset_Manager instance. | |
| 55 | + * | |
| 56 | + * @var WPSEO_Admin_Asset_Manager | |
| 57 | + */ | |
| 58 | + private $admin_asset_manager; | |
| 59 | + | |
| 60 | + /** | |
| 46 | 61 | * When page analysis is enabled, just initialize the hooks. |
| 47 | 62 | */ |
| 48 | 63 | public function __construct() { |
| 49 | 64 | if ( apply_filters( 'wpseo_use_page_analysis', true ) === true ) { |
| @@ -52,12 +67,16 @@ | ||
| 52 | 67 | |
| 53 | 68 | $this->analysis_seo = new WPSEO_Metabox_Analysis_SEO(); |
| 54 | 69 | $this->analysis_readability = new WPSEO_Metabox_Analysis_Readability(); |
| 55 | 70 | $this->admin_columns_cache = YoastSEO()->classes->get( Admin_Columns_Cache_Integration::class ); |
| 71 | + $this->score_icon_helper = YoastSEO()->helpers->score_icon; | |
| 72 | + $this->admin_asset_manager = YoastSEO()->classes->get( WPSEO_Admin_Asset_Manager::class ); | |
| 56 | 73 | } |
| 57 | 74 | |
| 58 | 75 | /** |
| 59 | 76 | * Sets up up the hooks. |
| 77 | + * | |
| 78 | + * @return void | |
| 60 | 79 | */ |
| 61 | 80 | public function setup_hooks() { |
| 62 | 81 | $this->set_post_type_hooks(); |
| 63 | 82 | |
| @@ -84,16 +103,27 @@ | ||
| 84 | 103 | if ( $this->display_metabox() === false ) { |
| 85 | 104 | return $columns; |
| 86 | 105 | } |
| 87 | 106 | |
| 107 | + $this->admin_asset_manager->enqueue_script( 'edit-page' ); | |
| 108 | + $this->admin_asset_manager->enqueue_style( 'edit-page' ); | |
| 109 | + | |
| 88 | 110 | $added_columns = []; |
| 89 | 111 | |
| 90 | 112 | if ( $this->analysis_seo->is_enabled() ) { |
| 91 | - $added_columns['wpseo-score'] = '<span class="yoast-tooltip yoast-tooltip-n yoast-tooltip-alt" data-label="' . esc_attr__( 'SEO score', 'wordpress-seo' ) . '"><span class="yoast-column-seo-score yoast-column-header-has-tooltip"><span class="screen-reader-text">' . __( 'SEO score', 'wordpress-seo' ) . '</span></span></span>'; | |
| 113 | + $added_columns['wpseo-score'] = '<span class="yoast-column-seo-score yoast-column-header-has-tooltip" data-tooltip-text="' | |
| 114 | + . esc_attr__( 'SEO score', 'wordpress-seo' ) | |
| 115 | + . '"><span class="screen-reader-text">' | |
| 116 | + . __( 'SEO score', 'wordpress-seo' ) | |
| 117 | + . '</span></span>'; | |
| 92 | 118 | } |
| 93 | 119 | |
| 94 | 120 | if ( $this->analysis_readability->is_enabled() ) { |
| 95 | - $added_columns['wpseo-score-readability'] = '<span class="yoast-tooltip yoast-tooltip-n yoast-tooltip-alt" data-label="' . esc_attr__( 'Readability score', 'wordpress-seo' ) . '"><span class="yoast-column-readability yoast-column-header-has-tooltip"><span class="screen-reader-text">' . __( 'Readability score', 'wordpress-seo' ) . '</span></span></span>'; | |
| 121 | + $added_columns['wpseo-score-readability'] = '<span class="yoast-column-readability yoast-column-header-has-tooltip" data-tooltip-text="' | |
| 122 | + . esc_attr__( 'Readability score', 'wordpress-seo' ) | |
| 123 | + . '"><span class="screen-reader-text">' | |
| 124 | + . __( 'Readability score', 'wordpress-seo' ) | |
| 125 | + . '</span></span>'; | |
| 96 | 126 | } |
| 97 | 127 | |
| 98 | 128 | $added_columns['wpseo-title'] = __( 'SEO Title', 'wordpress-seo' ); |
| 99 | 129 | $added_columns['wpseo-metadesc'] = __( 'Meta Desc.', 'wordpress-seo' ); |
| @@ -109,8 +139,10 @@ | ||
| 109 | 139 | * Displays the column content for the given column. |
| 110 | 140 | * |
| 111 | 141 | * @param string $column_name Column to display the content for. |
| 112 | 142 | * @param int $post_id Post to display the column content for. |
| 143 | + * | |
| 144 | + * @return void | |
| 113 | 145 | */ |
| 114 | 146 | public function column_content( $column_name, $post_id ) { |
| 115 | 147 | if ( $this->display_metabox() === false ) { |
| 116 | 148 | return; |
| @@ -119,30 +151,42 @@ | ||
| 119 | 151 | switch ( $column_name ) { |
| 120 | 152 | case 'wpseo-score': |
| 121 | 153 | // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Correctly escaped in render_score_indicator() method. |
| 122 | 154 | echo $this->parse_column_score( $post_id ); |
| 155 | + | |
| 123 | 156 | return; |
| 124 | 157 | |
| 125 | 158 | case 'wpseo-score-readability': |
| 126 | 159 | // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Correctly escaped in render_score_indicator() method. |
| 127 | 160 | echo $this->parse_column_score_readability( $post_id ); |
| 161 | + | |
| 128 | 162 | return; |
| 129 | 163 | |
| 130 | 164 | case 'wpseo-title': |
| 131 | - echo esc_html( $this->get_meta( $post_id )->title ); | |
| 165 | + $meta = $this->get_meta( $post_id ); | |
| 166 | + if ( $meta ) { | |
| 167 | + echo esc_html( $meta->title ); | |
| 168 | + } | |
| 169 | + | |
| 132 | 170 | return; |
| 133 | 171 | |
| 134 | 172 | case 'wpseo-metadesc': |
| 135 | - $metadesc_val = $this->get_meta( $post_id )->meta_description; | |
| 136 | - | |
| 173 | + $metadesc_val = ''; | |
| 174 | + $meta = $this->get_meta( $post_id ); | |
| 175 | + if ( $meta ) { | |
| 176 | + $metadesc_val = $meta->meta_description; | |
| 177 | + } | |
| 137 | 178 | if ( $metadesc_val === '' ) { |
| 138 | 179 | echo '<span aria-hidden="true">—</span><span class="screen-reader-text">', |
| 139 | - esc_html__( 'Meta description not set.', 'wordpress-seo' ), | |
| 140 | - '</span>'; | |
| 180 | + /* translators: Hidden accessibility text. */ | |
| 181 | + esc_html__( 'Meta description not set.', 'wordpress-seo' ), | |
| 182 | + '</span>'; | |
| 183 | + | |
| 141 | 184 | return; |
| 142 | 185 | } |
| 143 | 186 | |
| 144 | 187 | echo esc_html( $metadesc_val ); |
| 188 | + | |
| 145 | 189 | return; |
| 146 | 190 | |
| 147 | 191 | case 'wpseo-focuskw': |
| 148 | 192 | $focuskw_val = WPSEO_Meta::get_value( 'focuskw', $post_id ); |
| @@ -148,14 +192,17 @@ | ||
| 148 | 192 | $focuskw_val = WPSEO_Meta::get_value( 'focuskw', $post_id ); |
| 149 | 193 | |
| 150 | 194 | if ( $focuskw_val === '' ) { |
| 151 | 195 | echo '<span aria-hidden="true">—</span><span class="screen-reader-text">', |
| 152 | - esc_html__( 'Focus keyphrase not set.', 'wordpress-seo' ), | |
| 153 | - '</span>'; | |
| 196 | + /* translators: Hidden accessibility text. */ | |
| 197 | + esc_html__( 'Focus keyphrase not set.', 'wordpress-seo' ), | |
| 198 | + '</span>'; | |
| 199 | + | |
| 154 | 200 | return; |
| 155 | 201 | } |
| 156 | 202 | |
| 157 | 203 | echo esc_html( $focuskw_val ); |
| 204 | + | |
| 158 | 205 | return; |
| 159 | 206 | } |
| 160 | 207 | } |
| 161 | 208 | |
| @@ -174,10 +221,15 @@ | ||
| 174 | 221 | $columns['wpseo-metadesc'] = 'wpseo-metadesc'; |
| 175 | 222 | |
| 176 | 223 | if ( $this->analysis_seo->is_enabled() ) { |
| 177 | 224 | $columns['wpseo-focuskw'] = 'wpseo-focuskw'; |
| 225 | + $columns['wpseo-score'] = 'wpseo-score'; | |
| 178 | 226 | } |
| 179 | 227 | |
| 228 | + if ( $this->analysis_readability->is_enabled() ) { | |
| 229 | + $columns['wpseo-score-readability'] = 'wpseo-score-readability'; | |
| 230 | + } | |
| 231 | + | |
| 180 | 232 | return $columns; |
| 181 | 233 | } |
| 182 | 234 | |
| 183 | 235 | /** |
| @@ -202,8 +254,10 @@ | ||
| 202 | 254 | } |
| 203 | 255 | |
| 204 | 256 | /** |
| 205 | 257 | * Adds a dropdown that allows filtering on the posts SEO Quality. |
| 258 | + * | |
| 259 | + * @return void | |
| 206 | 260 | */ |
| 207 | 261 | public function posts_filter_dropdown() { |
| 208 | 262 | if ( ! $this->can_display_filter() ) { |
| 209 | 263 | return; |
| @@ -210,8 +264,9 @@ | ||
| 210 | 264 | } |
| 211 | 265 | |
| 212 | 266 | $ranks = WPSEO_Rank::get_all_ranks(); |
| 213 | 267 | |
| 268 | + /* translators: Hidden accessibility text. */ | |
| 214 | 269 | echo '<label class="screen-reader-text" for="wpseo-filter">' . esc_html__( 'Filter by SEO Score', 'wordpress-seo' ) . '</label>'; |
| 215 | 270 | echo '<select name="seo_filter" id="wpseo-filter">'; |
| 216 | 271 | |
| 217 | 272 | // phpcs:ignore WordPress.Security.EscapeOutput -- Output is correctly escaped in the generate_option() method. |
| @@ -238,8 +293,9 @@ | ||
| 238 | 293 | } |
| 239 | 294 | |
| 240 | 295 | $ranks = WPSEO_Rank::get_all_readability_ranks(); |
| 241 | 296 | |
| 297 | + /* translators: Hidden accessibility text. */ | |
| 242 | 298 | echo '<label class="screen-reader-text" for="wpseo-readability-filter">' . esc_html__( 'Filter by Readability Score', 'wordpress-seo' ) . '</label>'; |
| 243 | 299 | echo '<select name="readability_filter" id="wpseo-readability-filter">'; |
| 244 | 300 | |
| 245 | 301 | // phpcs:ignore WordPress.Security.EscapeOutput -- Output is correctly escaped in the generate_option() method. |
| @@ -309,8 +365,14 @@ | ||
| 309 | 365 | * |
| 310 | 366 | * @return array The Readability score filter. |
| 311 | 367 | */ |
| 312 | 368 | protected function determine_readability_filters( $readability_filter ) { |
| 369 | + if ( $readability_filter === WPSEO_Rank::NO_FOCUS ) { | |
| 370 | + return $this->create_no_readability_scores_filter(); | |
| 371 | + } | |
| 372 | + if ( $readability_filter === WPSEO_Rank::BAD ) { | |
| 373 | + return $this->create_bad_readability_scores_filter(); | |
| 374 | + } | |
| 313 | 375 | $rank = new WPSEO_Rank( $readability_filter ); |
| 314 | 376 | |
| 315 | 377 | return $this->create_readability_score_filter( $rank->get_starting_score(), $rank->get_end_score() ); |
| 316 | 378 | } |
| @@ -334,9 +396,9 @@ | ||
| 334 | 396 | * Determines whether the passed filter is considered to be valid. |
| 335 | 397 | * |
| 336 | 398 | * @param mixed $filter The filter to check against. |
| 337 | 399 | * |
| 338 | - * @return bool Whether or not the filter is considered valid. | |
| 400 | + * @return bool Whether the filter is considered valid. | |
| 339 | 401 | */ |
| 340 | 402 | protected function is_valid_filter( $filter ) { |
| 341 | 403 | return ! empty( $filter ) && is_string( $filter ); |
| 342 | 404 | } |
| @@ -355,9 +417,9 @@ | ||
| 355 | 417 | |
| 356 | 418 | if ( $this->is_valid_filter( $seo_filter ) ) { |
| 357 | 419 | $active_filters = array_merge( |
| 358 | 420 | $active_filters, |
| 359 | - $this->determine_seo_filters( $seo_filter ) | |
| 421 | + $this->determine_seo_filters( $seo_filter ), | |
| 360 | 422 | ); |
| 361 | 423 | } |
| 362 | 424 | |
| 363 | 425 | if ( $this->is_valid_filter( $readability_filter ) ) { |
| @@ -362,20 +424,43 @@ | ||
| 362 | 424 | |
| 363 | 425 | if ( $this->is_valid_filter( $readability_filter ) ) { |
| 364 | 426 | $active_filters = array_merge( |
| 365 | 427 | $active_filters, |
| 366 | - $this->determine_readability_filters( $readability_filter ) | |
| 428 | + $this->determine_readability_filters( $readability_filter ), | |
| 367 | 429 | ); |
| 368 | 430 | } |
| 369 | 431 | |
| 370 | 432 | if ( $this->is_valid_filter( $current_keyword_filter ) ) { |
| 371 | - $active_filters = array_merge( | |
| 372 | - $active_filters, | |
| 373 | - $this->get_keyword_filter( $current_keyword_filter ) | |
| 433 | + /** | |
| 434 | + * Adapt the meta query used to filter the post overview on keyphrase. | |
| 435 | + * | |
| 436 | + * @internal | |
| 437 | + * | |
| 438 | + * @param array $keyphrase The keyphrase used in the filter. | |
| 439 | + * @param array $keyword_filter The current keyword filter. | |
| 440 | + */ | |
| 441 | + $keyphrase_filter = apply_filters( | |
| 442 | + 'wpseo_change_keyphrase_filter_in_request', | |
| 443 | + $this->get_keyword_filter( $current_keyword_filter ), | |
| 444 | + $current_keyword_filter, | |
| 374 | 445 | ); |
| 446 | + | |
| 447 | + if ( is_array( $keyphrase_filter ) ) { | |
| 448 | + $active_filters = array_merge( | |
| 449 | + $active_filters, | |
| 450 | + [ $keyphrase_filter ], | |
| 451 | + ); | |
| 452 | + } | |
| 375 | 453 | } |
| 376 | 454 | |
| 377 | - return $active_filters; | |
| 455 | + /** | |
| 456 | + * Adapt the active applicable filters on the posts overview. | |
| 457 | + * | |
| 458 | + * @internal | |
| 459 | + * | |
| 460 | + * @param array $active_filters The current applicable filters. | |
| 461 | + */ | |
| 462 | + return apply_filters( 'wpseo_change_applicable_filters', $active_filters ); | |
| 378 | 463 | } |
| 379 | 464 | |
| 380 | 465 | /** |
| 381 | 466 | * Modify the query based on the filters that are being passed. |
| @@ -386,10 +471,24 @@ | ||
| 386 | 471 | */ |
| 387 | 472 | public function column_sort_orderby( $vars ) { |
| 388 | 473 | $collected_filters = $this->collect_filters(); |
| 389 | 474 | |
| 390 | - if ( isset( $vars['orderby'] ) ) { | |
| 391 | - $vars = array_merge( $vars, $this->filter_order_by( $vars['orderby'] ) ); | |
| 475 | + $order_by_column = $vars['orderby']; | |
| 476 | + if ( isset( $order_by_column ) ) { | |
| 477 | + // Based on the selected column, create a meta query. | |
| 478 | + $order_by = $this->filter_order_by( $order_by_column ); | |
| 479 | + | |
| 480 | + /** | |
| 481 | + * Adapt the order by part of the query on the posts overview. | |
| 482 | + * | |
| 483 | + * @internal | |
| 484 | + * | |
| 485 | + * @param array $order_by The current order by. | |
| 486 | + * @param string $order_by_column The current order by column. | |
| 487 | + */ | |
| 488 | + $order_by = apply_filters( 'wpseo_change_order_by', $order_by, $order_by_column ); | |
| 489 | + | |
| 490 | + $vars = array_merge( $vars, $order_by ); | |
| 392 | 491 | } |
| 393 | 492 | |
| 394 | 493 | return $this->build_filter_query( $vars, $collected_filters ); |
| 395 | 494 | } |
| @@ -431,39 +530,59 @@ | ||
| 431 | 530 | |
| 432 | 531 | /** |
| 433 | 532 | * Retrieves the post type from the $_GET variable. |
| 434 | 533 | * |
| 435 | - * @return string The current post type. | |
| 534 | + * @return string|null The sanitized current post type or null when the variable is not set in $_GET. | |
| 436 | 535 | */ |
| 437 | 536 | public function get_current_post_type() { |
| 438 | - return filter_input( INPUT_GET, 'post_type' ); | |
| 537 | + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. | |
| 538 | + if ( isset( $_GET['post_type'] ) && is_string( $_GET['post_type'] ) ) { | |
| 539 | + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. | |
| 540 | + return sanitize_text_field( wp_unslash( $_GET['post_type'] ) ); | |
| 541 | + } | |
| 542 | + return null; | |
| 439 | 543 | } |
| 440 | 544 | |
| 441 | 545 | /** |
| 442 | 546 | * Retrieves the SEO filter from the $_GET variable. |
| 443 | 547 | * |
| 444 | - * @return string The current post type. | |
| 548 | + * @return string|null The sanitized seo filter or null when the variable is not set in $_GET. | |
| 445 | 549 | */ |
| 446 | 550 | public function get_current_seo_filter() { |
| 447 | - return filter_input( INPUT_GET, 'seo_filter' ); | |
| 551 | + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. | |
| 552 | + if ( isset( $_GET['seo_filter'] ) && is_string( $_GET['seo_filter'] ) ) { | |
| 553 | + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. | |
| 554 | + return sanitize_text_field( wp_unslash( $_GET['seo_filter'] ) ); | |
| 555 | + } | |
| 556 | + return null; | |
| 448 | 557 | } |
| 449 | 558 | |
| 450 | 559 | /** |
| 451 | 560 | * Retrieves the Readability filter from the $_GET variable. |
| 452 | 561 | * |
| 453 | - * @return string The current post type. | |
| 562 | + * @return string|null The sanitized readability filter or null when the variable is not set in $_GET. | |
| 454 | 563 | */ |
| 455 | 564 | public function get_current_readability_filter() { |
| 456 | - return filter_input( INPUT_GET, 'readability_filter' ); | |
| 565 | + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. | |
| 566 | + if ( isset( $_GET['readability_filter'] ) && is_string( $_GET['readability_filter'] ) ) { | |
| 567 | + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. | |
| 568 | + return sanitize_text_field( wp_unslash( $_GET['readability_filter'] ) ); | |
| 569 | + } | |
| 570 | + return null; | |
| 457 | 571 | } |
| 458 | 572 | |
| 459 | 573 | /** |
| 460 | 574 | * Retrieves the keyword filter from the $_GET variable. |
| 461 | 575 | * |
| 462 | - * @return string The current post type. | |
| 576 | + * @return string|null The sanitized seo keyword filter or null when the variable is not set in $_GET. | |
| 463 | 577 | */ |
| 464 | 578 | public function get_current_keyword_filter() { |
| 465 | - return filter_input( INPUT_GET, 'seo_kw_filter' ); | |
| 579 | + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. | |
| 580 | + if ( isset( $_GET['seo_kw_filter'] ) && is_string( $_GET['seo_kw_filter'] ) ) { | |
| 581 | + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information. | |
| 582 | + return sanitize_text_field( wp_unslash( $_GET['seo_kw_filter'] ) ); | |
| 583 | + } | |
| 584 | + return null; | |
| 466 | 585 | } |
| 467 | 586 | |
| 468 | 587 | /** |
| 469 | 588 | * Uses the vars to create a complete filter query that can later be executed to filter out posts. |
| @@ -484,9 +603,9 @@ | ||
| 484 | 603 | |
| 485 | 604 | $current_seo_filter = $this->get_current_seo_filter(); |
| 486 | 605 | |
| 487 | 606 | // This only applies for the SEO score filter because it can because the SEO score can be altered by the no-index option. |
| 488 | - if ( $this->is_valid_filter( $current_seo_filter ) && ! in_array( $current_seo_filter, [ WPSEO_Rank::NO_INDEX, WPSEO_Rank::NO_FOCUS ], true ) ) { | |
| 607 | + if ( $this->is_valid_filter( $current_seo_filter ) && ! in_array( $current_seo_filter, [ WPSEO_Rank::NO_INDEX ], true ) ) { | |
| 489 | 608 | $result['meta_query'] = array_merge( $result['meta_query'], [ $this->get_meta_robots_query_values() ] ); |
| 490 | 609 | } |
| 491 | 610 | |
| 492 | 611 | return array_merge( $vars, $result ); |
| @@ -497,9 +616,9 @@ | ||
| 497 | 616 | * |
| 498 | 617 | * @param number $low The lower boundary of the score. |
| 499 | 618 | * @param number $high The higher boundary of the score. |
| 500 | 619 | * |
| 501 | - * @return array The Readability Score filter. | |
| 620 | + * @return array<array<string>> The Readability Score filter. | |
| 502 | 621 | */ |
| 503 | 622 | protected function create_readability_score_filter( $low, $high ) { |
| 504 | 623 | return [ |
| 505 | 624 | [ |
| @@ -516,9 +635,9 @@ | ||
| 516 | 635 | * |
| 517 | 636 | * @param number $low The lower boundary of the score. |
| 518 | 637 | * @param number $high The higher boundary of the score. |
| 519 | 638 | * |
| 520 | - * @return array The SEO score filter. | |
| 639 | + * @return array<array<string>> The SEO score filter. | |
| 521 | 640 | */ |
| 522 | 641 | protected function create_seo_score_filter( $low, $high ) { |
| 523 | 642 | return [ |
| 524 | 643 | [ |
| @@ -532,9 +651,9 @@ | ||
| 532 | 651 | |
| 533 | 652 | /** |
| 534 | 653 | * Creates a filter to retrieve posts that were set to no-index. |
| 535 | 654 | * |
| 536 | - * @return array Array containin the no-index filter. | |
| 655 | + * @return array<array<string>> Array containin the no-index filter. | |
| 537 | 656 | */ |
| 538 | 657 | protected function create_no_index_filter() { |
| 539 | 658 | return [ |
| 540 | 659 | [ |
| @@ -547,26 +666,82 @@ | ||
| 547 | 666 | |
| 548 | 667 | /** |
| 549 | 668 | * Creates a filter to retrieve posts that have no keyword set. |
| 550 | 669 | * |
| 551 | - * @return array Array containing the no focus keyword filter. | |
| 670 | + * @return array<array<string>> Array containing the no focus keyword filter. | |
| 552 | 671 | */ |
| 553 | 672 | protected function create_no_focus_keyword_filter() { |
| 554 | 673 | return [ |
| 555 | 674 | [ |
| 556 | - 'key' => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex', | |
| 675 | + 'key' => WPSEO_Meta::$meta_prefix . 'linkdex', | |
| 557 | 676 | 'value' => 'needs-a-value-anyway', |
| 558 | 677 | 'compare' => 'NOT EXISTS', |
| 559 | 678 | ], |
| 679 | + ]; | |
| 680 | + } | |
| 681 | + | |
| 682 | + /** | |
| 683 | + * Creates a filter to retrieve posts that have not been analyzed for readability yet. | |
| 684 | + * | |
| 685 | + * @return array<array<string>> Array containing the no readability filter. | |
| 686 | + */ | |
| 687 | + protected function create_no_readability_scores_filter() { | |
| 688 | + // We check the existence of the Estimated Reading Time, because readability scores of posts that haven't been manually saved while Yoast SEO is active, don't exist, which is also the case for posts with not enough content. | |
| 689 | + // Meanwhile, the ERT is a solid indicator of whether a post has ever been saved (aka, analyzed), so we're using that. | |
| 690 | + $rank = new WPSEO_Rank( WPSEO_Rank::BAD ); | |
| 691 | + return [ | |
| 560 | 692 | [ |
| 561 | - 'key' => WPSEO_Meta::$meta_prefix . 'linkdex', | |
| 693 | + 'key' => WPSEO_Meta::$meta_prefix . 'estimated-reading-time-minutes', | |
| 562 | 694 | 'value' => 'needs-a-value-anyway', |
| 563 | 695 | 'compare' => 'NOT EXISTS', |
| 564 | 696 | ], |
| 697 | + [ | |
| 698 | + 'relation' => 'OR', | |
| 699 | + [ | |
| 700 | + 'key' => WPSEO_Meta::$meta_prefix . 'content_score', | |
| 701 | + 'value' => $rank->get_starting_score(), | |
| 702 | + 'type' => 'numeric', | |
| 703 | + 'compare' => '<', | |
| 704 | + ], | |
| 705 | + [ | |
| 706 | + 'key' => WPSEO_Meta::$meta_prefix . 'content_score', | |
| 707 | + 'value' => 'needs-a-value-anyway', | |
| 708 | + 'compare' => 'NOT EXISTS', | |
| 709 | + ], | |
| 710 | + ], | |
| 565 | 711 | ]; |
| 566 | 712 | } |
| 567 | 713 | |
| 568 | 714 | /** |
| 715 | + * Creates a filter to retrieve posts that have bad readability scores, including those that have not enough content to have one. | |
| 716 | + * | |
| 717 | + * @return array<array<string>> Array containing the bad readability filter. | |
| 718 | + */ | |
| 719 | + protected function create_bad_readability_scores_filter() { | |
| 720 | + $rank = new WPSEO_Rank( WPSEO_Rank::BAD ); | |
| 721 | + return [ | |
| 722 | + 'relation' => 'OR', | |
| 723 | + [ | |
| 724 | + 'key' => WPSEO_Meta::$meta_prefix . 'content_score', | |
| 725 | + 'value' => [ $rank->get_starting_score(), $rank->get_end_score() ], | |
| 726 | + 'type' => 'numeric', | |
| 727 | + 'compare' => 'BETWEEN', | |
| 728 | + ], | |
| 729 | + [ | |
| 730 | + [ | |
| 731 | + 'key' => WPSEO_Meta::$meta_prefix . 'content_score', | |
| 732 | + 'value' => 'needs-a-value-anyway', | |
| 733 | + 'compare' => 'NOT EXISTS', | |
| 734 | + ], | |
| 735 | + [ | |
| 736 | + 'key' => WPSEO_Meta::$meta_prefix . 'estimated-reading-time-minutes', | |
| 737 | + 'compare' => 'EXISTS', | |
| 738 | + ], | |
| 739 | + ], | |
| 740 | + ]; | |
| 741 | + } | |
| 742 | + | |
| 743 | + /** | |
| 569 | 744 | * Determines whether a particular post_id is of an indexable post type. |
| 570 | 745 | * |
| 571 | 746 | * @param string $post_id The post ID to check. |
| 572 | 747 | * |
| @@ -602,14 +777,15 @@ | ||
| 602 | 777 | * Returns filters when $order_by is matched in the if-statement. |
| 603 | 778 | * |
| 604 | 779 | * @param string $order_by The ID of the column by which to order the posts. |
| 605 | 780 | * |
| 606 | - * @return array Array containing the order filters. | |
| 781 | + * @return array<string> Array containing the order filters. | |
| 607 | 782 | */ |
| 608 | 783 | private function filter_order_by( $order_by ) { |
| 609 | 784 | switch ( $order_by ) { |
| 610 | 785 | case 'wpseo-metadesc': |
| 611 | 786 | return [ |
| 787 | + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Reason: Only used when user requests sorting. | |
| 612 | 788 | 'meta_key' => WPSEO_Meta::$meta_prefix . 'metadesc', |
| 613 | 789 | 'orderby' => 'meta_value', |
| 614 | 790 | ]; |
| 615 | 791 | |
| @@ -614,11 +790,26 @@ | ||
| 614 | 790 | ]; |
| 615 | 791 | |
| 616 | 792 | case 'wpseo-focuskw': |
| 617 | 793 | return [ |
| 794 | + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Reason: Only used when user requests sorting. | |
| 618 | 795 | 'meta_key' => WPSEO_Meta::$meta_prefix . 'focuskw', |
| 619 | 796 | 'orderby' => 'meta_value', |
| 620 | 797 | ]; |
| 798 | + | |
| 799 | + case 'wpseo-score': | |
| 800 | + return [ | |
| 801 | + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Reason: Only used when user requests sorting. | |
| 802 | + 'meta_key' => WPSEO_Meta::$meta_prefix . 'linkdex', | |
| 803 | + 'orderby' => 'meta_value_num', | |
| 804 | + ]; | |
| 805 | + | |
| 806 | + case 'wpseo-score-readability': | |
| 807 | + return [ | |
| 808 | + // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Reason: Only used when user requests sorting. | |
| 809 | + 'meta_key' => WPSEO_Meta::$meta_prefix . 'content_score', | |
| 810 | + 'orderby' => 'meta_value_num', | |
| 811 | + ]; | |
| 621 | 812 | } |
| 622 | 813 | |
| 623 | 814 | return []; |
| 624 | 815 | } |
| @@ -630,29 +821,13 @@ | ||
| 630 | 821 | * |
| 631 | 822 | * @return string The HTML for the SEO score indicator. |
| 632 | 823 | */ |
| 633 | 824 | private function parse_column_score( $post_id ) { |
| 634 | - if ( ! $this->is_indexable( $post_id ) ) { | |
| 635 | - $rank = new WPSEO_Rank( WPSEO_Rank::NO_INDEX ); | |
| 636 | - $title = __( 'Post is set to noindex.', 'wordpress-seo' ); | |
| 825 | + $meta = $this->get_meta( $post_id ); | |
| 637 | 826 | |
| 638 | - WPSEO_Meta::set_value( 'linkdex', 0, $post_id ); | |
| 639 | - | |
| 640 | - return $this->render_score_indicator( $rank, $title ); | |
| 827 | + if ( $meta ) { | |
| 828 | + return $this->score_icon_helper->for_seo( $meta->indexable, '', __( 'Post is set to noindex.', 'wordpress-seo' ) ); | |
| 641 | 829 | } |
| 642 | - | |
| 643 | - if ( WPSEO_Meta::get_value( 'focuskw', $post_id ) === '' ) { | |
| 644 | - $rank = new WPSEO_Rank( WPSEO_Rank::BAD ); | |
| 645 | - $title = __( 'Focus keyphrase not set.', 'wordpress-seo' ); | |
| 646 | - | |
| 647 | - return $this->render_score_indicator( $rank, $title ); | |
| 648 | - } | |
| 649 | - | |
| 650 | - $score = (int) WPSEO_Meta::get_value( 'linkdex', $post_id ); | |
| 651 | - $rank = WPSEO_Rank::from_numeric_score( $score ); | |
| 652 | - $title = $rank->get_label(); | |
| 653 | - | |
| 654 | - return $this->render_score_indicator( $rank, $title ); | |
| 655 | 830 | } |
| 656 | 831 | |
| 657 | 832 | /** |
| 658 | 833 | * Parsing the readability score column. |
| @@ -661,16 +836,18 @@ | ||
| 661 | 836 | * |
| 662 | 837 | * @return string The HTML for the readability score indicator. |
| 663 | 838 | */ |
| 664 | 839 | private function parse_column_score_readability( $post_id ) { |
| 665 | - $score = (int) WPSEO_Meta::get_value( 'content_score', $post_id ); | |
| 666 | - $rank = WPSEO_Rank::from_numeric_score( $score ); | |
| 667 | - | |
| 668 | - return $this->render_score_indicator( $rank ); | |
| 840 | + $meta = $this->get_meta( $post_id ); | |
| 841 | + if ( $meta ) { | |
| 842 | + return $this->score_icon_helper->for_readability( $meta->indexable->readability_score ); | |
| 843 | + } | |
| 669 | 844 | } |
| 670 | 845 | |
| 671 | 846 | /** |
| 672 | 847 | * Sets up the hooks for the post_types. |
| 848 | + * | |
| 849 | + * @return void | |
| 673 | 850 | */ |
| 674 | 851 | private function set_post_type_hooks() { |
| 675 | 852 | $post_types = WPSEO_Post_Type::get_accessible_post_types(); |
| 676 | 853 | |
| @@ -701,9 +878,9 @@ | ||
| 701 | 878 | * |
| 702 | 879 | * @return bool Whether or not the meta box (and associated columns etc) should be hidden. |
| 703 | 880 | */ |
| 704 | 881 | private function display_metabox( $post_type = null ) { |
| 705 | - $current_post_type = sanitize_text_field( $this->get_current_post_type() ); | |
| 882 | + $current_post_type = $this->get_current_post_type(); | |
| 706 | 883 | |
| 707 | 884 | if ( ! isset( $post_type ) && ! empty( $current_post_type ) ) { |
| 708 | 885 | $post_type = $current_post_type; |
| 709 | 886 | } |
| @@ -708,24 +885,8 @@ | ||
| 708 | 885 | $post_type = $current_post_type; |
| 709 | 886 | } |
| 710 | 887 | |
| 711 | 888 | return WPSEO_Utils::is_metabox_active( $post_type, 'post_type' ); |
| 712 | - } | |
| 713 | - | |
| 714 | - /** | |
| 715 | - * Renders the score indicator. | |
| 716 | - * | |
| 717 | - * @param WPSEO_Rank $rank The rank this indicator should have. | |
| 718 | - * @param string $title Optional. The title for this rank, defaults to the title of the rank. | |
| 719 | - * | |
| 720 | - * @return string The HTML for a score indicator. | |
| 721 | - */ | |
| 722 | - private function render_score_indicator( $rank, $title = '' ) { | |
| 723 | - if ( empty( $title ) ) { | |
| 724 | - $title = $rank->get_label(); | |
| 725 | - } | |
| 726 | - | |
| 727 | - return '<div aria-hidden="true" title="' . esc_attr( $title ) . '" class="' . esc_attr( 'wpseo-score-icon ' . $rank->get_css_class() ) . '"></div><span class="screen-reader-text wpseo-score-text">' . esc_html( $title ) . '</span>'; | |
| 728 | 889 | } |
| 729 | 890 | |
| 730 | 891 | /** |
| 731 | 892 | * Determines whether or not filter dropdowns should be displayed. |