class-link-columns.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. <?php
  2. /**
  3. * WPSEO plugin file.
  4. *
  5. * @package WPSEO\Admin\Links
  6. */
  7. /**
  8. * Represents the link columns. This class will add and handle the link columns.
  9. */
  10. class WPSEO_Link_Columns {
  11. /**
  12. * @var string Partial column name.
  13. */
  14. const COLUMN_LINKED = 'linked';
  15. /**
  16. * @var string Partial column name.
  17. */
  18. const COLUMN_LINKS = 'links';
  19. /**
  20. * @var WPSEO_Link_Column_Count
  21. */
  22. protected $link_count;
  23. /**
  24. * @var WPSEO_Meta_Storage Storage to use.
  25. */
  26. protected $storage;
  27. /**
  28. * @var array List of public post types.
  29. */
  30. protected $public_post_types = array();
  31. /**
  32. * WPSEO_Link_Columns constructor.
  33. *
  34. * @param WPSEO_Meta_Storage $storage The storage object to use.
  35. */
  36. public function __construct( WPSEO_Meta_Storage $storage ) {
  37. $this->storage = $storage;
  38. }
  39. /**
  40. * Registers the hooks.
  41. */
  42. public function register_hooks() {
  43. global $pagenow;
  44. $is_ajax_request = defined( 'DOING_AJAX' ) && DOING_AJAX;
  45. if ( ! WPSEO_Metabox::is_post_overview( $pagenow ) && ! $is_ajax_request ) {
  46. return;
  47. }
  48. // Exit when either table is not present or accessible.
  49. if ( ! WPSEO_Link_Table_Accessible::is_accessible() || ! WPSEO_Meta_Table_Accessible::is_accessible() ) {
  50. return;
  51. }
  52. if ( $is_ajax_request ) {
  53. add_action( 'admin_init', array( $this, 'set_count_objects' ) );
  54. }
  55. // Hook into tablenav to calculate links and linked.
  56. add_action( 'manage_posts_extra_tablenav', array( $this, 'count_objects' ) );
  57. add_filter( 'posts_clauses', array( $this, 'order_by_links' ), 1, 2 );
  58. add_filter( 'posts_clauses', array( $this, 'order_by_linked' ), 1, 2 );
  59. add_filter( 'admin_init', array( $this, 'register_init_hooks' ) );
  60. }
  61. /**
  62. * Register hooks that require to be registered after `init`.
  63. */
  64. public function register_init_hooks() {
  65. $this->public_post_types = apply_filters( 'wpseo_link_count_post_types', WPSEO_Post_Type::get_accessible_post_types() );
  66. if ( is_array( $this->public_post_types ) && $this->public_post_types !== array() ) {
  67. array_walk( $this->public_post_types, array( $this, 'set_post_type_hooks' ) );
  68. }
  69. }
  70. /**
  71. * Modifies the query pieces to allow ordering column by links to post.
  72. *
  73. * @param array $pieces Array of Query pieces.
  74. * @param \WP_Query $query The Query on which to apply.
  75. *
  76. * @return array
  77. */
  78. public function order_by_links( $pieces, $query ) {
  79. if ( 'wpseo-' . self::COLUMN_LINKS !== $query->get( 'orderby' ) ) {
  80. return $pieces;
  81. }
  82. return $this->build_sort_query_pieces( $pieces, $query, 'internal_link_count' );
  83. }
  84. /**
  85. * Modifies the query pieces to allow ordering column by links to post.
  86. *
  87. * @param array $pieces Array of Query pieces.
  88. * @param \WP_Query $query The Query on which to apply.
  89. *
  90. * @return array
  91. */
  92. public function order_by_linked( $pieces, $query ) {
  93. if ( 'wpseo-' . self::COLUMN_LINKED !== $query->get( 'orderby' ) ) {
  94. return $pieces;
  95. }
  96. return $this->build_sort_query_pieces( $pieces, $query, 'incoming_link_count' );
  97. }
  98. /**
  99. * Builds the pieces for a sorting query.
  100. *
  101. * @param array $pieces Array of Query pieces.
  102. * @param \WP_Query $query The Query on which to apply.
  103. * @param string $field The field in the table to JOIN on.
  104. *
  105. * @return array Modified Query pieces.
  106. */
  107. protected function build_sort_query_pieces( $pieces, $query, $field ) {
  108. global $wpdb;
  109. // We only want our code to run in the main WP query.
  110. if ( ! $query->is_main_query() ) {
  111. return $pieces;
  112. }
  113. // Get the order query variable - ASC or DESC.
  114. $order = strtoupper( $query->get( 'order' ) );
  115. // Make sure the order setting qualifies. If not, set default as ASC.
  116. if ( ! in_array( $order, array( 'ASC', 'DESC' ), true ) ) {
  117. $order = 'ASC';
  118. }
  119. $table = $this->storage->get_table_name();
  120. $pieces['join'] .= " LEFT JOIN $table AS yst_links ON yst_links.object_id = {$wpdb->posts}.ID ";
  121. $pieces['orderby'] = "{$field} $order, FIELD( {$wpdb->posts}.post_status, 'publish' ) $order, {$pieces['orderby']}";
  122. return $pieces;
  123. }
  124. /**
  125. * Sets the hooks for each post type.
  126. *
  127. * @param string $post_type The post type.
  128. */
  129. public function set_post_type_hooks( $post_type ) {
  130. add_filter( 'manage_' . $post_type . '_posts_columns', array( $this, 'add_post_columns' ) );
  131. add_action( 'manage_' . $post_type . '_posts_custom_column', array( $this, 'column_content' ), 10, 2 );
  132. add_filter( 'manage_edit-' . $post_type . '_sortable_columns', array( $this, 'column_sort' ) );
  133. }
  134. /**
  135. * Adds the columns for the post overview.
  136. *
  137. * @param array $columns Array with columns.
  138. *
  139. * @return array The extended array with columns.
  140. */
  141. public function add_post_columns( $columns ) {
  142. if ( ! is_array( $columns ) ) {
  143. return $columns;
  144. }
  145. $columns[ 'wpseo-' . self::COLUMN_LINKS ] = '<span class="yoast-linked-to yoast-column-header-has-tooltip" data-label="' . esc_attr__( 'Number of internal links in this post. See "Yoast Columns" text in the help tab for more info.', 'wordpress-seo' ) . '"><span class="screen-reader-text">' . __( '# links in post', 'wordpress-seo' ) . '</span></span>';
  146. if ( ! WPSEO_Link_Query::has_unprocessed_posts( $this->public_post_types ) ) {
  147. $columns[ 'wpseo-' . self::COLUMN_LINKED ] = '<span class="yoast-linked-from yoast-column-header-has-tooltip" data-label="' . esc_attr__( 'Number of internal links linking to this post. See "Yoast Columns" text in the help tab for more info.', 'wordpress-seo' ) . '"><span class="screen-reader-text">' . __( '# internal links to', 'wordpress-seo' ) . '</span></span>';
  148. }
  149. return $columns;
  150. }
  151. /**
  152. * Makes sure we calculate all values in one query.
  153. *
  154. * @param string $target Extra table navigation location which is triggered.
  155. */
  156. public function count_objects( $target ) {
  157. if ( 'top' === $target ) {
  158. $this->set_count_objects();
  159. }
  160. }
  161. /**
  162. * Sets the objects to use for the count.
  163. */
  164. public function set_count_objects() {
  165. global $wp_query;
  166. $posts = $wp_query->get_posts();
  167. $post_ids = array();
  168. // Post lists return a list of objects.
  169. if ( isset( $posts[0] ) && is_object( $posts[0] ) ) {
  170. $post_ids = wp_list_pluck( $posts, 'ID' );
  171. }
  172. elseif ( ! empty( $posts ) ) {
  173. // Page list returns an array of post IDs.
  174. $post_ids = array_keys( $posts );
  175. }
  176. $post_ids = WPSEO_Link_Query::filter_unprocessed_posts( $post_ids );
  177. $links = new WPSEO_Link_Column_Count();
  178. $links->set( $post_ids );
  179. $this->link_count = $links;
  180. }
  181. /**
  182. * Displays the column content for the given column
  183. *
  184. * @param string $column_name Column to display the content for.
  185. * @param int $post_id Post to display the column content for.
  186. */
  187. public function column_content( $column_name, $post_id ) {
  188. $link_count = null;
  189. switch ( $column_name ) {
  190. case 'wpseo-' . self::COLUMN_LINKS:
  191. $link_count = $this->link_count->get( $post_id, 'internal_link_count' );
  192. break;
  193. case 'wpseo-' . self::COLUMN_LINKED:
  194. if ( get_post_status( $post_id ) === 'publish' ) {
  195. $link_count = $this->link_count->get( $post_id, 'incoming_link_count' );
  196. }
  197. break;
  198. }
  199. if ( isset( $link_count ) ) {
  200. echo (int) $link_count;
  201. }
  202. }
  203. /**
  204. * Sets the sortable columns.
  205. *
  206. * @param array $columns Array with sortable columns.
  207. *
  208. * @return array The extended array with sortable columns.
  209. */
  210. public function column_sort( array $columns ) {
  211. $columns[ 'wpseo-' . self::COLUMN_LINKS ] = 'wpseo-' . self::COLUMN_LINKS;
  212. $columns[ 'wpseo-' . self::COLUMN_LINKED ] = 'wpseo-' . self::COLUMN_LINKED;
  213. return $columns;
  214. }
  215. }