• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Yoast / wordpress-seo / 0fdb51d5ef87fb55b55da5a950cf7350c155980d

11 Mar 2025 10:16AM UTC coverage: 53.422% (-1.3%) from 54.687%
0fdb51d5ef87fb55b55da5a950cf7350c155980d

push

github

web-flow
Merge pull request #22086 from Yoast/add-pregnant-women-to-potentially-non-inclusive-phrases

Inclusive language: Add 'pregnant women' to potentially non-inclusive phrases

7918 of 13987 branches covered (56.61%)

Branch coverage included in aggregate %.

30526 of 57976 relevant lines covered (52.65%)

41097.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

21.67
/admin/class-meta-columns.php
1
<?php
2
/**
3
 * WPSEO plugin file.
4
 *
5
 * @package WPSEO\Admin
6
 */
7

8
use Yoast\WP\SEO\Context\Meta_Tags_Context;
9
use Yoast\WP\SEO\Helpers\Score_Icon_Helper;
10
use Yoast\WP\SEO\Integrations\Admin\Admin_Columns_Cache_Integration;
11
use Yoast\WP\SEO\Surfaces\Values\Meta;
12

13
/**
14
 * Class WPSEO_Meta_Columns.
15
 */
16
class WPSEO_Meta_Columns {
17

18
        /**
19
         * Holds the context objects for each indexable.
20
         *
21
         * @var Meta_Tags_Context[]
22
         */
23
        protected $context = [];
24

25
        /**
26
         * Holds the SEO analysis.
27
         *
28
         * @var WPSEO_Metabox_Analysis_SEO
29
         */
30
        private $analysis_seo;
31

32
        /**
33
         * Holds the readability analysis.
34
         *
35
         * @var WPSEO_Metabox_Analysis_Readability
36
         */
37
        private $analysis_readability;
38

39
        /**
40
         * Admin columns cache.
41
         *
42
         * @var Admin_Columns_Cache_Integration
43
         */
44
        private $admin_columns_cache;
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
        /**
61
         * When page analysis is enabled, just initialize the hooks.
62
         */
63
        public function __construct() {
×
64
                if ( apply_filters( 'wpseo_use_page_analysis', true ) === true ) {
×
65
                        add_action( 'admin_init', [ $this, 'setup_hooks' ] );
×
66
                }
67

68
                $this->analysis_seo         = new WPSEO_Metabox_Analysis_SEO();
×
69
                $this->analysis_readability = new WPSEO_Metabox_Analysis_Readability();
×
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 );
×
73
        }
74

75
        /**
76
         * Sets up up the hooks.
77
         *
78
         * @return void
79
         */
80
        public function setup_hooks() {
×
81
                $this->set_post_type_hooks();
×
82

83
                if ( $this->analysis_seo->is_enabled() ) {
×
84
                        add_action( 'restrict_manage_posts', [ $this, 'posts_filter_dropdown' ] );
×
85
                }
86

87
                if ( $this->analysis_readability->is_enabled() ) {
×
88
                        add_action( 'restrict_manage_posts', [ $this, 'posts_filter_dropdown_readability' ] );
×
89
                }
90

91
                add_filter( 'request', [ $this, 'column_sort_orderby' ] );
×
92
                add_filter( 'default_hidden_columns', [ $this, 'column_hidden' ], 10, 1 );
×
93
        }
94

95
        /**
96
         * Adds the column headings for the SEO plugin for edit posts / pages overview.
97
         *
98
         * @param array $columns Already existing columns.
99
         *
100
         * @return array Array containing the column headings.
101
         */
102
        public function column_heading( $columns ) {
6✔
103
                if ( $this->display_metabox() === false ) {
6✔
104
                        return $columns;
×
105
                }
106

107
                $this->admin_asset_manager->enqueue_script( 'edit-page' );
6✔
108
                $this->admin_asset_manager->enqueue_style( 'edit-page' );
6✔
109

110
                $added_columns = [];
6✔
111

112
                if ( $this->analysis_seo->is_enabled() ) {
6✔
113
                        $added_columns['wpseo-score'] = '<span class="yoast-column-seo-score yoast-column-header-has-tooltip" data-tooltip-text="'
6✔
114
                                                                                        . esc_attr__( 'SEO score', 'wordpress-seo' )
6✔
115
                                                                                        . '"><span class="screen-reader-text">'
6✔
116
                                                                                        . __( 'SEO score', 'wordpress-seo' )
6✔
117
                                                                                        . '</span></span>';
6✔
118
                }
119

120
                if ( $this->analysis_readability->is_enabled() ) {
6✔
121
                        $added_columns['wpseo-score-readability'] = '<span class="yoast-column-readability yoast-column-header-has-tooltip" data-tooltip-text="'
6✔
122
                                                                                                                . esc_attr__( 'Readability score', 'wordpress-seo' )
6✔
123
                                                                                                                . '"><span class="screen-reader-text">'
6✔
124
                                                                                                                . __( 'Readability score', 'wordpress-seo' )
6✔
125
                                                                                                                . '</span></span>';
6✔
126
                }
127

128
                $added_columns['wpseo-title']    = __( 'SEO Title', 'wordpress-seo' );
6✔
129
                $added_columns['wpseo-metadesc'] = __( 'Meta Desc.', 'wordpress-seo' );
6✔
130

131
                if ( $this->analysis_seo->is_enabled() ) {
6✔
132
                        $added_columns['wpseo-focuskw'] = __( 'Keyphrase', 'wordpress-seo' );
6✔
133
                }
134

135
                return array_merge( $columns, $added_columns );
6✔
136
        }
137

138
        /**
139
         * Displays the column content for the given column.
140
         *
141
         * @param string $column_name Column to display the content for.
142
         * @param int    $post_id     Post to display the column content for.
143
         *
144
         * @return void
145
         */
146
        public function column_content( $column_name, $post_id ) {
×
147
                if ( $this->display_metabox() === false ) {
×
148
                        return;
×
149
                }
150

151
                switch ( $column_name ) {
152
                        case 'wpseo-score':
×
153
                                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Correctly escaped in render_score_indicator() method.
154
                                echo $this->parse_column_score( $post_id );
×
155

156
                                return;
×
157

158
                        case 'wpseo-score-readability':
×
159
                                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Correctly escaped in render_score_indicator() method.
160
                                echo $this->parse_column_score_readability( $post_id );
×
161

162
                                return;
×
163

164
                        case 'wpseo-title':
×
165
                                $meta = $this->get_meta( $post_id );
×
166
                                if ( $meta ) {
×
167
                                        echo esc_html( $meta->title );
×
168
                                }
169

170
                                return;
×
171

172
                        case 'wpseo-metadesc':
×
173
                                $metadesc_val = '';
×
174
                                $meta         = $this->get_meta( $post_id );
×
175
                                if ( $meta ) {
×
176
                                        $metadesc_val = $meta->meta_description;
×
177
                                }
178
                                if ( $metadesc_val === '' ) {
×
179
                                        echo '<span aria-hidden="true">&#8212;</span><span class="screen-reader-text">',
×
180
                                        /* translators: Hidden accessibility text. */
181
                                        esc_html__( 'Meta description not set.', 'wordpress-seo' ),
×
182
                                        '</span>';
×
183

184
                                        return;
×
185
                                }
186

187
                                echo esc_html( $metadesc_val );
×
188

189
                                return;
×
190

191
                        case 'wpseo-focuskw':
×
192
                                $focuskw_val = WPSEO_Meta::get_value( 'focuskw', $post_id );
×
193

194
                                if ( $focuskw_val === '' ) {
×
195
                                        echo '<span aria-hidden="true">&#8212;</span><span class="screen-reader-text">',
×
196
                                        /* translators: Hidden accessibility text. */
197
                                        esc_html__( 'Focus keyphrase not set.', 'wordpress-seo' ),
×
198
                                        '</span>';
×
199

200
                                        return;
×
201
                                }
202

203
                                echo esc_html( $focuskw_val );
×
204

205
                                return;
×
206
                }
207
        }
208

209
        /**
210
         * Indicates which of the SEO columns are sortable.
211
         *
212
         * @param array $columns Appended with their orderby variable.
213
         *
214
         * @return array Array containing the sortable columns.
215
         */
216
        public function column_sort( $columns ) {
×
217
                if ( $this->display_metabox() === false ) {
×
218
                        return $columns;
×
219
                }
220

221
                $columns['wpseo-metadesc'] = 'wpseo-metadesc';
×
222

223
                if ( $this->analysis_seo->is_enabled() ) {
×
224
                        $columns['wpseo-focuskw'] = 'wpseo-focuskw';
×
225
                        $columns['wpseo-score']   = 'wpseo-score';
×
226
                }
227

228
                if ( $this->analysis_readability->is_enabled() ) {
×
229
                        $columns['wpseo-score-readability'] = 'wpseo-score-readability';
×
230
                }
231

232
                return $columns;
×
233
        }
234

235
        /**
236
         * Hides the SEO title, meta description and focus keyword columns if the user hasn't chosen which columns to hide.
237
         *
238
         * @param array $hidden The hidden columns.
239
         *
240
         * @return array Array containing the columns to hide.
241
         */
242
        public function column_hidden( $hidden ) {
6✔
243
                if ( ! is_array( $hidden ) ) {
6✔
244
                        $hidden = [];
2✔
245
                }
246

247
                array_push( $hidden, 'wpseo-title', 'wpseo-metadesc' );
6✔
248

249
                if ( $this->analysis_seo->is_enabled() ) {
6✔
250
                        $hidden[] = 'wpseo-focuskw';
6✔
251
                }
252

253
                return $hidden;
6✔
254
        }
255

256
        /**
257
         * Adds a dropdown that allows filtering on the posts SEO Quality.
258
         *
259
         * @return void
260
         */
261
        public function posts_filter_dropdown() {
×
262
                if ( ! $this->can_display_filter() ) {
×
263
                        return;
×
264
                }
265

266
                $ranks = WPSEO_Rank::get_all_ranks();
×
267

268
                /* translators: Hidden accessibility text. */
269
                echo '<label class="screen-reader-text" for="wpseo-filter">' . esc_html__( 'Filter by SEO Score', 'wordpress-seo' ) . '</label>';
×
270
                echo '<select name="seo_filter" id="wpseo-filter">';
×
271

272
                // phpcs:ignore WordPress.Security.EscapeOutput -- Output is correctly escaped in the generate_option() method.
273
                echo $this->generate_option( '', __( 'All SEO Scores', 'wordpress-seo' ) );
×
274

275
                foreach ( $ranks as $rank ) {
×
276
                        $selected = selected( $this->get_current_seo_filter(), $rank->get_rank(), false );
×
277

278
                        // phpcs:ignore WordPress.Security.EscapeOutput -- Output is correctly escaped in the generate_option() method.
279
                        echo $this->generate_option( $rank->get_rank(), $rank->get_drop_down_label(), $selected );
×
280
                }
281

282
                echo '</select>';
×
283
        }
284

285
        /**
286
         * Adds a dropdown that allows filtering on the posts Readability Quality.
287
         *
288
         * @return void
289
         */
290
        public function posts_filter_dropdown_readability() {
×
291
                if ( ! $this->can_display_filter() ) {
×
292
                        return;
×
293
                }
294

295
                $ranks = WPSEO_Rank::get_all_readability_ranks();
×
296

297
                /* translators: Hidden accessibility text. */
298
                echo '<label class="screen-reader-text" for="wpseo-readability-filter">' . esc_html__( 'Filter by Readability Score', 'wordpress-seo' ) . '</label>';
×
299
                echo '<select name="readability_filter" id="wpseo-readability-filter">';
×
300

301
                // phpcs:ignore WordPress.Security.EscapeOutput -- Output is correctly escaped in the generate_option() method.
302
                echo $this->generate_option( '', __( 'All Readability Scores', 'wordpress-seo' ) );
×
303

304
                foreach ( $ranks as $rank ) {
×
305
                        $selected = selected( $this->get_current_readability_filter(), $rank->get_rank(), false );
×
306

307
                        // phpcs:ignore WordPress.Security.EscapeOutput -- Output is correctly escaped in the generate_option() method.
308
                        echo $this->generate_option( $rank->get_rank(), $rank->get_drop_down_readability_labels(), $selected );
×
309
                }
310

311
                echo '</select>';
×
312
        }
313

314
        /**
315
         * Generates an <option> element.
316
         *
317
         * @param string $value    The option's value.
318
         * @param string $label    The option's label.
319
         * @param string $selected HTML selected attribute for an option.
320
         *
321
         * @return string The generated <option> element.
322
         */
323
        protected function generate_option( $value, $label, $selected = '' ) {
×
324
                return '<option ' . $selected . ' value="' . esc_attr( $value ) . '">' . esc_html( $label ) . '</option>';
×
325
        }
326

327
        /**
328
         * Returns the meta object for a given post ID.
329
         *
330
         * @param int $post_id The post ID.
331
         *
332
         * @return Meta The meta object.
333
         */
334
        protected function get_meta( $post_id ) {
×
335
                $indexable = $this->admin_columns_cache->get_indexable( $post_id );
×
336

337
                return YoastSEO()->meta->for_indexable( $indexable, 'Post_Type' );
×
338
        }
339

340
        /**
341
         * Determines the SEO score filter to be later used in the meta query, based on the passed SEO filter.
342
         *
343
         * @param string $seo_filter The SEO filter to use to determine what further filter to apply.
344
         *
345
         * @return array The SEO score filter.
346
         */
347
        protected function determine_seo_filters( $seo_filter ) {
12✔
348
                if ( $seo_filter === WPSEO_Rank::NO_FOCUS ) {
12✔
349
                        return $this->create_no_focus_keyword_filter();
2✔
350
                }
351

352
                if ( $seo_filter === WPSEO_Rank::NO_INDEX ) {
10✔
353
                        return $this->create_no_index_filter();
2✔
354
                }
355

356
                $rank = new WPSEO_Rank( $seo_filter );
8✔
357

358
                return $this->create_seo_score_filter( $rank->get_starting_score(), $rank->get_end_score() );
8✔
359
        }
360

361
        /**
362
         * Determines the Readability score filter to the meta query, based on the passed Readability filter.
363
         *
364
         * @param string $readability_filter The Readability filter to use to determine what further filter to apply.
365
         *
366
         * @return array The Readability score filter.
367
         */
368
        protected function determine_readability_filters( $readability_filter ) {
8✔
369
                if ( $readability_filter === WPSEO_Rank::NO_FOCUS ) {
8✔
370
                        return $this->create_no_readability_scores_filter();
2✔
371
                }
372
                if ( $readability_filter === WPSEO_Rank::BAD ) {
6✔
373
                        return $this->create_bad_readability_scores_filter();
2✔
374
                }
375
                $rank = new WPSEO_Rank( $readability_filter );
4✔
376

377
                return $this->create_readability_score_filter( $rank->get_starting_score(), $rank->get_end_score() );
4✔
378
        }
379

380
        /**
381
         * Creates a keyword filter for the meta query, based on the passed Keyword filter.
382
         *
383
         * @param string $keyword_filter The keyword filter to use.
384
         *
385
         * @return array The keyword filter.
386
         */
387
        protected function get_keyword_filter( $keyword_filter ) {
×
388
                return [
×
389
                        'post_type' => get_query_var( 'post_type', 'post' ),
×
390
                        'key'       => WPSEO_Meta::$meta_prefix . 'focuskw',
×
391
                        'value'     => sanitize_text_field( $keyword_filter ),
×
392
                ];
×
393
        }
394

395
        /**
396
         * Determines whether the passed filter is considered to be valid.
397
         *
398
         * @param mixed $filter The filter to check against.
399
         *
400
         * @return bool Whether the filter is considered valid.
401
         */
402
        protected function is_valid_filter( $filter ) {
4✔
403
                return ! empty( $filter ) && is_string( $filter );
4✔
404
        }
405

406
        /**
407
         * Collects the filters and merges them into a single array.
408
         *
409
         * @return array Array containing all the applicable filters.
410
         */
411
        protected function collect_filters() {
×
412
                $active_filters = [];
×
413

414
                $seo_filter             = $this->get_current_seo_filter();
×
415
                $readability_filter     = $this->get_current_readability_filter();
×
416
                $current_keyword_filter = $this->get_current_keyword_filter();
×
417

418
                if ( $this->is_valid_filter( $seo_filter ) ) {
×
419
                        $active_filters = array_merge(
×
420
                                $active_filters,
×
421
                                $this->determine_seo_filters( $seo_filter )
×
422
                        );
×
423
                }
424

425
                if ( $this->is_valid_filter( $readability_filter ) ) {
×
426
                        $active_filters = array_merge(
×
427
                                $active_filters,
×
428
                                $this->determine_readability_filters( $readability_filter )
×
429
                        );
×
430
                }
431

432
                if ( $this->is_valid_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
×
445
                        );
×
446

447
                        if ( is_array( $keyphrase_filter ) ) {
×
448
                                $active_filters = array_merge(
×
449
                                        $active_filters,
×
450
                                        [ $keyphrase_filter ]
×
451
                                );
×
452
                        }
453
                }
454

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 );
×
463
        }
464

465
        /**
466
         * Modify the query based on the filters that are being passed.
467
         *
468
         * @param array $vars Query variables that need to be modified based on the filters.
469
         *
470
         * @return array Array containing the meta query to use for filtering the posts overview.
471
         */
472
        public function column_sort_orderby( $vars ) {
×
473
                $collected_filters = $this->collect_filters();
×
474

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 );
×
491
                }
492

493
                return $this->build_filter_query( $vars, $collected_filters );
×
494
        }
495

496
        /**
497
         * Retrieves the meta robots query values to be used within the meta query.
498
         *
499
         * @return array Array containing the query parameters regarding meta robots.
500
         */
501
        protected function get_meta_robots_query_values() {
×
502
                return [
×
503
                        'relation' => 'OR',
×
504
                        [
×
505
                                'key'     => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex',
×
506
                                'compare' => 'NOT EXISTS',
×
507
                        ],
×
508
                        [
×
509
                                'key'     => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex',
×
510
                                'value'   => '1',
×
511
                                'compare' => '!=',
×
512
                        ],
×
513
                ];
×
514
        }
515

516
        /**
517
         * Determines the score filters to be used. If more than one is passed, it created an AND statement for the query.
518
         *
519
         * @param array $score_filters Array containing the score filters.
520
         *
521
         * @return array Array containing the score filters that need to be applied to the meta query.
522
         */
523
        protected function determine_score_filters( $score_filters ) {
×
524
                if ( count( $score_filters ) > 1 ) {
×
525
                        return array_merge( [ 'relation' => 'AND' ], $score_filters );
×
526
                }
527

528
                return $score_filters;
×
529
        }
530

531
        /**
532
         * Retrieves the post type from the $_GET variable.
533
         *
534
         * @return string|null The sanitized current post type or null when the variable is not set in $_GET.
535
         */
536
        public function get_current_post_type() {
6✔
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'] ) ) {
6✔
539
                        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
540
                        return sanitize_text_field( wp_unslash( $_GET['post_type'] ) );
2✔
541
                }
542
                return null;
4✔
543
        }
544

545
        /**
546
         * Retrieves the SEO filter from the $_GET variable.
547
         *
548
         * @return string|null The sanitized seo filter or null when the variable is not set in $_GET.
549
         */
550
        public function get_current_seo_filter() {
6✔
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'] ) ) {
6✔
553
                        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
554
                        return sanitize_text_field( wp_unslash( $_GET['seo_filter'] ) );
2✔
555
                }
556
                return null;
4✔
557
        }
558

559
        /**
560
         * Retrieves the Readability filter from the $_GET variable.
561
         *
562
         * @return string|null The sanitized readability filter or null when the variable is not set in $_GET.
563
         */
564
        public function get_current_readability_filter() {
6✔
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'] ) ) {
6✔
567
                        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
568
                        return sanitize_text_field( wp_unslash( $_GET['readability_filter'] ) );
2✔
569
                }
570
                return null;
4✔
571
        }
572

573
        /**
574
         * Retrieves the keyword filter from the $_GET variable.
575
         *
576
         * @return string|null The sanitized seo keyword filter or null when the variable is not set in $_GET.
577
         */
578
        public function get_current_keyword_filter() {
6✔
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'] ) ) {
6✔
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'] ) );
2✔
583
                }
584
                return null;
4✔
585
        }
586

587
        /**
588
         * Uses the vars to create a complete filter query that can later be executed to filter out posts.
589
         *
590
         * @param array $vars    Array containing the variables that will be used in the meta query.
591
         * @param array $filters Array containing the filters that we need to apply in the meta query.
592
         *
593
         * @return array Array containing the complete filter query.
594
         */
595
        protected function build_filter_query( $vars, $filters ) {
8✔
596
                // If no filters were applied, just return everything.
597
                if ( count( $filters ) === 0 ) {
8✔
598
                        return $vars;
2✔
599
                }
600

601
                $result               = [ 'meta_query' => [] ];
6✔
602
                $result['meta_query'] = array_merge( $result['meta_query'], [ $this->determine_score_filters( $filters ) ] );
6✔
603

604
                $current_seo_filter = $this->get_current_seo_filter();
6✔
605

606
                // This only applies for the SEO score filter because it can because the SEO score can be altered by the no-index option.
607
                if ( $this->is_valid_filter( $current_seo_filter ) && ! in_array( $current_seo_filter, [ WPSEO_Rank::NO_INDEX ], true ) ) {
6✔
608
                        $result['meta_query'] = array_merge( $result['meta_query'], [ $this->get_meta_robots_query_values() ] );
×
609
                }
610

611
                return array_merge( $vars, $result );
6✔
612
        }
613

614
        /**
615
         * Creates a Readability score filter.
616
         *
617
         * @param number $low  The lower boundary of the score.
618
         * @param number $high The higher boundary of the score.
619
         *
620
         * @return array<array<string>> The Readability Score filter.
621
         */
622
        protected function create_readability_score_filter( $low, $high ) {
×
623
                return [
×
624
                        [
×
625
                                'key'     => WPSEO_Meta::$meta_prefix . 'content_score',
×
626
                                'value'   => [ $low, $high ],
×
627
                                'type'    => 'numeric',
×
628
                                'compare' => 'BETWEEN',
×
629
                        ],
×
630
                ];
×
631
        }
632

633
        /**
634
         * Creates an SEO score filter.
635
         *
636
         * @param number $low  The lower boundary of the score.
637
         * @param number $high The higher boundary of the score.
638
         *
639
         * @return array<array<string>> The SEO score filter.
640
         */
641
        protected function create_seo_score_filter( $low, $high ) {
×
642
                return [
×
643
                        [
×
644
                                'key'     => WPSEO_Meta::$meta_prefix . 'linkdex',
×
645
                                'value'   => [ $low, $high ],
×
646
                                'type'    => 'numeric',
×
647
                                'compare' => 'BETWEEN',
×
648
                        ],
×
649
                ];
×
650
        }
651

652
        /**
653
         * Creates a filter to retrieve posts that were set to no-index.
654
         *
655
         * @return array<array<string>> Array containin the no-index filter.
656
         */
657
        protected function create_no_index_filter() {
×
658
                return [
×
659
                        [
×
660
                                'key'     => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex',
×
661
                                'value'   => '1',
×
662
                                'compare' => '=',
×
663
                        ],
×
664
                ];
×
665
        }
666

667
        /**
668
         * Creates a filter to retrieve posts that have no keyword set.
669
         *
670
         * @return array<array<string>> Array containing the no focus keyword filter.
671
         */
672
        protected function create_no_focus_keyword_filter() {
×
673
                return [
×
674
                        [
×
675
                                'key'     => WPSEO_Meta::$meta_prefix . 'linkdex',
×
676
                                'value'   => 'needs-a-value-anyway',
×
677
                                'compare' => 'NOT EXISTS',
×
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 [
×
692
                        [
×
693
                                'key'     => WPSEO_Meta::$meta_prefix . 'estimated-reading-time-minutes',
×
694
                                'value'   => 'needs-a-value-anyway',
×
695
                                'compare' => 'NOT EXISTS',
×
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
                        ],
×
711
                ];
×
712
        }
713

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
        /**
744
         * Determines whether a particular post_id is of an indexable post type.
745
         *
746
         * @param string $post_id The post ID to check.
747
         *
748
         * @return bool Whether or not it is indexable.
749
         */
750
        protected function is_indexable( $post_id ) {
8✔
751
                if ( ! empty( $post_id ) && ! $this->uses_default_indexing( $post_id ) ) {
8✔
752
                        return WPSEO_Meta::get_value( 'meta-robots-noindex', $post_id ) === '2';
2✔
753
                }
754

755
                $post = get_post( $post_id );
6✔
756

757
                if ( is_object( $post ) ) {
6✔
758
                        // If the option is false, this means we want to index it.
759
                        return WPSEO_Options::get( 'noindex-' . $post->post_type, false ) === false;
4✔
760
                }
761

762
                return true;
2✔
763
        }
764

765
        /**
766
         * Determines whether the given post ID uses the default indexing settings.
767
         *
768
         * @param int $post_id The post ID to check.
769
         *
770
         * @return bool Whether or not the default indexing is being used for the post.
771
         */
772
        protected function uses_default_indexing( $post_id ) {
4✔
773
                return WPSEO_Meta::get_value( 'meta-robots-noindex', $post_id ) === '0';
4✔
774
        }
775

776
        /**
777
         * Returns filters when $order_by is matched in the if-statement.
778
         *
779
         * @param string $order_by The ID of the column by which to order the posts.
780
         *
781
         * @return array<string> Array containing the order filters.
782
         */
783
        private function filter_order_by( $order_by ) {
×
784
                switch ( $order_by ) {
785
                        case 'wpseo-metadesc':
×
786
                                return [
×
787
                                        // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Reason: Only used when user requests sorting.
788
                                        'meta_key' => WPSEO_Meta::$meta_prefix . 'metadesc',
×
789
                                        'orderby'  => 'meta_value',
×
790
                                ];
×
791

792
                        case 'wpseo-focuskw':
×
793
                                return [
×
794
                                        // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Reason: Only used when user requests sorting.
795
                                        'meta_key' => WPSEO_Meta::$meta_prefix . 'focuskw',
×
796
                                        'orderby'  => 'meta_value',
×
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
                                ];
×
812
                }
813

814
                return [];
×
815
        }
816

817
        /**
818
         * Parses the score column.
819
         *
820
         * @param int $post_id The ID of the post for which to show the score.
821
         *
822
         * @return string The HTML for the SEO score indicator.
823
         */
824
        private function parse_column_score( $post_id ) {
×
825
                $meta = $this->get_meta( $post_id );
×
826

827
                if ( $meta ) {
×
828
                        return $this->score_icon_helper->for_seo( $meta->indexable, '', __( 'Post is set to noindex.', 'wordpress-seo' ) );
×
829
                }
830
        }
831

832
        /**
833
         * Parsing the readability score column.
834
         *
835
         * @param int $post_id The ID of the post for which to show the readability score.
836
         *
837
         * @return string The HTML for the readability score indicator.
838
         */
839
        private function parse_column_score_readability( $post_id ) {
×
840
                $meta = $this->get_meta( $post_id );
×
841
                if ( $meta ) {
×
842
                        return $this->score_icon_helper->for_readability( $meta->indexable->readability_score );
×
843
                }
844
        }
845

846
        /**
847
         * Sets up the hooks for the post_types.
848
         *
849
         * @return void
850
         */
851
        private function set_post_type_hooks() {
×
852
                $post_types = WPSEO_Post_Type::get_accessible_post_types();
×
853

854
                if ( ! is_array( $post_types ) || $post_types === [] ) {
×
855
                        return;
×
856
                }
857

858
                foreach ( $post_types as $post_type ) {
×
859
                        if ( $this->display_metabox( $post_type ) === false ) {
×
860
                                continue;
×
861
                        }
862

863
                        add_filter( 'manage_' . $post_type . '_posts_columns', [ $this, 'column_heading' ], 10, 1 );
×
864
                        add_action( 'manage_' . $post_type . '_posts_custom_column', [ $this, 'column_content' ], 10, 2 );
×
865
                        add_action( 'manage_edit-' . $post_type . '_sortable_columns', [ $this, 'column_sort' ], 10, 2 );
×
866
                }
867

868
                unset( $post_type );
×
869
        }
870

871
        /**
872
         * Wraps the WPSEO_Metabox check to determine whether the metabox should be displayed either by
873
         * choice of the admin or because the post type is not a public post type.
874
         *
875
         * @since 7.0
876
         *
877
         * @param string|null $post_type Optional. The post type to test, defaults to the current post post_type.
878
         *
879
         * @return bool Whether or not the meta box (and associated columns etc) should be hidden.
880
         */
881
        private function display_metabox( $post_type = null ) {
×
882
                $current_post_type = $this->get_current_post_type();
×
883

884
                if ( ! isset( $post_type ) && ! empty( $current_post_type ) ) {
×
885
                        $post_type = $current_post_type;
×
886
                }
887

888
                return WPSEO_Utils::is_metabox_active( $post_type, 'post_type' );
×
889
        }
890

891
        /**
892
         * Determines whether or not filter dropdowns should be displayed.
893
         *
894
         * @return bool Whether or the current page can display the filter drop downs.
895
         */
896
        public function can_display_filter() {
×
897
                if ( $GLOBALS['pagenow'] === 'upload.php' ) {
×
898
                        return false;
×
899
                }
900

901
                if ( $this->display_metabox() === false ) {
×
902
                        return false;
×
903
                }
904

905
                $screen = get_current_screen();
×
906
                if ( $screen === null ) {
×
907
                        return false;
×
908
                }
909

910
                return WPSEO_Post_Type::is_post_type_accessible( $screen->post_type );
×
911
        }
912
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc