class-author-sitemap-provider.php 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. <?php
  2. /**
  3. * WPSEO plugin file.
  4. *
  5. * @package WPSEO\XML_Sitemaps
  6. */
  7. /**
  8. * Sitemap provider for author archives.
  9. */
  10. class WPSEO_Author_Sitemap_Provider implements WPSEO_Sitemap_Provider {
  11. /**
  12. * Check if provider supports given item type.
  13. *
  14. * @param string $type Type string to check for.
  15. *
  16. * @return boolean
  17. */
  18. public function handles_type( $type ) {
  19. // If the author archives have been disabled, we don't do anything.
  20. if ( WPSEO_Options::get( 'disable-author', false ) || WPSEO_Options::get( 'noindex-author-wpseo', false ) ) {
  21. return false;
  22. }
  23. return $type === 'author';
  24. }
  25. /**
  26. * Get the links for the sitemap index.
  27. *
  28. * @param int $max_entries Entries per sitemap.
  29. *
  30. * @return array
  31. */
  32. public function get_index_links( $max_entries ) {
  33. if ( ! $this->handles_type( 'author' ) ) {
  34. return [];
  35. }
  36. // @todo Consider doing this less often / when necessary. R.
  37. $this->update_user_meta();
  38. $has_exclude_filter = has_filter( 'wpseo_sitemap_exclude_author' );
  39. $query_arguments = [];
  40. if ( ! $has_exclude_filter ) { // We only need full users if legacy filter(s) hooked to exclusion logic. R.
  41. $query_arguments['fields'] = 'ID';
  42. }
  43. $users = $this->get_users( $query_arguments );
  44. if ( $has_exclude_filter ) {
  45. $users = $this->exclude_users( $users );
  46. $users = wp_list_pluck( $users, 'ID' );
  47. }
  48. if ( empty( $users ) ) {
  49. return [];
  50. }
  51. $index = [];
  52. $page = 1;
  53. $user_pages = array_chunk( $users, $max_entries );
  54. if ( count( $user_pages ) === 1 ) {
  55. $page = '';
  56. }
  57. foreach ( $user_pages as $users_page ) {
  58. $user_id = array_shift( $users_page ); // Time descending, first user on page is most recently updated.
  59. $user = get_user_by( 'id', $user_id );
  60. $index[] = [
  61. 'loc' => WPSEO_Sitemaps_Router::get_base_url( 'author-sitemap' . $page . '.xml' ),
  62. 'lastmod' => '@' . $user->_yoast_wpseo_profile_updated, // @ for explicit timestamp format
  63. ];
  64. $page++;
  65. }
  66. return $index;
  67. }
  68. /**
  69. * Retrieve users, taking account of all necessary exclusions.
  70. *
  71. * @param array $arguments Arguments to add.
  72. *
  73. * @return array
  74. */
  75. protected function get_users( $arguments = [] ) {
  76. global $wpdb;
  77. $defaults = [
  78. // @todo Re-enable after plugin requirements raised to WP 4.6 with the fix.
  79. // 'who' => 'authors', Breaks meta keys, {@link https://core.trac.wordpress.org/ticket/36724#ticket} R.
  80. 'meta_key' => '_yoast_wpseo_profile_updated',
  81. 'orderby' => 'meta_value_num',
  82. 'order' => 'DESC',
  83. 'meta_query' => [
  84. 'relation' => 'AND',
  85. [
  86. 'key' => $wpdb->get_blog_prefix() . 'user_level',
  87. 'value' => '0',
  88. 'compare' => '!=',
  89. ],
  90. [
  91. 'relation' => 'OR',
  92. [
  93. 'key' => 'wpseo_noindex_author',
  94. 'value' => 'on',
  95. 'compare' => '!=',
  96. ],
  97. [
  98. 'key' => 'wpseo_noindex_author',
  99. 'compare' => 'NOT EXISTS',
  100. ],
  101. ],
  102. ],
  103. ];
  104. if ( WPSEO_Options::get( 'noindex-author-noposts-wpseo', true ) ) {
  105. // $defaults['who'] = ''; // Otherwise it cancels out next argument.
  106. $defaults['has_published_posts'] = true;
  107. }
  108. return get_users( array_merge( $defaults, $arguments ) );
  109. }
  110. /**
  111. * Get set of sitemap link data.
  112. *
  113. * @param string $type Sitemap type.
  114. * @param int $max_entries Entries per sitemap.
  115. * @param int $current_page Current page of the sitemap.
  116. *
  117. * @throws OutOfBoundsException When an invalid page is requested.
  118. *
  119. * @return array
  120. */
  121. public function get_sitemap_links( $type, $max_entries, $current_page ) {
  122. $links = [];
  123. if ( ! $this->handles_type( 'author' ) ) {
  124. return $links;
  125. }
  126. $user_criteria = [
  127. 'offset' => ( ( $current_page - 1 ) * $max_entries ),
  128. 'number' => $max_entries,
  129. ];
  130. $users = $this->get_users( $user_criteria );
  131. // Throw an exception when there are no users in the sitemap.
  132. if ( count( $users ) === 0 ) {
  133. throw new OutOfBoundsException( 'Invalid sitemap page requested' );
  134. }
  135. $users = $this->exclude_users( $users );
  136. if ( empty( $users ) ) {
  137. $users = [];
  138. }
  139. $time = time();
  140. foreach ( $users as $user ) {
  141. $author_link = get_author_posts_url( $user->ID );
  142. if ( empty( $author_link ) ) {
  143. continue;
  144. }
  145. $mod = $time;
  146. if ( isset( $user->_yoast_wpseo_profile_updated ) ) {
  147. $mod = $user->_yoast_wpseo_profile_updated;
  148. }
  149. $url = [
  150. 'loc' => $author_link,
  151. 'mod' => date( DATE_W3C, $mod ),
  152. // Deprecated, kept for backwards data compat. R.
  153. 'chf' => 'daily',
  154. 'pri' => 1,
  155. ];
  156. /** This filter is documented at inc/sitemaps/class-post-type-sitemap-provider.php */
  157. $url = apply_filters( 'wpseo_sitemap_entry', $url, 'user', $user );
  158. if ( ! empty( $url ) ) {
  159. $links[] = $url;
  160. }
  161. }
  162. return $links;
  163. }
  164. /**
  165. * Update any users that don't have last profile update timestamp.
  166. *
  167. * @return int Count of users updated.
  168. */
  169. protected function update_user_meta() {
  170. $user_criteria = [
  171. 'who' => 'authors',
  172. 'meta_query' => [
  173. [
  174. 'key' => '_yoast_wpseo_profile_updated',
  175. 'compare' => 'NOT EXISTS',
  176. ],
  177. ],
  178. ];
  179. $users = get_users( $user_criteria );
  180. $time = time();
  181. foreach ( $users as $user ) {
  182. update_user_meta( $user->ID, '_yoast_wpseo_profile_updated', $time );
  183. }
  184. return count( $users );
  185. }
  186. /**
  187. * Wrap legacy filter to deduplicate calls.
  188. *
  189. * @param array $users Array of user objects to filter.
  190. *
  191. * @return array
  192. */
  193. protected function exclude_users( $users ) {
  194. /**
  195. * Filter the authors, included in XML sitemap.
  196. *
  197. * @param array $users Array of user objects to filter.
  198. */
  199. return apply_filters( 'wpseo_sitemap_exclude_author', $users );
  200. }
  201. }