class-meta-columns.php 22 KB

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