PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / 18.1
Yoast SEO – Advanced SEO with real-time guidance and built-in AI v18.1
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / admin / class-meta-columns.php

class-meta-columns.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI 18.1, at admin/class-meta-columns.php

752 lines 21.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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\Integrations\Admin\Admin_Columns_Cache_Integration;
10 use Yoast\WP\SEO\Surfaces\Values\Meta;
11
12 /**
13 * Class WPSEO_Meta_Columns.
14 */
15 class WPSEO_Meta_Columns {
16
17 /**
18 * Holds the context objects for each indexable.
19 *
20 * @var Meta_Tags_Context[]
21 */
22 protected $context = [];
23
24 /**
25 * Holds the SEO analysis.
26 *
27 * @var WPSEO_Metabox_Analysis_SEO
28 */
29 private $analysis_seo;
30
31 /**
32 * Holds the readability analysis.
33 *
34 * @var WPSEO_Metabox_Analysis_Readability
35 */
36 private $analysis_readability;
37
38 /**
39 * Admin columns cache.
40 *
41 * @var Admin_Columns_Cache_Integration
42 */
43 private $admin_columns_cache;
44
45 /**
46 * When page analysis is enabled, just initialize the hooks.
47 */
48 public function __construct() {
49 if ( apply_filters( 'wpseo_use_page_analysis', true ) === true ) {
50 add_action( 'admin_init', [ $this, 'setup_hooks' ] );
51 }
52
53 $this->analysis_seo = new WPSEO_Metabox_Analysis_SEO();
54 $this->analysis_readability = new WPSEO_Metabox_Analysis_Readability();
55 $this->admin_columns_cache = YoastSEO()->classes->get( Admin_Columns_Cache_Integration::class );
56 }
57
58 /**
59 * Sets up up the hooks.
60 */
61 public function setup_hooks() {
62 $this->set_post_type_hooks();
63
64 if ( $this->analysis_seo->is_enabled() ) {
65 add_action( 'restrict_manage_posts', [ $this, 'posts_filter_dropdown' ] );
66 }
67
68 if ( $this->analysis_readability->is_enabled() ) {
69 add_action( 'restrict_manage_posts', [ $this, 'posts_filter_dropdown_readability' ] );
70 }
71
72 add_filter( 'request', [ $this, 'column_sort_orderby' ] );
73 add_filter( 'default_hidden_columns', [ $this, 'column_hidden' ], 10, 1 );
74 }
75
76 /**
77 * Adds the column headings for the SEO plugin for edit posts / pages overview.
78 *
79 * @param array $columns Already existing columns.
80 *
81 * @return array Array containing the column headings.
82 */
83 public function column_heading( $columns ) {
84 if ( $this->display_metabox() === false ) {
85 return $columns;
86 }
87
88 $added_columns = [];
89
90 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>';
92 }
93
94 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>';
96 }
97
98 $added_columns['wpseo-title'] = __( 'SEO Title', 'wordpress-seo' );
99 $added_columns['wpseo-metadesc'] = __( 'Meta Desc.', 'wordpress-seo' );
100
101 if ( $this->analysis_seo->is_enabled() ) {
102 $added_columns['wpseo-focuskw'] = __( 'Keyphrase', 'wordpress-seo' );
103 }
104
105 return array_merge( $columns, $added_columns );
106 }
107
108 /**
109 * Displays the column content for the given column.
110 *
111 * @param string $column_name Column to display the content for.
112 * @param int $post_id Post to display the column content for.
113 */
114 public function column_content( $column_name, $post_id ) {
115 if ( $this->display_metabox() === false ) {
116 return;
117 }
118
119 switch ( $column_name ) {
120 case 'wpseo-score':
121 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Correctly escaped in render_score_indicator() method.
122 echo $this->parse_column_score( $post_id );
123 return;
124
125 case 'wpseo-score-readability':
126 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Correctly escaped in render_score_indicator() method.
127 echo $this->parse_column_score_readability( $post_id );
128 return;
129
130 case 'wpseo-title':
131 echo esc_html( $this->get_meta( $post_id )->title );
132 return;
133
134 case 'wpseo-metadesc':
135 $metadesc_val = $this->get_meta( $post_id )->meta_description;
136
137 if ( $metadesc_val === '' ) {
138 echo '<span aria-hidden="true">&#8212;</span><span class="screen-reader-text">',
139 esc_html__( 'Meta description not set.', 'wordpress-seo' ),
140 '</span>';
141 return;
142 }
143
144 echo esc_html( $metadesc_val );
145 return;
146
147 case 'wpseo-focuskw':
148 $focuskw_val = WPSEO_Meta::get_value( 'focuskw', $post_id );
149
150 if ( $focuskw_val === '' ) {
151 echo '<span aria-hidden="true">&#8212;</span><span class="screen-reader-text">',
152 esc_html__( 'Focus keyphrase not set.', 'wordpress-seo' ),
153 '</span>';
154 return;
155 }
156
157 echo esc_html( $focuskw_val );
158 return;
159 }
160 }
161
162 /**
163 * Indicates which of the SEO columns are sortable.
164 *
165 * @param array $columns Appended with their orderby variable.
166 *
167 * @return array Array containing the sortable columns.
168 */
169 public function column_sort( $columns ) {
170 if ( $this->display_metabox() === false ) {
171 return $columns;
172 }
173
174 $columns['wpseo-metadesc'] = 'wpseo-metadesc';
175
176 if ( $this->analysis_seo->is_enabled() ) {
177 $columns['wpseo-focuskw'] = 'wpseo-focuskw';
178 }
179
180 return $columns;
181 }
182
183 /**
184 * Hides the SEO title, meta description and focus keyword columns if the user hasn't chosen which columns to hide.
185 *
186 * @param array $hidden The hidden columns.
187 *
188 * @return array Array containing the columns to hide.
189 */
190 public function column_hidden( $hidden ) {
191 if ( ! is_array( $hidden ) ) {
192 $hidden = [];
193 }
194
195 array_push( $hidden, 'wpseo-title', 'wpseo-metadesc' );
196
197 if ( $this->analysis_seo->is_enabled() ) {
198 $hidden[] = 'wpseo-focuskw';
199 }
200
201 return $hidden;
202 }
203
204 /**
205 * Adds a dropdown that allows filtering on the posts SEO Quality.
206 */
207 public function posts_filter_dropdown() {
208 if ( ! $this->can_display_filter() ) {
209 return;
210 }
211
212 $ranks = WPSEO_Rank::get_all_ranks();
213
214 echo '<label class="screen-reader-text" for="wpseo-filter">' . esc_html__( 'Filter by SEO Score', 'wordpress-seo' ) . '</label>';
215 echo '<select name="seo_filter" id="wpseo-filter">';
216
217 // phpcs:ignore WordPress.Security.EscapeOutput -- Output is correctly escaped in the generate_option() method.
218 echo $this->generate_option( '', __( 'All SEO Scores', 'wordpress-seo' ) );
219
220 foreach ( $ranks as $rank ) {
221 $selected = selected( $this->get_current_seo_filter(), $rank->get_rank(), false );
222
223 // phpcs:ignore WordPress.Security.EscapeOutput -- Output is correctly escaped in the generate_option() method.
224 echo $this->generate_option( $rank->get_rank(), $rank->get_drop_down_label(), $selected );
225 }
226
227 echo '</select>';
228 }
229
230 /**
231 * Adds a dropdown that allows filtering on the posts Readability Quality.
232 *
233 * @return void
234 */
235 public function posts_filter_dropdown_readability() {
236 if ( ! $this->can_display_filter() ) {
237 return;
238 }
239
240 $ranks = WPSEO_Rank::get_all_readability_ranks();
241
242 echo '<label class="screen-reader-text" for="wpseo-readability-filter">' . esc_html__( 'Filter by Readability Score', 'wordpress-seo' ) . '</label>';
243 echo '<select name="readability_filter" id="wpseo-readability-filter">';
244
245 // phpcs:ignore WordPress.Security.EscapeOutput -- Output is correctly escaped in the generate_option() method.
246 echo $this->generate_option( '', __( 'All Readability Scores', 'wordpress-seo' ) );
247
248 foreach ( $ranks as $rank ) {
249 $selected = selected( $this->get_current_readability_filter(), $rank->get_rank(), false );
250
251 // phpcs:ignore WordPress.Security.EscapeOutput -- Output is correctly escaped in the generate_option() method.
252 echo $this->generate_option( $rank->get_rank(), $rank->get_drop_down_readability_labels(), $selected );
253 }
254
255 echo '</select>';
256 }
257
258 /**
259 * Generates an <option> element.
260 *
261 * @param string $value The option's value.
262 * @param string $label The option's label.
263 * @param string $selected HTML selected attribute for an option.
264 *
265 * @return string The generated <option> element.
266 */
267 protected function generate_option( $value, $label, $selected = '' ) {
268 return '<option ' . $selected . ' value="' . esc_attr( $value ) . '">' . esc_html( $label ) . '</option>';
269 }
270
271 /**
272 * Returns the meta object for a given post ID.
273 *
274 * @param int $post_id The post ID.
275 *
276 * @return Meta The meta object.
277 */
278 protected function get_meta( $post_id ) {
279 $indexable = $this->admin_columns_cache->get_indexable( $post_id );
280
281 return YoastSEO()->meta->for_indexable( $indexable, 'Post_Type' );
282 }
283
284 /**
285 * Determines the SEO score filter to be later used in the meta query, based on the passed SEO filter.
286 *
287 * @param string $seo_filter The SEO filter to use to determine what further filter to apply.
288 *
289 * @return array The SEO score filter.
290 */
291 protected function determine_seo_filters( $seo_filter ) {
292 if ( $seo_filter === WPSEO_Rank::NO_FOCUS ) {
293 return $this->create_no_focus_keyword_filter();
294 }
295
296 if ( $seo_filter === WPSEO_Rank::NO_INDEX ) {
297 return $this->create_no_index_filter();
298 }
299
300 $rank = new WPSEO_Rank( $seo_filter );
301
302 return $this->create_seo_score_filter( $rank->get_starting_score(), $rank->get_end_score() );
303 }
304
305 /**
306 * Determines the Readability score filter to the meta query, based on the passed Readability filter.
307 *
308 * @param string $readability_filter The Readability filter to use to determine what further filter to apply.
309 *
310 * @return array The Readability score filter.
311 */
312 protected function determine_readability_filters( $readability_filter ) {
313 $rank = new WPSEO_Rank( $readability_filter );
314
315 return $this->create_readability_score_filter( $rank->get_starting_score(), $rank->get_end_score() );
316 }
317
318 /**
319 * Creates a keyword filter for the meta query, based on the passed Keyword filter.
320 *
321 * @param string $keyword_filter The keyword filter to use.
322 *
323 * @return array The keyword filter.
324 */
325 protected function get_keyword_filter( $keyword_filter ) {
326 return [
327 'post_type' => get_query_var( 'post_type', 'post' ),
328 'key' => WPSEO_Meta::$meta_prefix . 'focuskw',
329 'value' => sanitize_text_field( $keyword_filter ),
330 ];
331 }
332
333 /**
334 * Determines whether the passed filter is considered to be valid.
335 *
336 * @param mixed $filter The filter to check against.
337 *
338 * @return bool Whether or not the filter is considered valid.
339 */
340 protected function is_valid_filter( $filter ) {
341 return ! empty( $filter ) && is_string( $filter );
342 }
343
344 /**
345 * Collects the filters and merges them into a single array.
346 *
347 * @return array Array containing all the applicable filters.
348 */
349 protected function collect_filters() {
350 $active_filters = [];
351
352 $seo_filter = $this->get_current_seo_filter();
353 $readability_filter = $this->get_current_readability_filter();
354 $current_keyword_filter = $this->get_current_keyword_filter();
355
356 if ( $this->is_valid_filter( $seo_filter ) ) {
357 $active_filters = array_merge(
358 $active_filters,
359 $this->determine_seo_filters( $seo_filter )
360 );
361 }
362
363 if ( $this->is_valid_filter( $readability_filter ) ) {
364 $active_filters = array_merge(
365 $active_filters,
366 $this->determine_readability_filters( $readability_filter )
367 );
368 }
369
370 if ( $this->is_valid_filter( $current_keyword_filter ) ) {
371 $active_filters = array_merge(
372 $active_filters,
373 $this->get_keyword_filter( $current_keyword_filter )
374 );
375 }
376
377 return $active_filters;
378 }
379
380 /**
381 * Modify the query based on the filters that are being passed.
382 *
383 * @param array $vars Query variables that need to be modified based on the filters.
384 *
385 * @return array Array containing the meta query to use for filtering the posts overview.
386 */
387 public function column_sort_orderby( $vars ) {
388 $collected_filters = $this->collect_filters();
389
390 if ( isset( $vars['orderby'] ) ) {
391 $vars = array_merge( $vars, $this->filter_order_by( $vars['orderby'] ) );
392 }
393
394 return $this->build_filter_query( $vars, $collected_filters );
395 }
396
397 /**
398 * Retrieves the meta robots query values to be used within the meta query.
399 *
400 * @return array Array containing the query parameters regarding meta robots.
401 */
402 protected function get_meta_robots_query_values() {
403 return [
404 'relation' => 'OR',
405 [
406 'key' => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex',
407 'compare' => 'NOT EXISTS',
408 ],
409 [
410 'key' => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex',
411 'value' => '1',
412 'compare' => '!=',
413 ],
414 ];
415 }
416
417 /**
418 * Determines the score filters to be used. If more than one is passed, it created an AND statement for the query.
419 *
420 * @param array $score_filters Array containing the score filters.
421 *
422 * @return array Array containing the score filters that need to be applied to the meta query.
423 */
424 protected function determine_score_filters( $score_filters ) {
425 if ( count( $score_filters ) > 1 ) {
426 return array_merge( [ 'relation' => 'AND' ], $score_filters );
427 }
428
429 return $score_filters;
430 }
431
432 /**
433 * Retrieves the post type from the $_GET variable.
434 *
435 * @return string The current post type.
436 */
437 public function get_current_post_type() {
438 return filter_input( INPUT_GET, 'post_type' );
439 }
440
441 /**
442 * Retrieves the SEO filter from the $_GET variable.
443 *
444 * @return string The current post type.
445 */
446 public function get_current_seo_filter() {
447 return filter_input( INPUT_GET, 'seo_filter' );
448 }
449
450 /**
451 * Retrieves the Readability filter from the $_GET variable.
452 *
453 * @return string The current post type.
454 */
455 public function get_current_readability_filter() {
456 return filter_input( INPUT_GET, 'readability_filter' );
457 }
458
459 /**
460 * Retrieves the keyword filter from the $_GET variable.
461 *
462 * @return string The current post type.
463 */
464 public function get_current_keyword_filter() {
465 return filter_input( INPUT_GET, 'seo_kw_filter' );
466 }
467
468 /**
469 * Uses the vars to create a complete filter query that can later be executed to filter out posts.
470 *
471 * @param array $vars Array containing the variables that will be used in the meta query.
472 * @param array $filters Array containing the filters that we need to apply in the meta query.
473 *
474 * @return array Array containing the complete filter query.
475 */
476 protected function build_filter_query( $vars, $filters ) {
477 // If no filters were applied, just return everything.
478 if ( count( $filters ) === 0 ) {
479 return $vars;
480 }
481
482 $result = [ 'meta_query' => [] ];
483 $result['meta_query'] = array_merge( $result['meta_query'], [ $this->determine_score_filters( $filters ) ] );
484
485 $current_seo_filter = $this->get_current_seo_filter();
486
487 // 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 ) ) {
489 $result['meta_query'] = array_merge( $result['meta_query'], [ $this->get_meta_robots_query_values() ] );
490 }
491
492 return array_merge( $vars, $result );
493 }
494
495 /**
496 * Creates a Readability score filter.
497 *
498 * @param number $low The lower boundary of the score.
499 * @param number $high The higher boundary of the score.
500 *
501 * @return array The Readability Score filter.
502 */
503 protected function create_readability_score_filter( $low, $high ) {
504 return [
505 [
506 'key' => WPSEO_Meta::$meta_prefix . 'content_score',
507 'value' => [ $low, $high ],
508 'type' => 'numeric',
509 'compare' => 'BETWEEN',
510 ],
511 ];
512 }
513
514 /**
515 * Creates an SEO score filter.
516 *
517 * @param number $low The lower boundary of the score.
518 * @param number $high The higher boundary of the score.
519 *
520 * @return array The SEO score filter.
521 */
522 protected function create_seo_score_filter( $low, $high ) {
523 return [
524 [
525 'key' => WPSEO_Meta::$meta_prefix . 'linkdex',
526 'value' => [ $low, $high ],
527 'type' => 'numeric',
528 'compare' => 'BETWEEN',
529 ],
530 ];
531 }
532
533 /**
534 * Creates a filter to retrieve posts that were set to no-index.
535 *
536 * @return array Array containin the no-index filter.
537 */
538 protected function create_no_index_filter() {
539 return [
540 [
541 'key' => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex',
542 'value' => '1',
543 'compare' => '=',
544 ],
545 ];
546 }
547
548 /**
549 * Creates a filter to retrieve posts that have no keyword set.
550 *
551 * @return array Array containing the no focus keyword filter.
552 */
553 protected function create_no_focus_keyword_filter() {
554 return [
555 [
556 'key' => WPSEO_Meta::$meta_prefix . 'meta-robots-noindex',
557 'value' => 'needs-a-value-anyway',
558 'compare' => 'NOT EXISTS',
559 ],
560 [
561 'key' => WPSEO_Meta::$meta_prefix . 'linkdex',
562 'value' => 'needs-a-value-anyway',
563 'compare' => 'NOT EXISTS',
564 ],
565 ];
566 }
567
568 /**
569 * Determines whether a particular post_id is of an indexable post type.
570 *
571 * @param string $post_id The post ID to check.
572 *
573 * @return bool Whether or not it is indexable.
574 */
575 protected function is_indexable( $post_id ) {
576 if ( ! empty( $post_id ) && ! $this->uses_default_indexing( $post_id ) ) {
577 return WPSEO_Meta::get_value( 'meta-robots-noindex', $post_id ) === '2';
578 }
579
580 $post = get_post( $post_id );
581
582 if ( is_object( $post ) ) {
583 // If the option is false, this means we want to index it.
584 return WPSEO_Options::get( 'noindex-' . $post->post_type, false ) === false;
585 }
586
587 return true;
588 }
589
590 /**
591 * Determines whether the given post ID uses the default indexing settings.
592 *
593 * @param int $post_id The post ID to check.
594 *
595 * @return bool Whether or not the default indexing is being used for the post.
596 */
597 protected function uses_default_indexing( $post_id ) {
598 return WPSEO_Meta::get_value( 'meta-robots-noindex', $post_id ) === '0';
599 }
600
601 /**
602 * Returns filters when $order_by is matched in the if-statement.
603 *
604 * @param string $order_by The ID of the column by which to order the posts.
605 *
606 * @return array Array containing the order filters.
607 */
608 private function filter_order_by( $order_by ) {
609 switch ( $order_by ) {
610 case 'wpseo-metadesc':
611 return [
612 'meta_key' => WPSEO_Meta::$meta_prefix . 'metadesc',
613 'orderby' => 'meta_value',
614 ];
615
616 case 'wpseo-focuskw':
617 return [
618 'meta_key' => WPSEO_Meta::$meta_prefix . 'focuskw',
619 'orderby' => 'meta_value',
620 ];
621 }
622
623 return [];
624 }
625
626 /**
627 * Parses the score column.
628 *
629 * @param int $post_id The ID of the post for which to show the score.
630 *
631 * @return string The HTML for the SEO score indicator.
632 */
633 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' );
637
638 WPSEO_Meta::set_value( 'linkdex', 0, $post_id );
639
640 return $this->render_score_indicator( $rank, $title );
641 }
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 }
656
657 /**
658 * Parsing the readability score column.
659 *
660 * @param int $post_id The ID of the post for which to show the readability score.
661 *
662 * @return string The HTML for the readability score indicator.
663 */
664 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 );
669 }
670
671 /**
672 * Sets up the hooks for the post_types.
673 */
674 private function set_post_type_hooks() {
675 $post_types = WPSEO_Post_Type::get_accessible_post_types();
676
677 if ( ! is_array( $post_types ) || $post_types === [] ) {
678 return;
679 }
680
681 foreach ( $post_types as $post_type ) {
682 if ( $this->display_metabox( $post_type ) === false ) {
683 continue;
684 }
685
686 add_filter( 'manage_' . $post_type . '_posts_columns', [ $this, 'column_heading' ], 10, 1 );
687 add_action( 'manage_' . $post_type . '_posts_custom_column', [ $this, 'column_content' ], 10, 2 );
688 add_action( 'manage_edit-' . $post_type . '_sortable_columns', [ $this, 'column_sort' ], 10, 2 );
689 }
690
691 unset( $post_type );
692 }
693
694 /**
695 * Wraps the WPSEO_Metabox check to determine whether the metabox should be displayed either by
696 * choice of the admin or because the post type is not a public post type.
697 *
698 * @since 7.0
699 *
700 * @param string|null $post_type Optional. The post type to test, defaults to the current post post_type.
701 *
702 * @return bool Whether or not the meta box (and associated columns etc) should be hidden.
703 */
704 private function display_metabox( $post_type = null ) {
705 $current_post_type = sanitize_text_field( $this->get_current_post_type() );
706
707 if ( ! isset( $post_type ) && ! empty( $current_post_type ) ) {
708 $post_type = $current_post_type;
709 }
710
711 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 }
729
730 /**
731 * Determines whether or not filter dropdowns should be displayed.
732 *
733 * @return bool Whether or the current page can display the filter drop downs.
734 */
735 public function can_display_filter() {
736 if ( $GLOBALS['pagenow'] === 'upload.php' ) {
737 return false;
738 }
739
740 if ( $this->display_metabox() === false ) {
741 return false;
742 }
743
744 $screen = get_current_screen();
745 if ( $screen === null ) {
746 return false;
747 }
748
749 return WPSEO_Post_Type::is_post_type_accessible( $screen->post_type );
750 }
751 }
752