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

Yoast / wordpress-seo / 7004843404

27 Nov 2023 11:48AM UTC coverage: 49.206% (-0.03%) from 49.232%
7004843404

push

github

web-flow
Merge pull request #20858 from Yoast/improve-copy-in-the-ftc-57

15305 of 31104 relevant lines covered (49.21%)

4.03 hits per line

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

26.18
/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
         * When page analysis is enabled, just initialize the hooks.
55
         */
56
        public function __construct() {
×
57
                if ( apply_filters( 'wpseo_use_page_analysis', true ) === true ) {
×
58
                        add_action( 'admin_init', [ $this, 'setup_hooks' ] );
×
59
                }
60

61
                $this->analysis_seo         = new WPSEO_Metabox_Analysis_SEO();
×
62
                $this->analysis_readability = new WPSEO_Metabox_Analysis_Readability();
×
63
                $this->admin_columns_cache  = YoastSEO()->classes->get( Admin_Columns_Cache_Integration::class );
×
64
                $this->score_icon_helper    = YoastSEO()->helpers->score_icon;
×
65
        }
66

67
        /**
68
         * Sets up up the hooks.
69
         */
70
        public function setup_hooks() {
×
71
                $this->set_post_type_hooks();
×
72

73
                if ( $this->analysis_seo->is_enabled() ) {
×
74
                        add_action( 'restrict_manage_posts', [ $this, 'posts_filter_dropdown' ] );
×
75
                }
76

77
                if ( $this->analysis_readability->is_enabled() ) {
×
78
                        add_action( 'restrict_manage_posts', [ $this, 'posts_filter_dropdown_readability' ] );
×
79
                }
80

81
                add_filter( 'request', [ $this, 'column_sort_orderby' ] );
×
82
                add_filter( 'default_hidden_columns', [ $this, 'column_hidden' ], 10, 1 );
×
83
        }
84

85
        /**
86
         * Adds the column headings for the SEO plugin for edit posts / pages overview.
87
         *
88
         * @param array $columns Already existing columns.
89
         *
90
         * @return array Array containing the column headings.
91
         */
92
        public function column_heading( $columns ) {
12✔
93
                if ( $this->display_metabox() === false ) {
12✔
94
                        return $columns;
×
95
                }
96

97
                $added_columns = [];
12✔
98

99
                if ( $this->analysis_seo->is_enabled() ) {
12✔
100
                        $added_columns['wpseo-score'] = '<span class="yoast-column-seo-score yoast-column-header-has-tooltip" data-tooltip-text="' .
12✔
101
                                                                                        esc_attr__( 'SEO score', 'wordpress-seo' ) .
12✔
102
                                                                                        '"><span class="screen-reader-text">' .
12✔
103
                                                                                        __( 'SEO score', 'wordpress-seo' ) .
12✔
104
                                                                                        '</span></span></span>';
12✔
105
                }
106

107
                if ( $this->analysis_readability->is_enabled() ) {
12✔
108
                        $added_columns['wpseo-score-readability'] = '<span class="yoast-column-readability yoast-column-header-has-tooltip" data-tooltip-text="' .
12✔
109
                                                                                                                esc_attr__( 'Readability score', 'wordpress-seo' ) .
12✔
110
                                                                                                                '"><span class="screen-reader-text">' .
12✔
111
                                                                                                                __( 'Readability score', 'wordpress-seo' ) .
12✔
112
                                                                                                                '</span></span></span>';
12✔
113
                }
114

115
                $added_columns['wpseo-title']    = __( 'SEO Title', 'wordpress-seo' );
12✔
116
                $added_columns['wpseo-metadesc'] = __( 'Meta Desc.', 'wordpress-seo' );
12✔
117

118
                if ( $this->analysis_seo->is_enabled() ) {
12✔
119
                        $added_columns['wpseo-focuskw'] = __( 'Keyphrase', 'wordpress-seo' );
12✔
120
                }
121

122
                return array_merge( $columns, $added_columns );
12✔
123
        }
124

125
        /**
126
         * Displays the column content for the given column.
127
         *
128
         * @param string $column_name Column to display the content for.
129
         * @param int    $post_id     Post to display the column content for.
130
         */
131
        public function column_content( $column_name, $post_id ) {
×
132
                if ( $this->display_metabox() === false ) {
×
133
                        return;
×
134
                }
135

136
                switch ( $column_name ) {
137
                        case 'wpseo-score':
×
138
                                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Correctly escaped in render_score_indicator() method.
139
                                echo $this->parse_column_score( $post_id );
×
140

141
                                return;
×
142

143
                        case 'wpseo-score-readability':
×
144
                                // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Correctly escaped in render_score_indicator() method.
145
                                echo $this->parse_column_score_readability( $post_id );
×
146

147
                                return;
×
148

149
                        case 'wpseo-title':
×
150
                                $meta = $this->get_meta( $post_id );
×
151
                                if ( $meta ) {
×
152
                                        echo esc_html( $meta->title );
×
153
                                }
154

155
                                return;
×
156

157
                        case 'wpseo-metadesc':
×
158
                                $metadesc_val = '';
×
159
                                $meta         = $this->get_meta( $post_id );
×
160
                                if ( $meta ) {
×
161
                                        $metadesc_val = $meta->meta_description;
×
162
                                }
163
                                if ( $metadesc_val === '' ) {
×
164
                                        echo '<span aria-hidden="true">&#8212;</span><span class="screen-reader-text">',
×
165
                                        /* translators: Hidden accessibility text. */
166
                                        esc_html__( 'Meta description not set.', 'wordpress-seo' ),
×
167
                                        '</span>';
×
168

169
                                        return;
×
170
                                }
171

172
                                echo esc_html( $metadesc_val );
×
173

174
                                return;
×
175

176
                        case 'wpseo-focuskw':
×
177
                                $focuskw_val = WPSEO_Meta::get_value( 'focuskw', $post_id );
×
178

179
                                if ( $focuskw_val === '' ) {
×
180
                                        echo '<span aria-hidden="true">&#8212;</span><span class="screen-reader-text">',
×
181
                                        /* translators: Hidden accessibility text. */
182
                                        esc_html__( 'Focus keyphrase not set.', 'wordpress-seo' ),
×
183
                                        '</span>';
×
184

185
                                        return;
×
186
                                }
187

188
                                echo esc_html( $focuskw_val );
×
189

190
                                return;
×
191
                }
192
        }
193

194
        /**
195
         * Indicates which of the SEO columns are sortable.
196
         *
197
         * @param array $columns Appended with their orderby variable.
198
         *
199
         * @return array Array containing the sortable columns.
200
         */
201
        public function column_sort( $columns ) {
×
202
                if ( $this->display_metabox() === false ) {
×
203
                        return $columns;
×
204
                }
205

206
                $columns['wpseo-metadesc'] = 'wpseo-metadesc';
×
207

208
                if ( $this->analysis_seo->is_enabled() ) {
×
209
                        $columns['wpseo-focuskw'] = 'wpseo-focuskw';
×
210
                        $columns['wpseo-score']   = 'wpseo-score';
×
211
                }
212

213
                if ( $this->analysis_readability->is_enabled() ) {
×
214
                        $columns['wpseo-score-readability'] = 'wpseo-score-readability';
×
215
                }
216

217
                return $columns;
×
218
        }
219

220
        /**
221
         * Hides the SEO title, meta description and focus keyword columns if the user hasn't chosen which columns to hide.
222
         *
223
         * @param array $hidden The hidden columns.
224
         *
225
         * @return array Array containing the columns to hide.
226
         */
227
        public function column_hidden( $hidden ) {
12✔
228
                if ( ! is_array( $hidden ) ) {
12✔
229
                        $hidden = [];
4✔
230
                }
231

232
                array_push( $hidden, 'wpseo-title', 'wpseo-metadesc' );
12✔
233

234
                if ( $this->analysis_seo->is_enabled() ) {
12✔
235
                        $hidden[] = 'wpseo-focuskw';
12✔
236
                }
237

238
                return $hidden;
12✔
239
        }
240

241
        /**
242
         * Adds a dropdown that allows filtering on the posts SEO Quality.
243
         */
244
        public function posts_filter_dropdown() {
×
245
                if ( ! $this->can_display_filter() ) {
×
246
                        return;
×
247
                }
248

249
                $ranks = WPSEO_Rank::get_all_ranks();
×
250

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

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

258
                foreach ( $ranks as $rank ) {
×
259
                        $selected = selected( $this->get_current_seo_filter(), $rank->get_rank(), false );
×
260

261
                        // phpcs:ignore WordPress.Security.EscapeOutput -- Output is correctly escaped in the generate_option() method.
262
                        echo $this->generate_option( $rank->get_rank(), $rank->get_drop_down_label(), $selected );
×
263
                }
264

265
                echo '</select>';
×
266
        }
267

268
        /**
269
         * Adds a dropdown that allows filtering on the posts Readability Quality.
270
         *
271
         * @return void
272
         */
273
        public function posts_filter_dropdown_readability() {
×
274
                if ( ! $this->can_display_filter() ) {
×
275
                        return;
×
276
                }
277

278
                $ranks = WPSEO_Rank::get_all_readability_ranks();
×
279

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

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

287
                foreach ( $ranks as $rank ) {
×
288
                        $selected = selected( $this->get_current_readability_filter(), $rank->get_rank(), false );
×
289

290
                        // phpcs:ignore WordPress.Security.EscapeOutput -- Output is correctly escaped in the generate_option() method.
291
                        echo $this->generate_option( $rank->get_rank(), $rank->get_drop_down_readability_labels(), $selected );
×
292
                }
293

294
                echo '</select>';
×
295
        }
296

297
        /**
298
         * Generates an <option> element.
299
         *
300
         * @param string $value    The option's value.
301
         * @param string $label    The option's label.
302
         * @param string $selected HTML selected attribute for an option.
303
         *
304
         * @return string The generated <option> element.
305
         */
306
        protected function generate_option( $value, $label, $selected = '' ) {
×
307
                return '<option ' . $selected . ' value="' . esc_attr( $value ) . '">' . esc_html( $label ) . '</option>';
×
308
        }
309

310
        /**
311
         * Returns the meta object for a given post ID.
312
         *
313
         * @param int $post_id The post ID.
314
         *
315
         * @return Meta The meta object.
316
         */
317
        protected function get_meta( $post_id ) {
×
318
                $indexable = $this->admin_columns_cache->get_indexable( $post_id );
×
319

320
                return YoastSEO()->meta->for_indexable( $indexable, 'Post_Type' );
×
321
        }
322

323
        /**
324
         * Determines the SEO score filter to be later used in the meta query, based on the passed SEO filter.
325
         *
326
         * @param string $seo_filter The SEO filter to use to determine what further filter to apply.
327
         *
328
         * @return array The SEO score filter.
329
         */
330
        protected function determine_seo_filters( $seo_filter ) {
24✔
331
                if ( $seo_filter === WPSEO_Rank::NO_FOCUS ) {
24✔
332
                        return $this->create_no_focus_keyword_filter();
4✔
333
                }
334

335
                if ( $seo_filter === WPSEO_Rank::NO_INDEX ) {
20✔
336
                        return $this->create_no_index_filter();
4✔
337
                }
338

339
                $rank = new WPSEO_Rank( $seo_filter );
16✔
340

341
                return $this->create_seo_score_filter( $rank->get_starting_score(), $rank->get_end_score() );
16✔
342
        }
343

344
        /**
345
         * Determines the Readability score filter to the meta query, based on the passed Readability filter.
346
         *
347
         * @param string $readability_filter The Readability filter to use to determine what further filter to apply.
348
         *
349
         * @return array The Readability score filter.
350
         */
351
        protected function determine_readability_filters( $readability_filter ) {
12✔
352
                $rank = new WPSEO_Rank( $readability_filter );
12✔
353

354
                return $this->create_readability_score_filter( $rank->get_starting_score(), $rank->get_end_score() );
12✔
355
        }
356

357
        /**
358
         * Creates a keyword filter for the meta query, based on the passed Keyword filter.
359
         *
360
         * @param string $keyword_filter The keyword filter to use.
361
         *
362
         * @return array The keyword filter.
363
         */
364
        protected function get_keyword_filter( $keyword_filter ) {
×
365
                return [
366
                        'post_type' => get_query_var( 'post_type', 'post' ),
×
367
                        'key'       => WPSEO_Meta::$meta_prefix . 'focuskw',
×
368
                        'value'     => sanitize_text_field( $keyword_filter ),
×
369
                ];
370
        }
371

372
        /**
373
         * Determines whether the passed filter is considered to be valid.
374
         *
375
         * @param mixed $filter The filter to check against.
376
         *
377
         * @return bool Whether the filter is considered valid.
378
         */
379
        protected function is_valid_filter( $filter ) {
8✔
380
                return ! empty( $filter ) && is_string( $filter );
8✔
381
        }
382

383
        /**
384
         * Collects the filters and merges them into a single array.
385
         *
386
         * @return array Array containing all the applicable filters.
387
         */
388
        protected function collect_filters() {
×
389
                $active_filters = [];
×
390

391
                $seo_filter             = $this->get_current_seo_filter();
×
392
                $readability_filter     = $this->get_current_readability_filter();
×
393
                $current_keyword_filter = $this->get_current_keyword_filter();
×
394

395
                if ( $this->is_valid_filter( $seo_filter ) ) {
×
396
                        $active_filters = array_merge(
×
397
                                $active_filters,
×
398
                                $this->determine_seo_filters( $seo_filter )
×
399
                        );
400
                }
401

402
                if ( $this->is_valid_filter( $readability_filter ) ) {
×
403
                        $active_filters = array_merge(
×
404
                                $active_filters,
×
405
                                $this->determine_readability_filters( $readability_filter )
×
406
                        );
407
                }
408

409
                if ( $this->is_valid_filter( $current_keyword_filter ) ) {
×
410
                        /**
411
                         * Adapt the meta query used to filter the post overview on keyphrase.
412
                         *
413
                         * @internal
414
                         *
415
                         * @api array $keyword_filter The current keyword filter.
416
                         *
417
                         * @param array $keyphrase The keyphrase used in the filter.
418
                         */
419
                        $keyphrase_filter = \apply_filters(
×
420
                                'wpseo_change_keyphrase_filter_in_request',
×
421
                                $this->get_keyword_filter( $current_keyword_filter ),
×
422
                                $current_keyword_filter
×
423
                        );
424

425
                        if ( \is_array( $keyphrase_filter ) ) {
×
426
                                $active_filters = array_merge(
×
427
                                        $active_filters,
×
428
                                        [ $keyphrase_filter ]
×
429
                                );
430
                        }
431
                }
432

433
                /**
434
                 * Adapt the active applicable filters on the posts overview.
435
                 *
436
                 * @internal
437
                 *
438
                 * @param array $active_filters The current applicable filters.
439
                 */
440
                return \apply_filters( 'wpseo_change_applicable_filters', $active_filters );
×
441
        }
442

443
        /**
444
         * Modify the query based on the filters that are being passed.
445
         *
446
         * @param array $vars Query variables that need to be modified based on the filters.
447
         *
448
         * @return array Array containing the meta query to use for filtering the posts overview.
449
         */
450
        public function column_sort_orderby( $vars ) {
×
451
                $collected_filters = $this->collect_filters();
×
452

453
                $order_by_column = $vars['orderby'];
×
454
                if ( isset( $order_by_column ) ) {
×
455
                        // Based on the selected column, create a meta query.
456
                        $order_by = $this->filter_order_by( $order_by_column );
×
457

458
                        /**
459
                         * Adapt the order by part of the query on the posts overview.
460
                         *
461
                         * @internal
462
                         *
463
                         * @param array  $order_by        The current order by.
464
                         * @param string $order_by_column The current order by column.
465
                         */
466
                        $order_by = \apply_filters( 'wpseo_change_order_by', $order_by, $order_by_column );
×
467

468
                        $vars = array_merge( $vars, $order_by );
×
469
                }
470

471
                return $this->build_filter_query( $vars, $collected_filters );
×
472
        }
473

474
        /**
475
         * Retrieves the meta robots query values to be used within the meta query.
476
         *
477
         * @return array Array containing the query parameters regarding meta robots.
478
         */
479
        protected function get_meta_robots_query_values() {
×
480
                return [
481
                        'relation' => 'OR',
×
482
                        [
483
                                'key'     => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex',
×
484
                                'compare' => 'NOT EXISTS',
×
485
                        ],
486
                        [
487
                                'key'     => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex',
×
488
                                'value'   => '1',
×
489
                                'compare' => '!=',
×
490
                        ],
491
                ];
492
        }
493

494
        /**
495
         * Determines the score filters to be used. If more than one is passed, it created an AND statement for the query.
496
         *
497
         * @param array $score_filters Array containing the score filters.
498
         *
499
         * @return array Array containing the score filters that need to be applied to the meta query.
500
         */
501
        protected function determine_score_filters( $score_filters ) {
×
502
                if ( count( $score_filters ) > 1 ) {
×
503
                        return array_merge( [ 'relation' => 'AND' ], $score_filters );
×
504
                }
505

506
                return $score_filters;
×
507
        }
508

509
        /**
510
         * Retrieves the post type from the $_GET variable.
511
         *
512
         * @return string|null The sanitized current post type or null when the variable is not set in $_GET.
513
         */
514
        public function get_current_post_type() {
12✔
515
                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
516
                if ( isset( $_GET['post_type'] ) && is_string( $_GET['post_type'] ) ) {
12✔
517
                        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
518
                        return sanitize_text_field( wp_unslash( $_GET['post_type'] ) );
4✔
519
                }
520
                return null;
8✔
521
        }
522

523
        /**
524
         * Retrieves the SEO filter from the $_GET variable.
525
         *
526
         * @return string|null The sanitized seo filter or null when the variable is not set in $_GET.
527
         */
528
        public function get_current_seo_filter() {
12✔
529
                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
530
                if ( isset( $_GET['seo_filter'] ) && is_string( $_GET['seo_filter'] ) ) {
12✔
531
                        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
532
                        return sanitize_text_field( wp_unslash( $_GET['seo_filter'] ) );
4✔
533
                }
534
                return null;
8✔
535
        }
536

537
        /**
538
         * Retrieves the Readability filter from the $_GET variable.
539
         *
540
         * @return string|null The sanitized readability filter or null when the variable is not set in $_GET.
541
         */
542
        public function get_current_readability_filter() {
12✔
543
                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
544
                if ( isset( $_GET['readability_filter'] ) && is_string( $_GET['readability_filter'] ) ) {
12✔
545
                        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
546
                        return sanitize_text_field( wp_unslash( $_GET['readability_filter'] ) );
4✔
547
                }
548
                return null;
8✔
549
        }
550

551
        /**
552
         * Retrieves the keyword filter from the $_GET variable.
553
         *
554
         * @return string|null The sanitized seo keyword filter or null when the variable is not set in $_GET.
555
         */
556
        public function get_current_keyword_filter() {
12✔
557
                // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
558
                if ( isset( $_GET['seo_kw_filter'] ) && is_string( $_GET['seo_kw_filter'] ) ) {
12✔
559
                        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reason: We are not processing form information.
560
                        return sanitize_text_field( wp_unslash( $_GET['seo_kw_filter'] ) );
4✔
561
                }
562
                return null;
8✔
563
        }
564

565
        /**
566
         * Uses the vars to create a complete filter query that can later be executed to filter out posts.
567
         *
568
         * @param array $vars    Array containing the variables that will be used in the meta query.
569
         * @param array $filters Array containing the filters that we need to apply in the meta query.
570
         *
571
         * @return array Array containing the complete filter query.
572
         */
573
        protected function build_filter_query( $vars, $filters ) {
16✔
574
                // If no filters were applied, just return everything.
575
                if ( count( $filters ) === 0 ) {
16✔
576
                        return $vars;
4✔
577
                }
578

579
                $result               = [ 'meta_query' => [] ];
12✔
580
                $result['meta_query'] = array_merge( $result['meta_query'], [ $this->determine_score_filters( $filters ) ] );
12✔
581

582
                $current_seo_filter = $this->get_current_seo_filter();
12✔
583

584
                // This only applies for the SEO score filter because it can because the SEO score can be altered by the no-index option.
585
                if ( $this->is_valid_filter( $current_seo_filter ) && ! in_array( $current_seo_filter, [ WPSEO_Rank::NO_INDEX, WPSEO_Rank::NO_FOCUS ], true ) ) {
12✔
586
                        $result['meta_query'] = array_merge( $result['meta_query'], [ $this->get_meta_robots_query_values() ] );
×
587
                }
588

589
                return array_merge( $vars, $result );
12✔
590
        }
591

592
        /**
593
         * Creates a Readability score filter.
594
         *
595
         * @param number $low  The lower boundary of the score.
596
         * @param number $high The higher boundary of the score.
597
         *
598
         * @return array The Readability Score filter.
599
         */
600
        protected function create_readability_score_filter( $low, $high ) {
×
601
                return [
602
                        [
603
                                'key'     => WPSEO_Meta::$meta_prefix . 'content_score',
×
604
                                'value'   => [ $low, $high ],
×
605
                                'type'    => 'numeric',
×
606
                                'compare' => 'BETWEEN',
×
607
                        ],
608
                ];
609
        }
610

611
        /**
612
         * Creates an SEO score filter.
613
         *
614
         * @param number $low  The lower boundary of the score.
615
         * @param number $high The higher boundary of the score.
616
         *
617
         * @return array The SEO score filter.
618
         */
619
        protected function create_seo_score_filter( $low, $high ) {
×
620
                return [
621
                        [
622
                                'key'     => WPSEO_Meta::$meta_prefix . 'linkdex',
×
623
                                'value'   => [ $low, $high ],
×
624
                                'type'    => 'numeric',
×
625
                                'compare' => 'BETWEEN',
×
626
                        ],
627
                ];
628
        }
629

630
        /**
631
         * Creates a filter to retrieve posts that were set to no-index.
632
         *
633
         * @return array Array containin the no-index filter.
634
         */
635
        protected function create_no_index_filter() {
×
636
                return [
637
                        [
638
                                'key'     => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex',
×
639
                                'value'   => '1',
×
640
                                'compare' => '=',
×
641
                        ],
642
                ];
643
        }
644

645
        /**
646
         * Creates a filter to retrieve posts that have no keyword set.
647
         *
648
         * @return array Array containing the no focus keyword filter.
649
         */
650
        protected function create_no_focus_keyword_filter() {
×
651
                return [
652
                        [
653
                                'key'     => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex',
×
654
                                'value'   => 'needs-a-value-anyway',
×
655
                                'compare' => 'NOT EXISTS',
×
656
                        ],
657
                        [
658
                                'key'     => WPSEO_Meta::$meta_prefix . 'linkdex',
×
659
                                'value'   => 'needs-a-value-anyway',
×
660
                                'compare' => 'NOT EXISTS',
×
661
                        ],
662
                ];
663
        }
664

665
        /**
666
         * Determines whether a particular post_id is of an indexable post type.
667
         *
668
         * @param string $post_id The post ID to check.
669
         *
670
         * @return bool Whether or not it is indexable.
671
         */
672
        protected function is_indexable( $post_id ) {
16✔
673
                if ( ! empty( $post_id ) && ! $this->uses_default_indexing( $post_id ) ) {
16✔
674
                        return WPSEO_Meta::get_value( 'meta-robots-noindex', $post_id ) === '2';
4✔
675
                }
676

677
                $post = get_post( $post_id );
12✔
678

679
                if ( is_object( $post ) ) {
12✔
680
                        // If the option is false, this means we want to index it.
681
                        return WPSEO_Options::get( 'noindex-' . $post->post_type, false ) === false;
8✔
682
                }
683

684
                return true;
4✔
685
        }
686

687
        /**
688
         * Determines whether the given post ID uses the default indexing settings.
689
         *
690
         * @param int $post_id The post ID to check.
691
         *
692
         * @return bool Whether or not the default indexing is being used for the post.
693
         */
694
        protected function uses_default_indexing( $post_id ) {
8✔
695
                return WPSEO_Meta::get_value( 'meta-robots-noindex', $post_id ) === '0';
8✔
696
        }
697

698
        /**
699
         * Returns filters when $order_by is matched in the if-statement.
700
         *
701
         * @param string $order_by The ID of the column by which to order the posts.
702
         *
703
         * @return array Array containing the order filters.
704
         */
705
        private function filter_order_by( $order_by ) {
×
706
                switch ( $order_by ) {
707
                        case 'wpseo-metadesc':
×
708
                                return [
709
                                        // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Reason: Only used when user requests sorting.
710
                                        'meta_key' => WPSEO_Meta::$meta_prefix . 'metadesc',
×
711
                                        'orderby'  => 'meta_value',
×
712
                                ];
713

714
                        case 'wpseo-focuskw':
×
715
                                return [
716
                                        // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Reason: Only used when user requests sorting.
717
                                        'meta_key' => WPSEO_Meta::$meta_prefix . 'focuskw',
×
718
                                        'orderby'  => 'meta_value',
×
719
                                ];
720

721
                        case 'wpseo-score':
×
722
                                return [
723
                                        // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Reason: Only used when user requests sorting.
724
                                        'meta_key' => WPSEO_Meta::$meta_prefix . 'linkdex',
×
725
                                        'orderby'  => 'meta_value_num',
×
726
                                ];
727

728
                        case 'wpseo-score-readability':
×
729
                                return [
730
                                        // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_key -- Reason: Only used when user requests sorting.
731
                                        'meta_key' => WPSEO_Meta::$meta_prefix . 'content_score',
×
732
                                        'orderby'  => 'meta_value_num',
×
733
                                ];
734
                }
735

736
                return [];
×
737
        }
738

739
        /**
740
         * Parses the score column.
741
         *
742
         * @param int $post_id The ID of the post for which to show the score.
743
         *
744
         * @return string The HTML for the SEO score indicator.
745
         */
746
        private function parse_column_score( $post_id ) {
×
747
                $meta = $this->get_meta( $post_id );
×
748

749
                if ( $meta ) {
×
750
                        return $this->score_icon_helper->for_seo( $meta->indexable, '', __( 'Post is set to noindex.', 'wordpress-seo' ) );
×
751
                }
752
        }
753

754
        /**
755
         * Parsing the readability score column.
756
         *
757
         * @param int $post_id The ID of the post for which to show the readability score.
758
         *
759
         * @return string The HTML for the readability score indicator.
760
         */
761
        private function parse_column_score_readability( $post_id ) {
×
762
                $meta = $this->get_meta( $post_id );
×
763
                if ( $meta ) {
×
764
                        return $this->score_icon_helper->for_readability( $meta->indexable->readability_score );
×
765
                }
766
        }
767

768
        /**
769
         * Sets up the hooks for the post_types.
770
         */
771
        private function set_post_type_hooks() {
×
772
                $post_types = WPSEO_Post_Type::get_accessible_post_types();
×
773

774
                if ( ! is_array( $post_types ) || $post_types === [] ) {
×
775
                        return;
×
776
                }
777

778
                foreach ( $post_types as $post_type ) {
×
779
                        if ( $this->display_metabox( $post_type ) === false ) {
×
780
                                continue;
×
781
                        }
782

783
                        add_filter( 'manage_' . $post_type . '_posts_columns', [ $this, 'column_heading' ], 10, 1 );
×
784
                        add_action( 'manage_' . $post_type . '_posts_custom_column', [ $this, 'column_content' ], 10, 2 );
×
785
                        add_action( 'manage_edit-' . $post_type . '_sortable_columns', [ $this, 'column_sort' ], 10, 2 );
×
786
                }
787

788
                unset( $post_type );
×
789
        }
790

791
        /**
792
         * Wraps the WPSEO_Metabox check to determine whether the metabox should be displayed either by
793
         * choice of the admin or because the post type is not a public post type.
794
         *
795
         * @since 7.0
796
         *
797
         * @param string|null $post_type Optional. The post type to test, defaults to the current post post_type.
798
         *
799
         * @return bool Whether or not the meta box (and associated columns etc) should be hidden.
800
         */
801
        private function display_metabox( $post_type = null ) {
×
802
                $current_post_type = $this->get_current_post_type();
×
803

804
                if ( ! isset( $post_type ) && ! empty( $current_post_type ) ) {
×
805
                        $post_type = $current_post_type;
×
806
                }
807

808
                return WPSEO_Utils::is_metabox_active( $post_type, 'post_type' );
×
809
        }
810

811
        /**
812
         * Determines whether or not filter dropdowns should be displayed.
813
         *
814
         * @return bool Whether or the current page can display the filter drop downs.
815
         */
816
        public function can_display_filter() {
×
817
                if ( $GLOBALS['pagenow'] === 'upload.php' ) {
×
818
                        return false;
×
819
                }
820

821
                if ( $this->display_metabox() === false ) {
×
822
                        return false;
×
823
                }
824

825
                $screen = get_current_screen();
×
826
                if ( $screen === null ) {
×
827
                        return false;
×
828
                }
829

830
                return WPSEO_Post_Type::is_post_type_accessible( $screen->post_type );
×
831
        }
832
}
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