<?php
/**
* Default sitemap builder
*
* @package Sitemap
* @author Arne Brachhold
* @since 4.0
*/
/**
* Class
*/
class GoogleSitemapGeneratorStandardBuilder {
private $linkPerPage = 1000;
private $maxLinksPerPage = 50000;
/**
* Creates a new GoogleSitemapGeneratorStandardBuilder instance
*/
public function __construct() {
add_action( 'sm_build_index', array( $this, 'index' ), 10, 1 );
add_action( 'sm_build_content', array( $this, 'content' ), 10, 3 );
add_filter( 'sm_sitemap_for_post', array( $this, 'get_sitemap_url_for_post' ), 10, 3 );
}
/**
* Generates the content of the requested sitemap
*
* @param GoogleSitemapGenerator $gsg instance of sitemap generator.
* @param String $type the type of the sitemap.
* @param String $params Parameters for the sitemap.
*/
public function content( $gsg, $type, $params ) {
$params = strval($params);
if (strpos($params, '/') !== false){
$newType = explode('/', $params);
$params = end($newType);
}
switch ( $type ) {
case 'pt':
$this->build_posts( $gsg, $params );
break;
case 'archives':
$this->build_archives( $gsg );
break;
case 'authors':
$this->build_authors( $gsg );
break;
case 'tax':
$this->build_taxonomies( $gsg, $params );
break;
case 'producttags':
$this->build_product_tags( $gsg, $params );
break;
case 'productcat':
$this->build_product_categories( $gsg, $params );
break;
case 'externals':
$this->build_externals( $gsg );
break;
case 'misc':
default:
$this->build_misc( $gsg );
break;
}
}
/**
* Generates the content for the post sitemap
*
* @param GoogleSitemapGenerator $gsg instance of sitemap generator.
* @param string $params string.
*/
public function build_posts( $gsg, $params ) {
$pts = strrpos( $params, '-' );
if ( ! $pts ) {
return;
}
$pts = strrpos( $params, '-', $pts - strlen( $params ) - 1 );
$param_length = count( explode( '-', $params ) );
$post_type = '';
$post_type = substr( $params, 0, $pts );
$type = explode( '-', $post_type );
if ( $param_length > 4 ) {
$new = array_slice( $type,0,count($type) -1 );
$post_type = implode( "-", $new );
} else {
$post_type = $type[0];
}
$limit = $type[count($type)-1];
$limits = substr( $limit, 1 );
$links_per_page = $gsg->get_entries_per_page();
if ( gettype( $links_per_page ) !== 'integer' ) {
$links_per_page = (int) 1000;
}
$limit = ( (int) $limits ) * $links_per_page;
if ( ! $post_type || ! in_array( $post_type, $gsg->get_active_post_types(), true ) ) {
return;
}
$params = substr( $params, $pts + 1 );
/**
* Global variable for database.
*
* @var $wpdb wpdb
*/
global $wpdb;
if ( preg_match( '/^([0-9]{4})\-([0-9]{2})$/', $params, $matches ) ) {
$year = $matches[1];
$month = $matches[2];
// Excluded posts by ID.
$excluded_post_ids = $gsg->get_excluded_post_ids( $gsg );
$not_allowed_slugs = $gsg->robots_disallowed();
$excluded_post_ids = array_unique( array_merge( $excluded_post_ids, $not_allowed_slugs ), SORT_REGULAR );
$gsg->set_option( 'b_exclude', $excluded_post_ids );
$gsg->save_options();
$ex_post_s_q_l = '';
if ( count( $excluded_post_ids ) > 0 ) {
$ex_post_s_q_l = 'AND p.ID NOT IN (' . implode( ',', $excluded_post_ids ) . ')';
}
// Excluded categories by taxonomy ID.
$excluded_category_i_d_s = $gsg->get_excluded_category_i_ds( $gsg );
$ex_cat_s_q_l = '';
if ( count( $excluded_category_i_d_s ) > 0 ) {
$ex_cat_s_q_l = "AND ( p.ID NOT IN ( SELECT object_id FROM {$wpdb->term_relationships} WHERE term_taxonomy_id IN ( SELECT term_taxonomy_id FROM {$wpdb->term_taxonomy} WHERE term_id IN ( " . implode( ',', $excluded_category_i_d_s ) . '))))';
}
// Statement to query the actual posts for this post type.
$qs = "
SELECT
p.ID,
p.post_author,
p.post_status,
p.post_name,
p.post_parent,
p.post_type,
p.post_date,
p.post_date_gmt,
p.post_modified,
p.post_modified_gmt,
p.comment_count
FROM
{$wpdb->posts} p
WHERE
p.post_password = ''
AND p.post_type = '%s'
AND p.post_status = 'publish'
{$ex_post_s_q_l}
{$ex_cat_s_q_l}
ORDER BY
p.post_date_gmt ASC
LIMIT
%d, %d
";
// Query for counting all relevant posts for this post type.
$qsc = "
SELECT
COUNT(*)
FROM
{$wpdb->posts} p
WHERE
p.post_password = ''
AND p.post_type = '%s'
AND p.post_status = 'publish'
{$ex_post_s_q_l}
{$ex_cat_s_q_l}
";
// Calculate the offset based on the limit and links_per_page
$offset = max( 0, ( $limit - $links_per_page ) );
// phpcs:disable
$q = $wpdb->prepare( $qs, $post_type, $offset, $links_per_page );
// phpcs:enable
$posts = $wpdb->get_results( $q ); // phpcs:ignore
$post_count = count( $posts );
if ( ( $post_count ) > 0 ) {
/**
* Description for priority provider
*
* @var $priority_provider GoogleSitemapGeneratorPrioProviderBase
*/
$priority_provider = null;
if ( $gsg->get_option( 'b_prio_provider' ) !== '' ) {
// Number of comments for all posts.
$cache_key = __CLASS__ . '::commentCount';
$comment_count = wp_cache_get( $cache_key, 'sitemap' );
if ( false === $comment_count ) {
$comment_count = $wpdb->get_var( "SELECT COUNT(*) as `comment_count` FROM {$wpdb->comments} WHERE `comment_approved`='1'" ); // db call ok.
wp_cache_set( $cache_key, $comment_count, 'sitemap', 20 );
}
// Number of all posts matching our criteria.
$cache_key = __CLASS__ . "::totalPostCount::$post_type";
$total_post_count = wp_cache_get( $cache_key, 'sitemap' );
if ( false === $total_post_count ) {
// phpcs:disable
$total_post_count = $wpdb->get_var(
$wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->posts} p WHERE p.post_password = '' AND p.post_type = '%s' AND p.post_status = 'publish' " . $ex_post_s_q_l . " " . $ex_cat_s_q_l . " ", $post_type ) // phpcs:ignore
); // db call ok.
// phpcs:enable
wp_cache_add( $cache_key, $total_post_count, 'sitemap', 20 );
}
// Initialize a new priority provider.
$provider_class = $gsg->get_option( 'b_prio_provider' );
$priority_provider = new $provider_class( $comment_count, $total_post_count );
}
// Default priorities.
$default_priority_for_posts = $gsg->get_option( 'pr_posts' );
$default_priority_for_pages = $gsg->get_option( 'pr_pages' );
// Minimum priority.
$minimum_priority = $gsg->get_option( 'pr_posts_min' );
// Change frequencies.
$change_frequency_for_posts = $gsg->get_option( 'cf_posts' );
$change_frequency_for_pages = $gsg->get_option( 'cf_pages' );
// Page as home handling.
$home_pid = 0;
$home = get_home_url();
if ( 'page' === get_option( 'show_on_front' ) && get_option( 'page_on_front' ) ) {
$page_on_front = get_option( 'page_on_front' );
$p = get_post( $page_on_front );
if ( $p ) {
$home_pid = $p->ID;
}
}
$siteLanguages = [];
$defaultLanguageCode = '';
if (function_exists('icl_get_languages')) {
if (function_exists('icl_get_default_language')) $defaultLanguageCode = icl_get_default_language();
$languages = icl_get_languages('skip_missing=0');
if($languages){
foreach ($languages as $language) {
if($defaultLanguageCode !== $language['language_code']) $siteLanguages[] = $language['language_code'];
}
}
} else if (function_exists('pll_the_languages')) {
if (function_exists('pll_default_language')) $defaultLanguageCode = pll_default_language();
$languages = pll_the_languages(array('raw' => 1));
if ($languages) {
foreach ($languages as $language) {
if($defaultLanguageCode !== $language['slug']) $siteLanguages[] = $language['slug'];
}
}
}
foreach ( $posts as $post ) {
$permalink = get_permalink( $post );
$permalink = apply_filters( 'sm_xml_sitemap_post_url', $permalink, $post );
if(count($siteLanguages) > 0){
$structurekArr = explode('/', get_option('permalink_structure'));
$postLinkArr = explode('/', $permalink);
$index = null;
if(is_array($structurekArr) && is_array($postLinkArr)){
foreach ($siteLanguages as $lang){
if (in_array($lang, $postLinkArr)) {
$index = array_search($lang, $postLinkArr);
}
}
}
if ($index !== null) {
if ($postLinkArr[$index] !== $defaultLanguageCode) {
$custom_post_type_name = get_post_type($post);
if (!in_array($custom_post_type_name, $postLinkArr)) {
$key = array_search('%postname%', $structurekArr);
if ($structurekArr[$key] === '%postname%') {
$postLinkArr[$index + $key] = $post->post_name;
}
}
$permalink = implode('/', $postLinkArr);
}
}
}
// Exclude the home page and placeholder items by some plugins. Also include only internal links.
if (
( ! empty( $permalink ) )
&& $permalink !== $home
&& $post->ID !== $home_pid
&& strpos( $permalink, $home ) !== false
) {
// Default Priority if auto calc is disabled.
$priority = ( 'page' === $post_type ? $default_priority_for_pages : $default_priority_for_posts );
// If priority calc. is enabled, calculate (but only for posts, not pages)!
if ( null !== $priority_provider && 'post' === $post_type ) {
$priority = $priority_provider->get_post_priority( $post->ID, $post->comment_count, $post );
}
// Ensure the minimum priority.
if ( 'post' === $post_type && $minimum_priority > 0 && $priority < $minimum_priority ) {
$priority = $minimum_priority;
}
// Add the URL to the sitemap.
$gsg->add_url(
$permalink,
$gsg->get_timestamp_from_my_sql( $post->post_modified_gmt && '0000-00-00 00:00:00' !== $post->post_modified_gmt ? $post->post_modified_gmt : $post->post_date_gmt ),
( 'page' === $post_type ? $change_frequency_for_pages : $change_frequency_for_posts ),
$priority,
$post->ID
);
}
// Why not use clean_post_cache? Because some plugin will go crazy then (lots of database queries).
// The post cache was not populated in a clean way, so we also won't delete it using the API.
// wp_cache_delete( $post->ID, 'posts' );.
unset( $post );
}
unset( $posts_custom );
}
}
}
/**
* Generates the content for the archives sitemap
*
* @param GoogleSitemapGenerator $gsg object of google sitemap.
*/
public function build_archives( $gsg ) {
/**
* Super global variable for database.
*
* @var $wpdb wpdb
*/
global $wpdb;
$now = current_time( 'mysql', true );
$archives = $wpdb->get_results(
// phpcs:disable
$wpdb->prepare(
"SELECT DISTINCT
YEAR(post_date_gmt) AS `year`,
MONTH(post_date_gmt) AS `month`,
MAX(post_date_gmt) AS last_mod,
count(ID) AS posts
FROM
$wpdb->posts
WHERE
post_date_gmt < '%s'
AND post_status = 'publish'
AND post_type = 'post'
GROUP BY
YEAR(post_date_gmt),
MONTH(post_date_gmt)
ORDER BY
post_date_gmt DESC",
$now
)
// phpcs:enable
); // db call ok; no-cache ok.
if ( $archives ) {
foreach ( $archives as $archive ) {
$url = get_month_link( $archive->year, $archive->month );
// Archive is the current one.
if ( gmdate( 'n' ) === $archive->month && gmdate( 'Y' ) === $archive->year ) {
$change_freq = $gsg->get_option( 'cf_arch_curr' );
} else { // Archive is older.
$change_freq = $gsg->get_option( 'cf_arch_old' );
}
$gsg->add_url( $url, $gsg->get_timestamp_from_my_sql( $archive->last_mod ), $change_freq, $gsg->get_option( 'pr_arch' ) );
}
}
$post_type_customs = get_post_types( array( 'public' => 1 ) );
$post_type_customs = array_diff( $post_type_customs, array( 'page', 'attachment', 'product', 'post' ) );
foreach ( $post_type_customs as $post_type_custom ) {
$latest = new WP_Query(
array(
'post_type' => $post_type_custom,
'post_status' => 'publish',
'posts_per_page' => 1,
'orderby' => 'modified',
'order' => 'DESC',
)
);
if ( $latest->have_posts() ) {
$modified_date = $latest->posts[0]->post_modified;
if ( gmdate( 'n', strtotime( $modified_date ) ) === gmdate( 'n' ) && gmdate( 'Y', strtotime( $modified_date ) ) === gmdate( 'Y' ) ) {
$change_freq = $gsg->get_option( 'cf_arch_curr' );
} else { // Archive is older.
$change_freq = $gsg->get_option( 'cf_arch_old' );
}
$gsg->add_url( get_post_type_archive_link( $post_type_custom ), $gsg->get_timestamp_from_my_sql( $modified_date ), $change_freq, $gsg->get_option( 'pr_arch' ), 0, array(), array(), '' );
}
}
}
/**
* Generates the misc sitemap
*
* @param GoogleSitemapGenerator $gsg instence of sitemap generator class.
*/
public function build_misc( $gsg ) {
$lm = get_lastpostmodified( 'gmt' );
if ( $gsg->get_option( 'in_home' ) ) {
$home = get_bloginfo( 'url' );
// Add the home page (WITH a slash!).
if ( $gsg->get_option( 'in_home' ) ) {
if ( 'page' === get_option( 'show_on_front' ) && get_option( 'page_on_front' ) ) {
$page_on_front = get_option( 'page_on_front' );
$p = get_post( $page_on_front );
if ( $p ) {
$gsg->add_url(
trailingslashit( $home ),
$gsg->get_timestamp_from_my_sql( ( $p->post_modified_gmt && '0000-00-00 00:00:00' !== $p->post_modified_gmt ? $p->post_modified_gmt : $p->post_date_gmt ) ),
$gsg->get_option( 'cf_home' ),
$gsg->get_option( 'pr_home' )
);
}
} else {
$gsg->add_url(
trailingslashit( $home ),
( $lm ? $gsg->get_timestamp_from_my_sql( $lm ) : time() ),
$gsg->get_option( 'cf_home' ),
$gsg->get_option( 'pr_home' )
);
}
}
}
if ( $gsg->is_xsl_enabled() && true === $gsg->get_option( 'b_html' ) ) {
if(is_multisite()) {
if(isset(get_blog_option( get_current_blog_id(), 'sm_options' )['sm_b_sitemap_name'])) {
$sm_sitemap_name = get_blog_option( get_current_blog_id(), 'sm_options' )['sm_b_sitemap_name'];
}
} else if(isset(get_option('sm_options')['sm_b_sitemap_name'])) $sm_sitemap_name = get_option('sm_options')['sm_b_sitemap_name'];
if(!isset($sm_sitemap_name)) $sm_sitemap_name = 'sitemap';
$gsg->add_url(
str_replace('.html', $sm_sitemap_name . '.html', $gsg->get_xml_url( 'main', '', array( 'html' => true ) ) ),
( $lm ? $gsg->get_timestamp_from_my_sql( $lm ) : time() )
);
}
do_action( 'sm_buildmap' );
}
/**
* Generates the author sitemap
*
* @param GoogleSitemapGenerator $gsg instence of sitemap generator class.
*/
public function build_authors( $gsg ) {
/**
* Use the wpdb global variable
*
* @var $wpdb wpdb
* */
global $wpdb;
// Unfortunately there is no API function to get all authors, so we have to do it the dirty way...
// We retrieve only users with published and not password protected enabled post types.
$enabled_post_types = null;
$enabled_post_types = $gsg->get_active_post_types();
// Ensure we count at least the posts...
$enabled_post_types_count = count( $enabled_post_types );
if ( 0 === $enabled_post_types_count ) {
$enabled_post_types[] = 'post';
}
$sql = "SELECT DISTINCT
u.ID,
u.user_nicename,
MAX(p.post_modified_gmt) AS last_post
FROM
{$wpdb->users} u,
{$wpdb->posts} p
WHERE
p.post_author = u.ID
AND p.post_status = 'publish'
AND p.post_type IN(" . implode( ', ', array_fill( 0, count( $enabled_post_types ), '%s' ) ) . ")
AND p.post_password = ''
GROUP BY
u.ID,
u.user_nicename";
$query = call_user_func_array( array( $wpdb, 'prepare' ), array_merge( array( $sql ), $enabled_post_types ) );
$authors = $wpdb->get_results( $query ); // phpcs:ignore
if ( $authors && is_array( $authors ) ) {
$authors = $this->exclude_authors( $authors );
if ( ! empty( $authors ) ) {
foreach ( $authors as $author ) {
$url = get_author_posts_url( $author->ID, $author->user_nicename );
$gsg->add_url(
$url,
$gsg->get_timestamp_from_my_sql( $author->last_post ),
$gsg->get_option( 'cf_auth' ),
$gsg->get_option( 'pr_auth' )
);
}
}
}
}
/**
* Wrap legacy filter to deduplicate calls.
*
* @param array $users Array of user objects to filter.
*
* @return array
*/
protected function exclude_authors( $authors ) {
/**
* Filter the authors, included in XML sitemap.
*
* @param array $authors Array of user objects to filter.
*/
return apply_filters( 'sm_sitemap_exclude_author', $authors );
}
/**
* Filters the terms query to only include published posts
*
* @param string[] $selects Array of string.
* @return string[]
*/
public function filter_terms_query( $selects ) {
/**
* Global variable in functional scope for database
*
* @var wpdb $wpdb Global variable for wpdb
*/
global $wpdb;
$selects[] = "
( /* ADDED BY XML SITEMAPS */
SELECT
UNIX_TIMESTAMP(MAX(p.post_date_gmt)) as _mod_date
FROM
{$wpdb->posts} p,
{$wpdb->term_relationships} r
WHERE
p.ID = r.object_id
AND p.post_status = 'publish'
AND p.post_password = ''
AND r.term_taxonomy_id = tt.term_taxonomy_id
) as _mod_date
/* END ADDED BY XML SITEMAPS */
";
return $selects;
}
/**
* Generates the taxonomies sitemap
*
* @param GoogleSitemapGenerator $gsg Instance of sitemap generator.
* @param string $taxonomy The Taxonomy.
*/
public function build_taxonomies( $gsg, $taxonomy ) {
$offset = $taxonomy;
$links_per_page = $gsg->get_entries_per_page();
if ( gettype( $links_per_page ) !== 'integer' ) {
$links_per_page = (int)1000;
}
if ( strpos( $taxonomy, '-' ) !== false ) {
$offset = substr( $taxonomy, strrpos( $taxonomy, '-' ) + 1 );
$taxonomy = str_replace( '-' . $offset, '', $taxonomy );
} else {
$offset = 1;
}
$temp_offset = $offset;
$offset = intval( $offset );
if ( 0 === $offset ) {
$taxonomy = $taxonomy . '-' . $temp_offset;
$links_per_page = $this->linkPerPage;
} else {
$offset = ( --$offset ) * $links_per_page;
}
$enabled_taxonomies = $this->get_enabled_taxonomies( $gsg );
if ( in_array( $taxonomy, $enabled_taxonomies, true ) ) {
$excludes = array();
$excl_cats = $gsg->get_option( 'b_exclude_cats' ); // Excluded cats.
if ( $excl_cats ) {
$excludes = $excl_cats;
}
add_filter( 'get_terms_fields', array( $this, 'filter_terms_query' ), 20, 2 );
/*
$terms = get_terms(
$taxonomy,
array(
'number' => $links_per_page,
'offset' => $offset,
'hide_empty' => true,
'hierarchical' => false,
'exclude' => $excludes,
)
);
*/
$queryArr = [
'taxonomy' => $taxonomy,
'number' => $links_per_page,
'offset' => $offset,
'exclude' => $excludes,
];
$queryArr['hide_empty'] = apply_filters( 'sm_sitemap_taxonomy_hide_empty', true );
if (preg_match('/(post_tag|category)/', $taxonomy)) {
$queryArr['hierarchical'] = false;
}
$terms = array_values(
array_unique(
array_filter(
$this->get_terms($queryArr),
function ($term) use ($taxonomy) {
return $term->taxonomy === $taxonomy;
}
),
SORT_REGULAR
)
);
remove_filter( 'get_terms_fields', array( $this, 'filter_terms_query' ), 20, 2 );
//$terms = array_values(array_unique($terms, SORT_REGULAR));
/**
* Filter: 'sm_exclude_from_sitemap_by_term_ids' - Allow excluding terms by ID.
*
* @param array $terms_to_exclude The terms to exclude.
*/
$terms_to_exclude = apply_filters( 'sm_exclude_from_sitemap_by_term_ids', [] );
$step = 1;
$size_of_terms = count( $terms );
for ( $tax_count = 0; $tax_count < $size_of_terms; $tax_count++ ) {
$term = $terms[ $tax_count ];
if ( in_array( $term->term_id, $terms_to_exclude ) ) {
$step++;
continue;
}
switch ( $term->taxonomy ) {
case 'category':
$gsg->add_url( get_term_link( $term, $step ), $this->getTaxonomyUpdatedDate($term->term_id) ?: 0, $gsg->get_option( 'cf_cats' ), $gsg->get_option( 'pr_cats' ) );
break;
case 'product_cat':
$gsg->add_url( get_term_link( $term, $step ), $term->_mod_date, $gsg->get_option( 'cf_product_cat' ), $gsg->get_option( 'pr_product_cat' ) );
break;
case 'post_tag':
$gsg->add_url( get_term_link( $term, $step ), $this->getTaxonomyUpdatedDate($term->term_id) ?: 0, $gsg->get_option( 'cf_tags' ), $gsg->get_option( 'pr_tags' ) );
break;
default:
$gsg->add_url( get_term_link( $term, $step ), $this->getTaxonomyUpdatedDate($term->term_id) ?: 0, $gsg->get_option( 'cf_' . $term->taxonomy ), $gsg->get_option( 'pr_' . $term->taxonomy ) );
break;
}
$step++;
}
}
}
/*
get last updated date of taxonomy post
returns timestamp (int)
*/
private function getTaxonomyUpdatedDate($term_id){
global $wpdb;
$query = $wpdb->prepare("
SELECT p.*
FROM {$wpdb->posts} p
INNER JOIN {$wpdb->term_relationships} tr ON p.ID = tr.object_id
INNER JOIN {$wpdb->term_taxonomy} tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
WHERE tt.term_id = %d
ORDER BY p.post_date DESC
LIMIT 1
", $term_id);
$post = $wpdb->get_row($query);
if ($post) {
return strtotime($post->post_date);
}
}
/**
* Returns the enabled taxonomies. Only taxonomies with posts are returned.
*
* @param GoogleSitemapGenerator $gsg Google sitemap generator's instance.
* @return array
*/
public function get_enabled_taxonomies( GoogleSitemapGenerator $gsg ) {
$enabled_taxonomies = $gsg->get_option( 'in_tax' );
if ( $gsg->get_option( 'in_tags' ) ) {
$enabled_taxonomies[] = 'post_tag';
}
if ( $gsg->get_option( 'in_cats' ) ) {
$enabled_taxonomies[] = 'category';
}
return $enabled_taxonomies;
}
/**
* Returns the enabled Product tags. Only Product Tags with posts are returned.
*
* @param GoogleSitemapGenerator $gsg Instance of sitemap generator.
* @param int $offset Offset.
* @return void
*/
public function build_product_tags( GoogleSitemapGenerator $gsg, $offset ) {
$links_per_page = $gsg->get_entries_per_page();
if ( gettype( $links_per_page ) !== 'integer' ) {
$links_per_page = (int) 1000;
}
$offset = (intval(--$offset)) * $links_per_page;
add_filter( 'get_terms_fields', array( $this, 'filter_terms_query' ), 20, 2 );
$terms = get_terms(
'product_tag',
array(
'number' => $links_per_page,
'offset' => $offset,
)
);
remove_filter( 'get_terms_fields', array( $this, 'filter_terms_query' ), 20, 2 );
$term_array = array();
if ( ! empty( $terms ) && ! is_wp_error( $terms ) ) {
foreach ( $terms as $term ) {
$term_array[] = $term->name;
$url = get_term_link( $term );
//$gsg->add_url( $url, $term->_mod_date, $gsg->get_option( 'cf_tags' ), $gsg->get_option( 'pr_tags' ), $term->ID, array(), array(), '' );
$gsg->add_url( $url, $this->getProductUpdatedDate($term->term_id, 'product_tag'), $gsg->get_option( 'cf_tags' ), $gsg->get_option( 'pr_tags' ), $term->ID, array(), array(), '' );
}
}
}
/**
* Returns the enabled Product Categories. Only Product Categories with posts are returned.
*
* @param GoogleSitemapGenerator $gsg Instance of sitemap generator.
* @param int $offset Offset.
* @return void
*/
public function build_product_categories( GoogleSitemapGenerator $gsg, $offset ) {
$links_per_page = $gsg->get_entries_per_page();
if ( gettype( $links_per_page ) !== 'integer' ) {
//$links_per_page = (int) 1000;
$links_per_page = (int)$links_per_page;
}
$offset = (intval(--$offset)) * $links_per_page;
$excludes = array();
$excl_cats = $gsg->get_option( 'b_exclude_cats' ); // Excluded cats.
if ( $excl_cats ) {
$excludes = $excl_cats;
}
add_filter( 'get_terms_fields', array( $this, 'filter_terms_query' ), 20, 2 );
$category = get_terms(
'product_cat',
array(
'number' => $links_per_page,
'offset' => $offset,
'exclude' => $excludes,
)
);
remove_filter( 'get_terms_fields', array( $this, 'filter_terms_query' ), 20, 2 );
$cat_array = array();
if ( ! empty( $category ) && ! is_wp_error( $category ) ) {
$step = 1;
foreach ( $category as $cat ) {
$cat_array[] = $cat->name;
if ( $cat && wp_count_terms( $cat->name, array( 'hide_empty' => true ) ) > 0 ) {
$step++;
$url = get_term_link( $cat );
$gsg->add_url( $url, $this->getProductUpdatedDate($cat->term_id, 'product_cat'), $gsg->get_option( 'cf_product_cat' ), $gsg->get_option( 'pr_product_cat' ), $cat->ID, array(), array(), '' );
}
}
}
}
/* Get last product updated date by tag ID */
private function getProductUpdatedDate($term_id, $taxonomy){
$args = array(
'post_type' => 'product',
'posts_per_page' => 1,
'tax_query' => array(
array(
'taxonomy' => $taxonomy,
'field' => 'id',
'terms' => $term_id,
),
),
'orderby' => 'modified',
'order' => 'DESC',
);
$products = new WC_Product_Query($args);
$product_results = $products->get_products();
if ($product_results) {
$product = array_shift($product_results);
$updated_date = strtotime($product->get_date_modified()->date('Y-m-d H:i:s'));
return $updated_date;
}
return false;
}
/**
* Generates the external sitemap
*
* @param GoogleSitemapGenerator $gsg Instance of sitemap generator.
*/
public function build_externals( $gsg ) {
$pages = $gsg->get_pages();
if ( $pages && is_array( $pages ) && count( $pages ) > 0 ) {
foreach ( $pages as $page ) {
// Disabled phpcs for backward compatibility .
// phpcs:disable
$url = ! empty( $page->get_url() ) ? $page->get_url() : $page->url;
$change_freq = ! empty( $page->get_change_freq() ) ? $page->get_change_freq() : $page->change_freq;
$priority = ! empty( $page->get_priority() ) ? $page->get_priority() : $page->priority;
$last_mod = ! empty( $page->get_last_mod() ) ? $page->get_last_mod() : $page->last_mod;
// phpcs:enable
/**
* Description for $page variable.
*
* @var $page GoogleSitemapGeneratorPage
*/
$gsg->add_url( $url, $last_mod, $change_freq, $priority );
}
}
}
/**
* Generates the sitemap index
*
* @param GoogleSitemapGenerator $gsg Instance of sitemap generator.
*/
public function index( $gsg ) {
/**
* Global variable for database.
*
* @var $wpdb wpdb
*/
global $wpdb;
$blog_update = strtotime( get_lastpostmodified( 'gmt' ) );
$links_per_page = $gsg->get_entries_per_page();
if ( 0 === $links_per_page || is_nan( $links_per_page ) ) {
$links_per_page = $this->linkPerPage;
$gsg->set_option( 'links_page', $this->linkPerPage );
}
else if ($links_per_page > $this->maxLinksPerPage) $links_per_page = $this->maxLinksPerPage;
$gsg->add_sitemap( 'misc', null, $blog_update );
/**
* Filter: 'sm_sitemap_exclude_taxonomy' - Allow extending and modifying the taxonomies to exclude.
*
* @param array $taxonomies_to_exclude The taxonomies to exclude.
*/
$taxonomies_to_exclude = [];
$default_taxonomies_to_exclude = [ 'product_tag', 'product_cat' ];
$taxonomies_to_exclude = apply_filters( 'sm_sitemap_exclude_taxonomy', $taxonomies_to_exclude );
if ( ! is_array( $taxonomies_to_exclude ) || empty( $taxonomies_to_exclude ) ) {
$taxonomies_to_exclude = $default_taxonomies_to_exclude;
} else {
$taxonomies_to_exclude = array_merge( $taxonomies_to_exclude, $default_taxonomies_to_exclude );
}
$enabled_taxonomies = $this->get_enabled_taxonomies( $gsg );
$excl_cats = $gsg->get_option( 'b_exclude_cats' );
$excludes = $excl_cats ? $excl_cats : array();
$terms_by_taxonomy = array();
foreach ( $enabled_taxonomies as $taxonomy ) {
if ( ! in_array( $taxonomy, $taxonomies_to_exclude, true ) ) {
$terms_args = [
'taxonomy' => $taxonomy,
'exclude' => $excludes
];
$terms_args['hide_empty'] = apply_filters( 'sm_sitemap_taxonomy_hide_empty', true );
$terms = $this->get_terms( $terms_args );
$terms_by_taxonomy[ $taxonomy ] = $terms;
}
}
foreach ( $terms_by_taxonomy as $taxonomy => $terms ) {
$step = 1;
$i = 0;
foreach ( $terms as $term ) {
if ( 0 === ( $i % $links_per_page ) && '' !== $term->taxonomy && taxonomy_exists( $term->taxonomy ) ) {
$gsg->add_sitemap( $term->taxonomy,'-sitemap' . ($step === 1? '' : $step), $blog_update );
$step++;
}
$i++;
}
}
// If Product Tags is enabled from sitemap settings.
if ( true === $gsg->get_option( 'product_tags' ) ) {
$product_tags = get_terms( 'product_tag' );
if ( ! empty( $product_tags ) && ! is_wp_error( $product_tags ) ) {
$step = 1;
$product_tags_size_of = count( $product_tags );
for ( $product_count = 0; $product_count < $product_tags_size_of; $product_count++ ) {
if ( 0 === ( $product_count % $links_per_page ) ) {
//$gsg->add_sitemap( 'producttags', $step, $blog_update );
$gsg->add_sitemap( 'producttags', '-sitemap' . ($step === 1? '' : $step), $blog_update );
$step = ++$step;
}
}
}
}
// If Product category is enabled from sitemap settings.
if ( true === $gsg->get_option( 'in_product_cat' ) ) {
$excludes = array();
$excl_cats = $gsg->get_option( 'b_exclude_cats' ); // Excluded cats.
if ( $excl_cats ) {
$excludes = $excl_cats;
}
$product_cat = get_terms( 'product_cat', array( 'exclude' => $excludes ) );
if ( ! empty( $product_cat ) && ! is_wp_error( $product_cat ) ) {
$step = 1;
$product_cat_count = count( $product_cat );
for ( $product_count = 0; $product_count < $product_cat_count; $product_count++ ) {
if ( 0 === ( $product_count % $links_per_page ) ) {
//$gsg->add_sitemap( 'productcat', $step, $blog_update );
$gsg->add_sitemap( 'productcat', '-sitemap' . ($step === 1? '' : $step), $blog_update );
$step = ++$step;
}
}
}
}
$pages = (array)$gsg->get_pages();
if ( count( $pages ) > 0 ) {
foreach ( $pages as $page ) {
$url = ! empty( $page->get_url() ) ? $page->get_url() : ( property_exists( $page, '_url' ) ? $page->_url : '' );
if ( $page instanceof GoogleSitemapGeneratorPage && $url ) {
$gsg->add_sitemap( 'externals-sitemap', null, $blog_update );
break;
}
}
}
$enabled_post_types = $gsg->get_active_post_types();
//checking for products enabled
if($gsg->get_option( 'in_product_assortment' ) !== null && $gsg->get_option( 'in_product_assortment' ) !== true){
$enabled_post_types = array_filter($enabled_post_types, function($value) {
return $value !== 'product';
});
}
$has_enabled_post_types_posts = false;
$has_posts = false;
if ( count( $enabled_post_types ) > 0 ) {
$excluded_post_ids = $gsg->get_excluded_post_ids( $gsg );
$not_allowed_slugs = $gsg->robots_disallowed();
$excluded_post_ids = array_unique( array_merge( $excluded_post_ids, $not_allowed_slugs ), SORT_REGULAR );
$gsg->set_option( 'b_exclude', $excluded_post_ids );
$gsg->save_options();
$ex_post_s_q_l = '';
$excluded_post_ids_count = count( $excluded_post_ids );
if ( $excluded_post_ids_count > 0 ) {
$ex_post_s_q_l = 'AND p.ID NOT IN (' . implode( ',', $excluded_post_ids ) . ')';
}
$excluded_category_i_d_s = $gsg->get_excluded_category_i_ds( $gsg );
$ex_cat_s_q_l = '';
$excluded_category_i_d_s_count = count( $excluded_category_i_d_s );
if ( $excluded_category_i_d_s_count > 0 ) {
$ex_cat_s_q_l = "AND ( p.ID NOT IN ( SELECT object_id FROM {$wpdb->term_relationships} WHERE term_taxonomy_id IN (" . implode( ',', $excluded_category_i_d_s ) . ')))';
}
foreach ( $enabled_post_types as $post_type_custom ) {
// phpcs:disable
$prp = $wpdb->prepare(
"SELECT
COUNT(p.ID) AS `numposts`,
MAX(p.post_modified_gmt) as `last_mod`
FROM
{$wpdb->posts} p
WHERE
p.post_password = ''
AND p.post_type = '%s'
AND p.post_status = 'publish'
" . $ex_post_s_q_l . ""
. $ex_cat_s_q_l . "
ORDER BY
p.post_date_gmt DESC",
$post_type_custom
);
$posts = $wpdb->get_results($prp);
if ( $posts ) {
if ( 'post' === $post_type_custom ) {
$has_posts = true;
}
$has_enabled_post_types_posts = true;
foreach ( $posts as $post ) {
$step = 1;
for ( $i = 0; $i < $post->numposts; $i++ ) {
if ( 0 === ( $i % $links_per_page ) ) {
//$gsg->add_sitemap( 'pt', $post_type_custom . '-p' . $step . '-' . sprintf( '%04d-%02d', $post->year, $post->month ), $gsg->get_timestamp_from_my_sql( $post->last_mod ), 'p' . $step );
$gsg->add_sitemap( 'pt', $post_type_custom . '-sitemap' . ($step === 1? '' : $step) , $gsg->get_timestamp_from_my_sql( $post->last_mod ) );
$step = ++$step;
}
}
// $gsg->add_sitemap( 'pt', $post_type_custom . '-' . sprintf( '%04d-%02d', $post->year, $post->month ), $gsg->get_timestamp_from_my_sql( $post->last_mod ) );
}
}
// phpcs:enable
}
}
// Only include authors if there is a public post with a enabled post type.
if ( $gsg->get_option( 'in_auth' ) && $has_enabled_post_types_posts ) {
$gsg->add_sitemap( 'authors-sitemap', null, $blog_update );
}
// Only include archived if there are posts with postType post.
if ( $gsg->get_option( 'in_arch' ) && $has_posts ) {
$gsg->add_sitemap( 'archives-sitemap', null, $blog_update );
}
/**
* Filter: 'sm_sitemap_index' - Allow extending and modifying the xml links to include.
*
* @param array $sitemap_custom_items The custom xml link to include.
*/
$sitemap_custom_items = [];
$sitemap_custom_items = apply_filters( 'sm_sitemap_index', $sitemap_custom_items );
if ( ! is_array( $sitemap_custom_items ) ) {
$sitemap_custom_items = [];
}
if ( ! empty( $sitemap_custom_items ) ) {
foreach ( $sitemap_custom_items as $sitemap_custom_item ) {
$title = ( isset( $sitemap_custom_item['title'] ) ) ? $sitemap_custom_item['title'] : '';
$modified = ( isset( $sitemap_custom_item['modified'] ) ) ? strtotime( $sitemap_custom_item['modified'] ) : '';
if ( $title != '' && $modified != '' ) {
$gsg->add_sitemap( $title, null, $modified );
}
}
}
}
/**
* Return the URL to the sitemap related to a specific post
*
* @param array $urls Post sitemap urls.
* @param GoogleSitemapGenerator $gsg Instance of google sitemap generator.
* @param int $post_id The post ID.
*
* @return string[]
*/
public function get_sitemap_url_for_post( array $urls, $gsg, $post_id ) {
$post = get_post( $post_id );
if ( $post ) {
$last_modified = $gsg->get_timestamp_from_my_sql( $post->post_modified_gmt );
$url = $gsg->get_xml_url( 'pt', $post->post_type . '-' . gmdate( 'Y-m', $last_modified ) );
$urls[] = $url;
}
return $urls;
}
public function get_terms( $args = [] ) {
global $wpdb;
$taxonomy = ( isset( $args['taxonomy'] ) && null !== $args['taxonomy'] ) ? $args['taxonomy'] : false;
$sql = 'SELECT DISTINCT *';
$sql .= ' FROM '.$wpdb->prefix.'terms as t';
$sql .= ' INNER JOIN '.$wpdb->prefix.'term_taxonomy as tt';
$sql .= ' WHERE `tt`.taxonomy = \'' . $taxonomy . '\'';
$sql .= ' AND `tt`.term_id = `t`.term_id';
if ( ! empty( $args ) ) {
if ( isset( $args['hide_empty'] ) && $args['hide_empty'] === true ) {
$sql .= ' AND `tt`.count != 0';
}
if ( isset( $args['hierarchical'] ) && $args['hierarchical'] === true ) {
$sql .= ' AND `tt`.parent != 0';
}
if ( isset( $args['exclude'] ) && is_array( $args['exclude'] ) && ! empty( $args['exclude'] ) ) {
foreach ( $args['exclude'] as $term_id ) {
$sql .= ' AND `tt`.term_id != ' . $term_id;
}
}
$sql .= ' ORDER BY t.name ASC';
if ( isset( $args['number'] ) && $args['number'] != '' ) {
$sql .= ' LIMIT ' . $args['number'];
}
if ( isset( $args['offset'] ) && $args['offset'] != '' ) {
$sql .= ' OFFSET ' . $args['offset'];
}
}
$result = $wpdb->get_results($sql);
return $result;
}
}
if ( defined( 'WPINC' ) ) {
new GoogleSitemapGeneratorStandardBuilder();
}