| 1 |
<?php |
| 2 |
/** |
| 3 |
* WPSEO plugin file. |
| 4 |
* |
| 5 |
* @package WPSEO\XML_Sitemaps |
| 6 |
*/ |
| 7 |
|
| 8 |
use Yoast\WP\SEO\Helpers\Author_Archive_Helper; |
| 9 |
use Yoast\WP\SEO\Helpers\Wordpress_Helper; |
| 10 |
|
| 11 |
/** |
| 12 |
* Sitemap provider for author archives. |
| 13 |
*/ |
| 14 |
class WPSEO_Author_Sitemap_Provider implements WPSEO_Sitemap_Provider { |
| 15 |
|
| 16 |
/** |
| 17 |
* Check if provider supports given item type. |
| 18 |
* |
| 19 |
* @param string $type Type string to check for. |
| 20 |
* |
| 21 |
* @return bool |
| 22 |
*/ |
| 23 |
public function handles_type( $type ) { |
| 24 |
// If the author archives have been disabled, we don't do anything. |
| 25 |
if ( WPSEO_Options::get( 'disable-author', false ) || WPSEO_Options::get( 'noindex-author-wpseo', false ) ) { |
| 26 |
return false; |
| 27 |
} |
| 28 |
|
| 29 |
return $type === 'author'; |
| 30 |
} |
| 31 |
|
| 32 |
/** |
| 33 |
* Get the links for the sitemap index. |
| 34 |
* |
| 35 |
* @param int $max_entries Entries per sitemap. |
| 36 |
* |
| 37 |
* @return array |
| 38 |
*/ |
| 39 |
public function get_index_links( $max_entries ) { |
| 40 |
|
| 41 |
if ( ! $this->handles_type( 'author' ) ) { |
| 42 |
return []; |
| 43 |
} |
| 44 |
|
| 45 |
// @todo Consider doing this less often / when necessary. R. |
| 46 |
$this->update_user_meta(); |
| 47 |
|
| 48 |
$has_exclude_filter = has_filter( 'wpseo_sitemap_exclude_author' ); |
| 49 |
|
| 50 |
$query_arguments = []; |
| 51 |
|
| 52 |
if ( ! $has_exclude_filter ) { // We only need full users if legacy filter(s) hooked to exclusion logic. R. |
| 53 |
$query_arguments['fields'] = 'ID'; |
| 54 |
} |
| 55 |
|
| 56 |
$users = $this->get_users( $query_arguments ); |
| 57 |
|
| 58 |
if ( $has_exclude_filter ) { |
| 59 |
$users = $this->exclude_users( $users ); |
| 60 |
$users = wp_list_pluck( $users, 'ID' ); |
| 61 |
} |
| 62 |
|
| 63 |
if ( empty( $users ) ) { |
| 64 |
return []; |
| 65 |
} |
| 66 |
|
| 67 |
$index = []; |
| 68 |
$user_pages = array_chunk( $users, $max_entries ); |
| 69 |
|
| 70 |
foreach ( $user_pages as $page_counter => $users_page ) { |
| 71 |
|
| 72 |
$current_page = ( $page_counter === 0 ) ? '' : ( $page_counter + 1 ); |
| 73 |
|
| 74 |
$user_id = array_shift( $users_page ); // Time descending, first user on page is most recently updated. |
| 75 |
$user = get_user_by( 'id', $user_id ); |
| 76 |
$index[] = [ |
| 77 |
'loc' => WPSEO_Sitemaps_Router::get_base_url( 'author-sitemap' . $current_page . '.xml' ), |
| 78 |
'lastmod' => ( $user->_yoast_wpseo_profile_updated ) ? YoastSEO()->helpers->date->format_timestamp( $user->_yoast_wpseo_profile_updated ) : null, |
| 79 |
]; |
| 80 |
} |
| 81 |
|
| 82 |
return $index; |
| 83 |
} |
| 84 |
|
| 85 |
/** |
| 86 |
* Retrieve users, taking account of all necessary exclusions. |
| 87 |
* |
| 88 |
* @param array $arguments Arguments to add. |
| 89 |
* |
| 90 |
* @return array |
| 91 |
*/ |
| 92 |
protected function get_users( $arguments = [] ) { |
| 93 |
|
| 94 |
global $wpdb; |
| 95 |
|
| 96 |
$defaults = [ |
| 97 |
'capability' => [ 'edit_posts' ], |
| 98 |
'meta_key' => '_yoast_wpseo_profile_updated', |
| 99 |
'orderby' => 'meta_value_num', |
| 100 |
'order' => 'DESC', |
| 101 |
'meta_query' => [ |
| 102 |
'relation' => 'AND', |
| 103 |
[ |
| 104 |
'key' => $wpdb->get_blog_prefix() . 'user_level', |
| 105 |
'value' => '0', |
| 106 |
'compare' => '!=', |
| 107 |
], |
| 108 |
[ |
| 109 |
'relation' => 'OR', |
| 110 |
[ |
| 111 |
'key' => 'wpseo_noindex_author', |
| 112 |
'value' => 'on', |
| 113 |
'compare' => '!=', |
| 114 |
], |
| 115 |
[ |
| 116 |
'key' => 'wpseo_noindex_author', |
| 117 |
'compare' => 'NOT EXISTS', |
| 118 |
], |
| 119 |
], |
| 120 |
], |
| 121 |
]; |
| 122 |
|
| 123 |
$wordpress_helper = new Wordpress_Helper(); |
| 124 |
$wordpress_version = $wordpress_helper->get_wordpress_version(); |
| 125 |
|
| 126 |
// Capability queries were only introduced in WP 5.9. |
| 127 |
if ( version_compare( $wordpress_version, '5.8.99', '<' ) ) { |
| 128 |
$defaults['who'] = 'authors'; |
| 129 |
unset( $defaults['capability'] ); |
| 130 |
} |
| 131 |
|
| 132 |
if ( WPSEO_Options::get( 'noindex-author-noposts-wpseo', true ) ) { |
| 133 |
unset( $defaults['who'], $defaults['capability'] ); // Otherwise it cancels out next argument. |
| 134 |
$author_archive = new Author_Archive_Helper(); |
| 135 |
$defaults['has_published_posts'] = $author_archive->get_author_archive_post_types(); |
| 136 |
} |
| 137 |
|
| 138 |
return get_users( array_merge( $defaults, $arguments ) ); |
| 139 |
} |
| 140 |
|
| 141 |
/** |
| 142 |
* Get set of sitemap link data. |
| 143 |
* |
| 144 |
* @param string $type Sitemap type. |
| 145 |
* @param int $max_entries Entries per sitemap. |
| 146 |
* @param int $current_page Current page of the sitemap. |
| 147 |
* |
| 148 |
* @return array |
| 149 |
* |
| 150 |
* @throws OutOfBoundsException When an invalid page is requested. |
| 151 |
*/ |
| 152 |
public function get_sitemap_links( $type, $max_entries, $current_page ) { |
| 153 |
|
| 154 |
$links = []; |
| 155 |
|
| 156 |
if ( ! $this->handles_type( 'author' ) ) { |
| 157 |
return $links; |
| 158 |
} |
| 159 |
|
| 160 |
$user_criteria = [ |
| 161 |
'offset' => ( ( $current_page - 1 ) * $max_entries ), |
| 162 |
'number' => $max_entries, |
| 163 |
]; |
| 164 |
|
| 165 |
$users = $this->get_users( $user_criteria ); |
| 166 |
|
| 167 |
// Throw an exception when there are no users in the sitemap. |
| 168 |
if ( count( $users ) === 0 ) { |
| 169 |
throw new OutOfBoundsException( 'Invalid sitemap page requested' ); |
| 170 |
} |
| 171 |
|
| 172 |
$users = $this->exclude_users( $users ); |
| 173 |
if ( empty( $users ) ) { |
| 174 |
$users = []; |
| 175 |
} |
| 176 |
|
| 177 |
$time = time(); |
| 178 |
|
| 179 |
foreach ( $users as $user ) { |
| 180 |
|
| 181 |
$author_link = get_author_posts_url( $user->ID ); |
| 182 |
|
| 183 |
if ( empty( $author_link ) ) { |
| 184 |
continue; |
| 185 |
} |
| 186 |
|
| 187 |
$mod = $time; |
| 188 |
|
| 189 |
if ( isset( $user->_yoast_wpseo_profile_updated ) ) { |
| 190 |
$mod = $user->_yoast_wpseo_profile_updated; |
| 191 |
} |
| 192 |
|
| 193 |
$url = [ |
| 194 |
'loc' => $author_link, |
| 195 |
'mod' => date( DATE_W3C, $mod ), |
| 196 |
|
| 197 |
// Deprecated, kept for backwards data compat. R. |
| 198 |
'chf' => 'daily', |
| 199 |
'pri' => 1, |
| 200 |
]; |
| 201 |
|
| 202 |
/** This filter is documented at inc/sitemaps/class-post-type-sitemap-provider.php */ |
| 203 |
$url = apply_filters( 'wpseo_sitemap_entry', $url, 'user', $user ); |
| 204 |
|
| 205 |
if ( ! empty( $url ) ) { |
| 206 |
$links[] = $url; |
| 207 |
} |
| 208 |
} |
| 209 |
|
| 210 |
return $links; |
| 211 |
} |
| 212 |
|
| 213 |
/** |
| 214 |
* Update any users that don't have last profile update timestamp. |
| 215 |
* |
| 216 |
* @return int Count of users updated. |
| 217 |
*/ |
| 218 |
protected function update_user_meta() { |
| 219 |
|
| 220 |
$user_criteria = [ |
| 221 |
'capability' => [ 'edit_posts' ], |
| 222 |
'meta_query' => [ |
| 223 |
[ |
| 224 |
'key' => '_yoast_wpseo_profile_updated', |
| 225 |
'compare' => 'NOT EXISTS', |
| 226 |
], |
| 227 |
], |
| 228 |
]; |
| 229 |
|
| 230 |
$wordpress_helper = new Wordpress_Helper(); |
| 231 |
$wordpress_version = $wordpress_helper->get_wordpress_version(); |
| 232 |
|
| 233 |
// Capability queries were only introduced in WP 5.9. |
| 234 |
if ( version_compare( $wordpress_version, '5.8.99', '<' ) ) { |
| 235 |
$user_criteria['who'] = 'authors'; |
| 236 |
unset( $user_criteria['capability'] ); |
| 237 |
} |
| 238 |
|
| 239 |
$users = get_users( $user_criteria ); |
| 240 |
|
| 241 |
$time = time(); |
| 242 |
|
| 243 |
foreach ( $users as $user ) { |
| 244 |
update_user_meta( $user->ID, '_yoast_wpseo_profile_updated', $time ); |
| 245 |
} |
| 246 |
|
| 247 |
return count( $users ); |
| 248 |
} |
| 249 |
|
| 250 |
/** |
| 251 |
* Wrap legacy filter to deduplicate calls. |
| 252 |
* |
| 253 |
* @param array $users Array of user objects to filter. |
| 254 |
* |
| 255 |
* @return array |
| 256 |
*/ |
| 257 |
protected function exclude_users( $users ) { |
| 258 |
|
| 259 |
/** |
| 260 |
* Filter the authors, included in XML sitemap. |
| 261 |
* |
| 262 |
* @param array $users Array of user objects to filter. |
| 263 |
*/ |
| 264 |
return apply_filters( 'wpseo_sitemap_exclude_author', $users ); |
| 265 |
} |
| 266 |
} |
| 267 |
|