WPSEO_Post_Type_Sitemap_Provider

Sitemap provider for author archives.

Defined (1)

The class is defined in the following location(s).

/inc/sitemaps/class-post-type-sitemap-provider.php  
  1. class WPSEO_Post_Type_Sitemap_Provider implements WPSEO_Sitemap_Provider { 
  2.  
  3. /** @var string $home_url Holds the home_url() value to speed up loops. */ 
  4. protected static $home_url; 
  5.  
  6. /** @var array $options All of plugin options. */ 
  7. protected static $options; 
  8.  
  9. /** @var WPSEO_Sitemap_Image_Parser $image_parser Holds image parser instance. */ 
  10. protected static $image_parser; 
  11.  
  12. /** @var int $page_on_front_id Static front page ID. */ 
  13. protected static $page_on_front_id; 
  14.  
  15. /** @var int $page_for_posts_id Posts page ID. */ 
  16. protected static $page_for_posts_id; 
  17.  
  18. /** 
  19. * Set up object properties for data reuse. 
  20. */ 
  21. public function __construct() { 
  22. add_filter( 'save_post', array( $this, 'save_post' ) ); 
  23.  
  24. /** 
  25. * Get front page ID 
  26. * @return int 
  27. */ 
  28. protected function get_page_on_front_id() { 
  29. if ( ! isset( self::$page_on_front_id ) ) { 
  30. self::$page_on_front_id = (int) get_option( 'page_on_front' ); 
  31.  
  32. return self::$page_on_front_id; 
  33.  
  34. /** 
  35. * Get page for posts ID 
  36. * @return int 
  37. */ 
  38. protected function get_page_for_posts_id() { 
  39. if ( ! isset( self::$page_for_posts_id ) ) { 
  40. self::$page_for_posts_id = (int) get_option( 'page_for_posts' ); 
  41.  
  42. return self::$page_for_posts_id; 
  43.  
  44. /** 
  45. * Get the Image Parser 
  46. * @return WPSEO_Sitemap_Image_Parser 
  47. */ 
  48. protected function get_image_parser() { 
  49. if ( ! isset( self::$image_parser ) ) { 
  50. self::$image_parser = new WPSEO_Sitemap_Image_Parser(); 
  51.  
  52. return self::$image_parser; 
  53.  
  54. /** 
  55. * Get Home URL 
  56. * This has been moved from the constructor because wp_rewrite is not available on plugins_loaded in multisite. 
  57. * It will now be requested on need and not on initialization. 
  58. * @return string 
  59. */ 
  60. protected function get_home_url() { 
  61. if ( ! isset( self::$home_url ) ) { 
  62. self::$home_url = WPSEO_Utils::home_url(); 
  63.  
  64. return self::$home_url; 
  65.  
  66. /** 
  67. * Get all the options 
  68. * @return array 
  69. */ 
  70. protected function get_options() { 
  71. if ( ! isset( self::$options ) ) { 
  72. self::$options = WPSEO_Options::get_all(); 
  73.  
  74. return self::$options; 
  75.  
  76. /** 
  77. * Check if provider supports given item type. 
  78. * @param string $type Type string to check for. 
  79. * @return boolean 
  80. */ 
  81. public function handles_type( $type ) { 
  82.  
  83. return post_type_exists( $type ); 
  84.  
  85. /** 
  86. * @param int $max_entries Entries per sitemap. 
  87. * @return array 
  88. */ 
  89. public function get_index_links( $max_entries ) { 
  90.  
  91. global $wpdb; 
  92.  
  93. $post_types = get_post_types( array( 'public' => true ) ); 
  94. $post_types = array_filter( $post_types, array( $this, 'is_valid_post_type' ) ); 
  95. $last_modified_times = WPSEO_Sitemaps::get_last_modified_gmt( $post_types, true ); 
  96. $index = array(); 
  97.  
  98. foreach ( $post_types as $post_type ) { 
  99.  
  100. $total_count = $this->get_post_type_count( $post_type ); 
  101.  
  102. if ( $total_count === 0 ) { 
  103. continue; 
  104.  
  105. $max_pages = 1; 
  106.  
  107. if ( $total_count > $max_entries ) { 
  108. $max_pages = (int) ceil( $total_count / $max_entries ); 
  109.  
  110. $all_dates = array(); 
  111.  
  112. if ( $max_pages > 1 ) { 
  113.  
  114. $sql = " 
  115. SELECT post_modified_gmt 
  116. FROM ( SELECT @rownum:=0 ) init  
  117. JOIN {$wpdb->posts} USE INDEX( type_status_date ) 
  118. WHERE post_status IN ( 'publish', 'inherit' ) 
  119. AND post_type = %s 
  120. AND ( @rownum:=@rownum+1 ) %% %d = 0 
  121. ORDER BY post_modified_gmt ASC 
  122. "; 
  123.  
  124. $all_dates = $wpdb->get_col( $wpdb->prepare( $sql, $post_type, $max_entries ) ); 
  125.  
  126. for ( $page_counter = 0; $page_counter < $max_pages; $page_counter++ ) { 
  127.  
  128. $current_page = ( $max_pages > 1 ) ? ( $page_counter + 1 ) : ''; 
  129. $date = false; 
  130.  
  131. if ( empty( $current_page ) || $current_page === $max_pages ) { 
  132.  
  133. if ( ! empty( $last_modified_times[ $post_type ] ) ) { 
  134. $date = $last_modified_times[ $post_type ]; 
  135. else { 
  136. $date = $all_dates[ $page_counter ]; 
  137.  
  138. $index[] = array( 
  139. 'loc' => WPSEO_Sitemaps_Router::get_base_url( $post_type . '-sitemap' . $current_page . '.xml' ),  
  140. 'lastmod' => $date,  
  141. ); 
  142.  
  143. return $index; 
  144.  
  145. /** 
  146. * Get set of sitemap link data. 
  147. * @param string $type Sitemap type. 
  148. * @param int $max_entries Entries per sitemap. 
  149. * @param int $current_page Current page of the sitemap. 
  150. * @return array 
  151. */ 
  152. public function get_sitemap_links( $type, $max_entries, $current_page ) { 
  153.  
  154. $links = array(); 
  155. $post_type = $type; 
  156.  
  157. if ( ! $this->is_valid_post_type( $post_type ) ) { 
  158. return $links; 
  159.  
  160. $steps = min( 100, $max_entries ); 
  161. $offset = ( $current_page > 1 ) ? ( ( $current_page - 1 ) * $max_entries ) : 0; 
  162. $total = ( $offset + $max_entries ); 
  163.  
  164. $typecount = $this->get_post_type_count( $post_type ); 
  165.  
  166. if ( $total > $typecount ) { 
  167. $total = $typecount; 
  168.  
  169. if ( $current_page === 1 ) { 
  170. $links = array_merge( $links, $this->get_first_links( $post_type ) ); 
  171.  
  172. if ( $typecount === 0 ) { 
  173.  
  174. return $links; 
  175.  
  176. $options = $this->get_options(); 
  177.  
  178. $stacked_urls = array(); 
  179.  
  180. while ( $total > $offset ) { 
  181.  
  182. $posts = $this->get_posts( $post_type, $steps, $offset ); 
  183.  
  184. $offset += $steps; 
  185.  
  186. if ( empty( $posts ) ) { 
  187. continue; 
  188.  
  189. $posts_to_exclude = explode( ', ', $options['excluded-posts'] ); 
  190.  
  191. foreach ( $posts as $post ) { 
  192.  
  193. if ( WPSEO_Meta::get_value( 'meta-robots-noindex', $post->ID ) === '1' ) { 
  194. continue; 
  195.  
  196. if ( in_array( $post->ID, $posts_to_exclude ) ) { 
  197. continue; 
  198.  
  199. $url = $this->get_url( $post ); 
  200.  
  201. if ( ! isset( $url['loc'] ) ) { 
  202. continue; 
  203.  
  204. /** 
  205. * Filter URL entry before it gets added to the sitemap. 
  206. * @param array $url Array of URL parts. 
  207. * @param string $type URL type. 
  208. * @param object $user Data object for the URL. 
  209. */ 
  210. $url = apply_filters( 'wpseo_sitemap_entry', $url, 'post', $post ); 
  211.  
  212. if ( empty( $url ) ) { 
  213. continue; 
  214.  
  215. $stacked_urls[] = $url['loc']; 
  216.  
  217. if ( (int) $post->ID === $this->get_page_for_posts_id() || (int) $post->ID === $this->get_page_on_front_id() ) { 
  218.  
  219. array_unshift( $links, $url ); 
  220. continue; 
  221. $links[] = $url; 
  222.  
  223. unset( $post, $url ); 
  224.  
  225. return $links; 
  226.  
  227. /** 
  228. * Check for relevant post type before invalidation. 
  229. * @param int $post_id Post ID to possibly invalidate for. 
  230. */ 
  231. public function save_post( $post_id ) { 
  232.  
  233. if ( $this->is_valid_post_type( get_post_type( $post_id ) ) ) { 
  234. WPSEO_Sitemaps_Cache::invalidate_post( $post_id ); 
  235.  
  236. /** 
  237. * Check if post type should be present in sitemaps. 
  238. * @param string $post_type Post type string to check for. 
  239. * @return bool 
  240. */ 
  241. public function is_valid_post_type( $post_type ) { 
  242.  
  243. $options = $this->get_options(); 
  244.  
  245. if ( ! empty( $options[ "post_types-{$post_type}-not_in_sitemap" ] ) ) { 
  246. return false; 
  247.  
  248. if ( ! in_array( $post_type, get_post_types( array( 'public' => true ) ) ) ) { 
  249. return false; 
  250.  
  251. /** 
  252. * Filter decision if post type is excluded from the XML sitemap. 
  253. * @param bool $exclude Default false. 
  254. * @param string $post_type Post type name. 
  255. */ 
  256. if ( apply_filters( 'wpseo_sitemap_exclude_post_type', false, $post_type ) ) { 
  257. return false; 
  258.  
  259. return true; 
  260.  
  261. /** 
  262. * Get count of posts for post type. 
  263. * @param string $post_type Post type to retrieve count for. 
  264. * @return int 
  265. */ 
  266. protected function get_post_type_count( $post_type ) { 
  267.  
  268. global $wpdb; 
  269.  
  270. /** 
  271. * Filter JOIN query part for type count of post type. 
  272. * @param string $join SQL part, defaults to empty string. 
  273. * @param string $post_type Post type name. 
  274. */ 
  275. $join_filter = apply_filters( 'wpseo_typecount_join', '', $post_type ); 
  276.  
  277. /** 
  278. * Filter WHERE query part for type count of post type. 
  279. * @param string $where SQL part, defaults to empty string. 
  280. * @param string $post_type Post type name. 
  281. */ 
  282. $where_filter = apply_filters( 'wpseo_typecount_where', '', $post_type ); 
  283.  
  284. $where = $this->get_sql_where_clause( $post_type ); 
  285.  
  286. $sql = " 
  287. SELECT COUNT({$wpdb->posts}.ID) 
  288. FROM {$wpdb->posts} 
  289. {$join_filter} 
  290. {$where} 
  291. {$where_filter} 
  292. "; 
  293.  
  294. return (int) $wpdb->get_var( $sql ); 
  295.  
  296. /** 
  297. * Produces set of links to prepend at start of first sitemap page. 
  298. * @param string $post_type Post type to produce links for. 
  299. * @return array 
  300. */ 
  301. protected function get_first_links( $post_type ) { 
  302.  
  303. $links = array(); 
  304.  
  305. $needs_archive = true; 
  306.  
  307. if ( ! $this->get_page_on_front_id() && ( $post_type == 'post' || $post_type == 'page' ) ) { 
  308.  
  309. $links[] = array( 
  310. 'loc' => $this->get_home_url(),  
  311.  
  312. // Deprecated, kept for backwards data compat. R. 
  313. 'chf' => 'daily',  
  314. 'pri' => 1,  
  315. ); 
  316.  
  317. $needs_archive = false; 
  318. elseif ( $this->get_page_on_front_id() && $post_type === 'post' && $this->get_page_for_posts_id() ) { 
  319.  
  320. $page_for_posts_url = get_permalink( $this->get_page_for_posts_id() ); 
  321.  
  322. $links[] = array( 
  323. 'loc' => $page_for_posts_url,  
  324.  
  325. // Deprecated, kept for backwards data compat. R. 
  326. 'chf' => 'daily',  
  327. 'pri' => 1,  
  328. ); 
  329.  
  330. $needs_archive = false; 
  331.  
  332. if ( ! $needs_archive ) { 
  333. return $links; 
  334.  
  335. $archive_url = get_post_type_archive_link( $post_type ); 
  336.  
  337. /** 
  338. * Filter the URL Yoast SEO uses in the XML sitemap for this post type archive. 
  339. * @param string $archive_url The URL of this archive 
  340. * @param string $post_type The post type this archive is for. 
  341. */ 
  342. $archive_url = apply_filters( 'wpseo_sitemap_post_type_archive_link', $archive_url, $post_type ); 
  343.  
  344. if ( $archive_url ) { 
  345. /** 
  346. * Filter the priority of the URL Yoast SEO uses in the XML sitemap. 
  347. * @param float $priority The priority for this URL, ranging from 0 to 1 
  348. * @param string $post_type The post type this archive is for. 
  349. */ 
  350. $links[] = array( 
  351. 'loc' => $archive_url,  
  352. 'mod' => WPSEO_Sitemaps::get_last_modified_gmt( $post_type ),  
  353.  
  354. // Deprecated, kept for backwards data compat. R. 
  355. 'chf' => 'daily',  
  356. 'pri' => 1,  
  357. ); 
  358.  
  359. return $links; 
  360.  
  361. /** 
  362. * Retrieve set of posts with optimized query routine. 
  363. * @param string $post_type Post type to retrieve. 
  364. * @param int $count Count of posts to retrieve. 
  365. * @param int $offset Starting offset. 
  366. * @return object[] 
  367. */ 
  368. protected function get_posts( $post_type, $count, $offset ) { 
  369.  
  370. global $wpdb; 
  371.  
  372. static $filters = array(); 
  373.  
  374. if ( ! isset( $filters[ $post_type ] ) ) { 
  375. // Make sure you're wpdb->preparing everything you throw into this!! 
  376. $filters[ $post_type ] = array( 
  377. /** 
  378. * Filter JOIN query part for the post type. 
  379. * @param string $join SQL part, defaults to false. 
  380. * @param string $post_type Post type name. 
  381. */ 
  382. 'join' => apply_filters( 'wpseo_posts_join', false, $post_type ),  
  383.  
  384. /** 
  385. * Filter Where query part for the post type. 
  386. * @param string $where SQL part, defaults to false. 
  387. * @param string $post_type Post type name. 
  388. */ 
  389. 'where' => apply_filters( 'wpseo_posts_where', false, $post_type ),  
  390. ); 
  391.  
  392. $join_filter = $filters[ $post_type ]['join']; 
  393. $where_filter = $filters[ $post_type ]['where']; 
  394. $where = $this->get_sql_where_clause( $post_type ); 
  395.  
  396. // Optimized query per this thread: http://wordpress.org/support/topic/plugin-wordpress-seo-by-yoast-performance-suggestion. 
  397. // Also see http://explainextended.com/2009/10/23/mysql-order-by-limit-performance-late-row-lookups/. 
  398. $sql = " 
  399. SELECT l.ID, post_title, post_content, post_name, post_parent, post_author, post_modified_gmt, post_date, post_date_gmt 
  400. FROM ( 
  401. SELECT {$wpdb->posts}.ID 
  402. FROM {$wpdb->posts} 
  403. {$join_filter} 
  404. {$where} 
  405. {$where_filter} 
  406. ORDER BY {$wpdb->posts}.post_modified ASC LIMIT %d OFFSET %d 
  407. o JOIN {$wpdb->posts} l ON l.ID = o.ID 
  408. "; 
  409.  
  410. $posts = $wpdb->get_results( $wpdb->prepare( $sql, $count, $offset ) ); 
  411.  
  412. $post_ids = array(); 
  413.  
  414. foreach ( $posts as $post ) { 
  415. $post->post_type = $post_type; 
  416. $post->post_status = 'publish'; 
  417. $post->filter = 'sample'; 
  418. $post_ids[] = $post->ID; 
  419.  
  420. update_meta_cache( 'post', $post_ids ); 
  421.  
  422. return $posts; 
  423.  
  424. /** 
  425. * @param string $post_type Post type slug. 
  426. * @return string 
  427. */ 
  428. protected function get_sql_where_clause( $post_type ) { 
  429.  
  430. global $wpdb; 
  431.  
  432. $join = ''; 
  433. $status = "{$wpdb->posts}.post_status = 'publish'"; 
  434.  
  435. // Based on WP_Query->get_posts(). R. 
  436. if ( 'attachment' === $post_type ) { 
  437. $join = " LEFT JOIN {$wpdb->posts} AS p2 ON ({$wpdb->posts}.post_parent = p2.ID) "; 
  438. $status = "p2.post_status = 'publish'"; 
  439.  
  440. $where_clause = " 
  441. {$join} 
  442. WHERE {$status} 
  443. AND {$wpdb->posts}.post_type = '%s' 
  444. AND {$wpdb->posts}.post_password = '' 
  445. AND {$wpdb->posts}.post_date != '0000-00-00 00:00:00' 
  446. "; 
  447.  
  448. return $wpdb->prepare( $where_clause, $post_type ); 
  449.  
  450. /** 
  451. * Produce array of URL parts for given post object. 
  452. * @param object $post Post object to get URL parts for. 
  453. * @return array|bool 
  454. */ 
  455. protected function get_url( $post ) { 
  456.  
  457. $url = array(); 
  458.  
  459. /** 
  460. * Filter the URL Yoast SEO uses in the XML sitemap. 
  461. * Note that only absolute local URLs are allowed as the check after this removes external URLs. 
  462. * @param string $url URL to use in the XML sitemap 
  463. * @param object $post Post object for the URL. 
  464. */ 
  465. $url['loc'] = apply_filters( 'wpseo_xml_sitemap_post_url', get_permalink( $post ), $post ); 
  466.  
  467. /** 
  468. * Do not include external URLs. 
  469. * @see https://wordpress.org/plugins/page-links-to/ can rewrite permalinks to external URLs. 
  470. */ 
  471. if ( false === strpos( $url['loc'], $this->get_home_url() ) ) { 
  472. return false; 
  473.  
  474. $modified = max( $post->post_modified_gmt, $post->post_date_gmt ); 
  475.  
  476. if ( $modified !== '0000-00-00 00:00:00' ) { 
  477. $url['mod'] = $modified; 
  478.  
  479. $url['chf'] = 'daily'; // Deprecated, kept for backwards data compat. R. 
  480.  
  481. $canonical = WPSEO_Meta::get_value( 'canonical', $post->ID ); 
  482.  
  483. if ( $canonical !== '' && $canonical !== $url['loc'] ) { 
  484. /** 
  485. Let's assume that if a canonical is set for this page and it's different from 
  486. the URL of this post, that page is either already in the XML sitemap OR is on 
  487. an external site, either way, we shouldn't include it here. 
  488. */ 
  489. return false; 
  490. unset( $canonical ); 
  491.  
  492. $options = $this->get_options(); 
  493. if ( $options['trailingslash'] === true && $post->post_type !== 'post' ) { 
  494. $url['loc'] = trailingslashit( $url['loc'] ); 
  495.  
  496. $url['pri'] = 1; // Deprecated, kept for backwards data compat. R. 
  497. $url['images'] = $this->get_image_parser()->get_images( $post ); 
  498.  
  499. return $url; 
  500.  
  501. /** 
  502. * Calculate the priority of the post. 
  503. * @deprecated 3.5 Priority data dropped from sitemaps. 
  504. * @param WP_Post $post Post object. 
  505. * @return float|mixed 
  506. */ 
  507. private function calculate_priority( $post ) { 
  508. _deprecated_function( __METHOD__, 'WPSEO 3.5' ); 
  509.  
  510. $return = 0.6; 
  511. if ( $post->post_parent == 0 && $post->post_type == 'page' ) { 
  512. $return = 0.8; 
  513.  
  514. if ( $post->ID === $this->get_page_on_front_id() || $post->ID === $this->get_page_for_posts_id() ) { 
  515. $return = 1.0; 
  516.  
  517. /** 
  518. * Filter the priority of the URL Yoast SEO uses in the XML sitemap. 
  519. * @param float $priority The priority for this URL, ranging from 0 to 1 
  520. * @param string $post_type The post type this archive is for. 
  521. * @param object $post The post object. 
  522. */ 
  523. $return = apply_filters( 'wpseo_xml_sitemap_post_priority', $return, $post->post_type, $post ); 
  524.  
  525. return $return;