This commit is contained in:
2026-03-11 15:57:27 +01:00
parent 481271c972
commit b4b460fd21
10775 changed files with 2071579 additions and 26409 deletions

View File

@@ -0,0 +1,174 @@
<?php
namespace GRIM_SG;
use GRIM_SG\Vendor\Controller;
class Cache extends Controller {
private static $max_chunk_size = 500000;
private static $prefix = 'sgg_cache_';
public static $sitemaps = array(
'sitemap',
'inner-sitemap',
'google-news',
'image-sitemap',
'video-sitemap',
);
public $sitemap;
public $inner_sitemap;
public $current_page;
public function __construct( $sitemap = 'sitemap', $inner_sitemap = null, $current_page = null ) {
$this->sitemap = $this->get_multilingual_sitemap_name( $sitemap );
$this->inner_sitemap = $inner_sitemap;
$this->current_page = $current_page ?? '';
}
public function set( $urls ) {
$expiration = self::get_expiration( $this->get_settings() );
$option_name = self::$prefix . $this->sitemap;
if ( $this->inner_sitemap ) {
$cached_urls = get_transient( $option_name );
if ( empty( $cached_urls ) ) {
$cached_urls = array();
}
if ( ! empty( $urls[ $this->inner_sitemap ] ) ) {
$cached_urls[ $this->inner_sitemap . $this->current_page ] = $urls[ $this->inner_sitemap ];
}
$urls = $cached_urls;
}
// Only apply splitting for Media Sitemaps.
if ( strpos( $this->sitemap, 'media-' ) === 0 ) {
$serialized = maybe_serialize( $urls );
if ( strlen( $serialized ) > self::$max_chunk_size ) {
$chunks = str_split( $serialized, self::$max_chunk_size );
foreach ( $chunks as $i => $chunk ) {
set_transient( $option_name . '_chunk_' . $i, $chunk, $expiration );
}
set_transient( $option_name . '_chunks', count( $chunks ), $expiration );
set_transient( $option_name . '_time', time(), $expiration );
return;
}
}
set_transient( $option_name, $urls, $expiration );
set_transient( $option_name . '_time', time(), $expiration );
}
public function get() {
$option_name = self::$prefix . $this->sitemap;
// Check if the data was saved in chunks.
$chunks_count = get_transient( $option_name . '_chunks' );
if ( false !== $chunks_count ) {
$serialized = '';
for ( $i = 0; $i < $chunks_count; $i ++ ) {
$chunk = get_transient( $option_name . '_chunk_' . $i );
if ( false === $chunk ) {
return null;
}
$serialized .= $chunk;
}
$urls = maybe_unserialize( $serialized );
} else {
$urls = get_transient( $option_name );
}
if ( $this->inner_sitemap ) {
if ( empty( $urls[ $this->inner_sitemap . $this->current_page ] ) ) {
return null;
}
return array(
$this->inner_sitemap => $urls[ $this->inner_sitemap . $this->current_page ],
);
}
return $urls;
}
public static function get_time( $sitemap ) {
return get_transient( self::$prefix . $sitemap . '_time' );
}
public static function get_time_formatted( $sitemap ) {
$time = self::get_time( $sitemap );
return $time
// translators: %s is Cached Time
? sprintf( __( '%s ago', 'xml-sitemap-generator-for-google' ), human_time_diff( $time, time() ) )
: __( 'No Cache', 'xml-sitemap-generator-for-google' );
}
public static function delete( $sitemap ): void {
$option_name = self::$prefix . $sitemap;
delete_transient( $option_name );
delete_transient( $option_name . '_time' );
// Delete chunks if they exist.
$chunks_count = get_transient( $option_name . '_chunks' );
if ( false !== $chunks_count ) {
for ( $i = 0; $i < $chunks_count; $i ++ ) {
delete_transient( $option_name . '_chunk_' . $i );
}
}
delete_transient( $option_name . '_chunks' );
}
public static function clear(): void {
foreach ( self::$sitemaps as $sitemap ) {
self::delete( $sitemap );
}
}
public static function maybe_clear( $expiration ): void {
foreach ( self::$sitemaps as $sitemap ) {
if ( $expiration < time() - self::get_time( $sitemap ) ) {
self::delete( $sitemap );
}
}
}
public static function get_expiration( $settings ) {
return intval( $settings->cache_timeout ?? 24 ) * intval( $settings->cache_timeout_period ?? 3600 );
}
public function get_multilingual_sitemap_name( $sitemap ) {
$suffix = '';
if ( function_exists( 'pll_current_language' ) ) {
$suffix = pll_current_language();
}
if ( function_exists( 'trp_get_languages' ) ) {
$trp_settings = get_option( 'trp_settings' );
$suffix = $trp_settings['default-language'] ?? null;
}
if ( defined( 'ICL_SITEPRESS_VERSION' ) ) {
$suffix = apply_filters( 'wpml_current_language', null );
}
if ( ! empty( $suffix ) ) {
$sitemap = "{$sitemap}_{$suffix}";
}
return $sitemap;
}
}

View File

@@ -0,0 +1,503 @@
<?php
namespace GRIM_SG;
use GRIM_SG\Vendor\Controller;
use SGG_PRO\Classes\Video_Sitemap;
use SGG_PRO\Classes\Video_Data_Cache;
class Dashboard extends Controller {
/**
* Dashboard constructor.
*/
public function __construct() {
add_action( 'admin_init', array( $this, 'register_settings' ) );
add_action( 'admin_menu', array( $this, 'admin_menu_pages' ) );
add_filter( 'plugin_action_links_' . GRIM_SG_BASENAME, array( $this, 'plugin_action_links' ) );
add_filter( 'plugin_row_meta', array( $this, 'plugin_meta_links' ), 10, 2 );
add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
add_action( 'admin_print_footer_scripts', array( $this, 'add_plugin_logo_script' ) );
}
/**
* Menu
*/
public function admin_menu_pages() {
add_options_page(
esc_html__( 'Google XML Sitemaps Generator Settings', 'xml-sitemap-generator-for-google' ),
esc_html__( 'XML Sitemaps', 'xml-sitemap-generator-for-google' ),
'manage_options',
self::$slug,
array( $this, 'render_settings_page' )
);
}
/**
* Enqueue admin styles and scripts for plugin
*/
public function admin_enqueue_scripts( $hook ) {
wp_register_style(
'sgg-rate-banner',
GRIM_SG_URL . 'assets/css/rate-banner.min.css',
array(),
GRIM_SG_VERSION
);
wp_register_style(
'sgg-icons',
GRIM_SG_URL . 'assets/fonts/icons/style.css',
array(),
GRIM_SG_VERSION
);
if ( 'settings_page_xml-sitemap-generator-for-google' === $hook ) {
wp_enqueue_style( 'sgg-fonts', GRIM_SG_URL . 'assets/fonts/albert_sans/fonts.css', array(), GRIM_SG_VERSION );
wp_enqueue_style( 'sgg-icons', GRIM_SG_URL . 'assets/fonts/icons/style.css', array(), GRIM_SG_VERSION );
wp_enqueue_style( 'sgg-styles', GRIM_SG_URL . 'assets/css/styles.min.css', array( 'sgg-fonts' ), GRIM_SG_VERSION );
wp_enqueue_script( 'jquery-ui', GRIM_SG_URL . 'assets/js/jquery-ui.min.js', array( 'jquery' ), GRIM_SG_VERSION, true );
wp_enqueue_script( 'sgg-scripts', GRIM_SG_URL . 'assets/js/scripts.js', array( 'jquery' ), GRIM_SG_VERSION, true );
if ( function_exists( 'xml_sitemap_generate_settings' ) ) {
$settings_array = xml_sitemap_generate_settings() ?? array();
wp_localize_script(
'sgg-scripts',
'grimData',
array(
'settingsArray' => $settings_array,
'noSettingsFound' => esc_html__( 'Settings not found', 'xml-sitemap-generator-for-google' ),
'NoUrls' => esc_html__( 'No URLs added yet!', 'xml-sitemap-generator-for-google' ),
)
);
}
wp_localize_script(
'sgg-scripts',
'sgg',
array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
)
);
}
}
/**
* Settings page
*/
public function render_settings_page() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
$this->save_settings();
self::render(
'settings.php',
array(
'settings' => $this->get_settings(),
'cpt' => $this->get_cpt( 'objects' ),
'taxonomies' => $this->get_taxonomy_types( 'objects' ),
)
);
}
/**
* Register Settings
*/
public function register_settings() {
register_setting( self::$slug, self::$slug );
}
/**
* Save Settings
*/
public function save_settings() {
if ( 'POST' !== strtoupper( $_SERVER['REQUEST_METHOD'] ?? '' ) || ! isset( $_POST['save_settings'] ) ) {
return;
}
if ( ! isset( $_POST['sgg_settings_nonce'] ) || ! wp_verify_nonce( $_POST['sgg_settings_nonce'], GRIM_SG_BASENAME . '-settings' ) ) {
return;
}
// Run Tools Actions
if ( Tools::run_tools_actions( $_POST ) ) {
return;
}
// Change IndexNow API Key
if ( ! empty( $_POST['change_indexnow_key'] ) ) {
IndexNow::delete_api_key();
add_settings_error(
self::$slug,
'indexnow_api_key',
esc_html__( 'IndexNow API Key changed successfully.', 'xml-sitemap-generator-for-google' ),
'success'
);
return;
}
// Clear Media Sitemap Cache on every save
if ( empty( $_POST['disable_media_sitemap_cache'] ) ) {
MediaSitemap::delete_all_cache();
}
// Clear Sitemaps Cache
if ( ! empty( $_POST['clear_cache'] ) ) {
Cache::clear();
add_settings_error(
self::$slug,
'sitemap_cache',
esc_html__( 'Sitemaps Cache cleared.', 'xml-sitemap-generator-for-google' ),
'success'
);
return;
}
// Import Settings
if ( ! empty( $_POST['import_settings'] ) ) {
ImportExport::import_settings();
return;
}
// Clear Video Data Cache
if ( ! empty( $_POST['clear_video_api_cache'] ) && is_callable( 'SGG_PRO\Classes\Video_Data_Cache::delete' ) ) {
Video_Data_Cache::delete();
add_settings_error(
self::$slug,
'video_api_cache',
esc_html__( 'Video API Data Cache cleared.', 'xml-sitemap-generator-for-google' ),
'success'
);
return;
}
$settings = new Settings();
$saved_settings = $this->get_settings();
// Check YouTube API Key
if ( ( ! empty( $_POST['youtube_check_api_key'] ) || $saved_settings->youtube_api_key !== $_POST['youtube_api_key'] )
&& ! empty( $_POST['youtube_api_key'] ) && is_callable( 'SGG_PRO\Classes\Video_Sitemap::request_youtube_data' ) ) {
$youtube_data = Video_Sitemap::request_youtube_data( 'dQw4w9WgXcQ', sanitize_text_field( $_POST['youtube_api_key'] ) );
if ( ! empty( $youtube_data ) && is_array( $youtube_data ) ) {
add_settings_error( self::$slug, 'youtube_api_key_success', esc_html__( 'YouTube API key passed the check successfully.', 'xml-sitemap-generator-for-google' ), 'success' );
} else {
add_settings_error( self::$slug, 'youtube_api_key_error', "YouTube API: $youtube_data" );
}
if ( ! empty( $_POST['youtube_check_api_key'] ) ) {
return;
}
}
// Check Vimeo API Key
if ( ( ! empty( $_POST['vimeo_check_api_key'] ) || $saved_settings->vimeo_api_key !== $_POST['vimeo_api_key'] )
&& ! empty( $_POST['vimeo_api_key'] ) && is_callable( 'SGG_PRO\Classes\Video_Sitemap::request_vimeo_data' ) ) {
$vimeo_data = Video_Sitemap::request_vimeo_data( '22439234', sanitize_text_field( $_POST['vimeo_api_key'] ) );
if ( ! empty( $vimeo_data ) && is_array( $vimeo_data ) ) {
add_settings_error( self::$slug, 'vimeo_api_key_success', esc_html__( 'Vimeo Access Token passed the check successfully.', 'xml-sitemap-generator-for-google' ), 'success' );
} else {
add_settings_error( self::$slug, 'vimeo_api_key_error', "Vimeo API: $vimeo_data" );
}
if ( ! empty( $_POST['vimeo_check_api_key'] ) ) {
return;
}
}
// Change Cron Job if Runtime is changed
if ( ! isset( $_POST['enable_cronjob'] ) || ! $_POST['enable_cronjob'] || ( ! empty( $_POST['cronjob_runtime'] ) && $saved_settings->cronjob_runtime !== $_POST['cronjob_runtime'] ) ) {
wp_clear_scheduled_hook( 'xml_sitemap_cronjob' );
}
$settings->enable_sitemap = sanitize_text_field( $_POST['enable_sitemap'] ?? 0 );
$settings->sitemap_url = sanitize_text_field( $_POST['sitemap_url'] ?? $settings->sitemap_url );
$settings->links_per_page = sanitize_text_field( $_POST['links_per_page'] ?? $settings->links_per_page );
$settings->html_sitemap_url = sanitize_text_field( $_POST['html_sitemap_url'] ?? $settings->html_sitemap_url );
$settings->enable_html_sitemap = sanitize_text_field( $_POST['enable_html_sitemap'] ?? 0 );
$settings->sitemap_to_robots = sanitize_text_field( $_POST['sitemap_to_robots'] ?? 0 );
$settings->enable_indexnow = sanitize_text_field( $_POST['enable_indexnow'] ?? 0 );
$settings->sitemap_view = sanitize_text_field( $_POST['sitemap_view'] ?? '' );
$settings->exclude_posts = apply_filters( 'sanitize_post_array', $_POST['exclude_posts'] ?? '' );
$settings->exclude_terms = apply_filters( 'sanitize_post_array', $_POST['exclude_terms'] ?? '' );
$settings->include_only_terms = apply_filters( 'sanitize_post_array', $_POST['include_only_terms'] ?? '' );
$settings->posts_priority = sanitize_text_field( $_POST['posts_priority'] ?? '' );
$settings->enable_google_news = sanitize_text_field( $_POST['enable_google_news'] ?? 0 );
$settings->google_news_old_posts = sanitize_text_field( $_POST['google_news_old_posts'] ?? 0 );
$settings->google_news_name = sanitize_text_field( $_POST['google_news_name'] ?? '' );
$settings->google_news_url = sanitize_text_field( $_POST['google_news_url'] ?? $settings->google_news_url );
$settings->google_news_keywords = sanitize_text_field( $_POST['google_news_keywords'] ?? '' );
$settings->google_news_stocks = sanitize_text_field( $_POST['google_news_stocks'] ?? 0 );
$settings->google_news_exclude = apply_filters( 'sanitize_post_array', $_POST['google_news_exclude'] ?? '' );
$settings->google_news_exclude_terms = apply_filters( 'sanitize_post_array', $_POST['google_news_exclude_terms'] ?? '' );
$settings->google_news_include_only_terms = apply_filters( 'sanitize_post_array', $_POST['google_news_include_only_terms'] ?? '' );
$settings->enable_image_sitemap = sanitize_text_field( $_POST['enable_image_sitemap'] ?? 0 );
$settings->enable_video_sitemap = sanitize_text_field( $_POST['enable_video_sitemap'] ?? 0 );
$settings->image_sitemap_url = sanitize_text_field( $_POST['image_sitemap_url'] ?? $settings->image_sitemap_url );
$settings->video_sitemap_url = sanitize_text_field( $_POST['video_sitemap_url'] ?? $settings->video_sitemap_url );
$settings->hide_image_previews = sanitize_text_field( $_POST['hide_image_previews'] ?? 0 );
$settings->hide_image_sitemap_xsl = sanitize_text_field( $_POST['hide_image_sitemap_xsl'] ?? 0 );
$settings->hide_video_sitemap_xsl = sanitize_text_field( $_POST['hide_video_sitemap_xsl'] ?? 0 );
$settings->image_mime_types = apply_filters( 'sanitize_post_array', $_POST['image_mime_types'] ?? $settings->image_mime_types );
$settings->youtube_api_key = sanitize_text_field( $_POST['youtube_api_key'] ?? $settings->youtube_api_key );
$settings->vimeo_api_key = sanitize_text_field( $_POST['vimeo_api_key'] ?? $settings->vimeo_api_key );
$settings->exclude_broken_images = sanitize_text_field( $_POST['exclude_broken_images'] ?? 0 );
$settings->include_featured_images = sanitize_text_field( $_POST['include_featured_images'] ?? 0 );
$settings->include_woo_gallery = sanitize_text_field( $_POST['include_woo_gallery'] ?? 0 );
$settings->enable_cache = sanitize_text_field( $_POST['enable_cache'] ?? 0 );
$settings->cache_timeout = sanitize_text_field( $_POST['cache_timeout'] ?? $settings->cache_timeout );
$settings->cache_timeout_period = sanitize_text_field( $_POST['cache_timeout_period'] ?? $settings->cache_timeout_period );
$settings->clear_cache_on_save_post = sanitize_text_field( $_POST['clear_cache_on_save_post'] ?? 0 );
$settings->enable_video_api_cache = sanitize_text_field( $_POST['enable_video_api_cache'] ?? 0 );
$settings->disable_media_sitemap_cache = sanitize_text_field( $_POST['disable_media_sitemap_cache'] ?? 0 );
$settings->minimize_sitemap = sanitize_text_field( $_POST['minimize_sitemap'] ?? 0 );
$settings->colors = apply_filters( 'sanitize_post_array', $_POST['colors'] ?? $settings->colors );
$settings->hide_branding = sanitize_text_field( $_POST['hide_branding'] ?? 0 );
$settings->enable_cronjob = sanitize_text_field( $_POST['enable_cronjob'] ?? 0 );
$settings->cronjob_runtime = sanitize_text_field( $_POST['cronjob_runtime'] ?? $settings->cronjob_runtime );
$settings->home = $settings->get_row_value( 'home' );
$settings->page = $settings->get_row_value( 'page' );
$settings->post = $settings->get_row_value( 'post' );
$settings->archive = $settings->get_row_value( 'archive' );
$settings->archive_older = $settings->get_row_value( 'archive_older' );
$settings->authors = $settings->get_row_value( 'authors' );
$settings->media = $settings->get_row_value( 'media' );
foreach ( $this->get_cpt() as $cpt ) {
$settings->cpt[ $cpt ] = $settings->get_row_value( $cpt );
}
foreach ( $this->get_taxonomy_types() as $taxonomy_type ) {
$settings->taxonomies[ $taxonomy_type ] = $settings->get_row_value( $taxonomy_type );
}
$custom_sitemap_urls = apply_filters( 'sanitize_post_array', $_POST['custom_sitemap_urls'] ?? array() );
$custom_sitemap_lastmods = apply_filters( 'sanitize_post_array', $_POST['custom_sitemap_lastmods'] ?? array() );
foreach ( $custom_sitemap_urls as $key => $value ) {
$custom_sitemap = array(
'url' => $value,
'lastmod' => $custom_sitemap_lastmods[ $key ],
);
$settings->custom_sitemaps[] = $custom_sitemap;
}
$additional_urls = apply_filters( 'sanitize_post_array', $_POST['additional_urls'] ?? array() );
$additional_priorities = apply_filters( 'sanitize_post_array', $_POST['additional_priorities'] ?? array() );
$additional_frequencies = apply_filters( 'sanitize_post_array', $_POST['additional_frequencies'] ?? array() );
$additional_lastmods = apply_filters( 'sanitize_post_array', $_POST['additional_lastmods'] ?? array() );
foreach ( $additional_urls as $key => $value ) {
$page = array(
'url' => $value,
'priority' => $additional_priorities[ $key ],
'frequency' => $additional_frequencies[ $key ],
'lastmod' => $additional_lastmods[ $key ],
);
$settings->additional_pages[] = $page;
}
update_option( self::$slug, $settings );
$new_cache_expires = Cache::get_expiration( $settings );
if ( Cache::get_expiration( $saved_settings ) !== $new_cache_expires ) {
Cache::maybe_clear( $new_cache_expires );
}
if ( ( $settings->sitemap_view !== $saved_settings->sitemap_view ) || ( $settings->links_per_page !== $saved_settings->links_per_page ) ) {
Cache::clear();
flush_rewrite_rules();
}
add_settings_error(
self::$slug,
'sitemap_settings',
esc_html__( 'Changes saved!', 'xml-sitemap-generator-for-google' ),
'success'
);
flush_rewrite_rules();
}
/**
* Render Sitemap Post Row
* @param $title
* @param $option
* @param $data
*/
public static function render_post_row( $title, $option, $data ) {
self::render(
'fields/post-row.php',
array(
'title' => $title,
'option' => $option,
'data' => $data,
)
);
}
/**
* Render Priority Selectbox
* @param $name
* @param $value
*/
public static function render_priority_field( $name, $value ) {
self::render(
'fields/priority.php',
array(
'name' => $name,
'value' => $value,
)
);
}
/**
* Render Frequency Selectbox
* @param $name
* @param $value
*/
public static function render_frequency_field( $name, $value ) {
self::render(
'fields/frequency.php',
array(
'name' => $name,
'value' => $value,
)
);
}
/**
* Render Google News Selectbox
* @param $title
* @param $name
* @param $value
*/
public static function render_content_field( $title, $name, $value, $class = '' ) {
self::render(
'fields/content.php',
array(
'title' => $title,
'name' => $name,
'value' => $value,
'class' => $class,
)
);
}
/**
* Render Template
* @param $template_name
* @param $args
*/
public static function render( $template_name, $args = array() ) {
global $wp_version;
if ( version_compare( $wp_version, '5.5.0', '<' ) ) {
set_query_var( 'args', $args );
}
load_template( GRIM_SG_PATH . "/templates/{$template_name}", false, $args );
}
/**
* Setting Link
*
* @param $links
* @return mixed
*/
public function plugin_action_links( $links ) {
$settings_link = sprintf(
'<a href="%1$s">%2$s</a>',
admin_url( 'options-general.php?page=' . self::$slug ),
esc_html__( 'Settings', 'xml-sitemap-generator-for-google' )
);
$docs_link = sprintf(
'<a href="%1$s" target="_blank" rel="noopener noreferrer">%2$s</a>',
esc_url( 'https://wpgrim.com/docs/google-xml-sitemaps-generator/general/xml-sitemap/' ),
esc_html__( 'Docs', 'xml-sitemap-generator-for-google' )
);
if ( ! sgg_pro_enabled() ) {
$pro_link = sprintf(
'<a href="%1$s" style="font-weight: 600; color: #ff7113;" target="_blank">%2$s</a>',
sgg_get_pro_url( 'plugin-action-link' ),
esc_html__( 'Get Pro Version', 'xml-sitemap-generator-for-google' )
);
$links = array_merge( array( $pro_link, $settings_link, $docs_link ), $links );
} else {
$links = array_merge( array( $settings_link, $docs_link ), $links );
}
return $links;
}
/**
* Meta Links
*
* @param $links
* @return mixed
*/
public function plugin_meta_links( $links, $file ) {
if ( GRIM_SG_BASENAME === $file ) {
$links[] = '<a href="' . esc_url( sgg_get_support_url() ) . '" target="_blank">' . __( 'Support', 'xml-sitemap-generator-for-google' ) . '</a>';
$links[] = '<a href="' . esc_url( sgg_get_review_url() ) . '" target="_blank">' . __( 'Rate ★★★★★', 'xml-sitemap-generator-for-google' ) . '</a>';
}
return $links;
}
/**
* Add plugin logo script to plugins page
*/
public function add_plugin_logo_script() {
global $pagenow;
// Only add script on plugins page
if ( 'plugins.php' !== $pagenow ) {
return;
}
?>
<script type="text/javascript">
document.addEventListener('DOMContentLoaded', function() {
// Find the plugin row for XML Sitemap Generator
const pluginRow = document.querySelector('tr[data-slug="xml-sitemap-generator-for-google"]');
if (pluginRow) {
const pluginTitle = pluginRow.querySelector('.plugin-title strong');
if (pluginTitle) {
// Create logo element
const logo = document.createElement('img');
logo.src = '<?php echo esc_url( GRIM_SG_URL . 'assets/images/sgg-logo.svg' ); ?>';
logo.alt = 'XML Sitemap Generator Logo';
logo.style.width = '20px';
logo.style.height = '20px';
logo.style.paddingRight = '10px';
logo.style.verticalAlign = 'middle';
// Insert logo before the plugin title
pluginTitle.insertBefore(logo, pluginTitle.firstChild);
}
}
});
</script>
<?php
}
}

View File

@@ -0,0 +1,240 @@
<?php
namespace GRIM_SG;
use GRIM_SG\Vendor\Controller;
class Frontend extends Controller {
private static $rules_option = 'grim_sg_rules';
private static $rules_version = '1.1';
/**
* Sitemap constructor.
*/
public function __construct() {
self::set_rewrite_hooks();
add_filter( 'query_vars', array( $this, 'register_query_vars' ), 1 );
add_action( 'parse_request', array( $this, 'render_sitemaps' ), 1 );
add_action( 'do_robots', array( $this, 'do_robots_link' ), 100 );
add_action( 'admin_init', array( $this, 'reset_rewrite_rules' ) );
}
/**
* Register Sitemap Query Variable
* @param $query_vars
* @return mixed
*/
public function register_query_vars( $query_vars ) {
$query_vars[] = 'sitemap_xsl';
$query_vars[] = 'sitemap_xml';
$query_vars[] = 'sitemap_html';
$query_vars[] = 'google_news';
$query_vars[] = 'image_sitemap';
$query_vars[] = 'video_sitemap';
$query_vars[] = 'inner_sitemap';
$query_vars[] = 'multilingual_sitemap';
$query_vars[] = 'page';
return $query_vars;
}
/**
* Template Redirect
*/
public function render_sitemaps( $wp ) {
global $wp_query;
$is_xsl_sitemap = ! empty( $wp->query_vars['sitemap_xsl'] );
$is_xml_sitemap = ! empty( $wp->query_vars['sitemap_xml'] );
$is_html_sitemap = ! empty( $wp->query_vars['sitemap_html'] );
$is_google_news = ! empty( $wp->query_vars['google_news'] );
$is_image_sitemap = ! empty( $wp->query_vars['image_sitemap'] );
$is_video_sitemap = ! empty( $wp->query_vars['video_sitemap'] );
$is_multilingual = ! empty( $wp->query_vars['multilingual_sitemap'] );
if ( $is_xsl_sitemap || $is_xml_sitemap || $is_html_sitemap || $is_google_news || $is_image_sitemap || $is_video_sitemap || $is_multilingual ) {
$wp_query->is_404 = false;
$wp_query->is_feed = true;
$inner_sitemap = $wp->query_vars['inner_sitemap'] ?? null;
$current_page = $wp->query_vars['page'] ?? null;
if ( ! empty( $inner_sitemap ) && empty( $current_page ) ) {
$current_page = 0;
}
if ( $is_xsl_sitemap ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
Sitemap::generate_sitemap_xsl( sanitize_text_field( $_GET['template'] ?? $wp->query_vars['sitemap_xsl'] ) );
} elseif ( $is_google_news ) {
( new GoogleNews() )->show_sitemap( GoogleNews::$template );
} elseif ( $is_image_sitemap || 'image' === $inner_sitemap ) {
( new ImageSitemap() )->show_sitemap( ImageSitemap::$template, true, $inner_sitemap, $current_page );
} elseif ( $is_video_sitemap || 'video' === $inner_sitemap ) {
( new VideoSitemap() )->show_sitemap( VideoSitemap::$template, true, $inner_sitemap, $current_page );
} elseif ( $is_multilingual ) {
( new MultilingualSitemap() )->show_sitemap( MultilingualSitemap::$template );
} else {
( new Sitemap() )->show_sitemap( 'sitemap', $is_xml_sitemap, $inner_sitemap, $current_page );
}
exit;
}
}
/**
* Add Sitemap Links to Robots
*/
public function do_robots_link() {
$settings = $this->get_settings();
$home_url = get_site_url();
if ( $settings->sitemap_to_robots ) {
echo "\nSitemap: {$home_url}/{$settings->sitemap_url}\n"; // phpcs:ignore
if ( $settings->enable_google_news ) {
echo "Sitemap: {$home_url}/{$settings->google_news_url}\n"; // phpcs:ignore
}
if ( empty( $settings->sitemap_view ) ) {
if ( $settings->enable_image_sitemap ) {
echo "Sitemap: {$home_url}/{$settings->image_sitemap_url}\n"; // phpcs:ignore
}
if ( $settings->enable_video_sitemap ) {
echo "Sitemap: {$home_url}/{$settings->video_sitemap_url}\n"; // phpcs:ignore
}
}
}
}
/**
* Add Custom Rewrite Rules
*
* @param $wp_rules
* @return array
*/
public static function add_rewrite_rules( $wp_rules ) {
$settings = get_option( self::$slug, new Settings() );
$stylesheet_url = str_replace( '.', '\.', apply_filters( 'sitemap_xsl_template_path', 'sitemap-stylesheet.xsl' ) ) . '$';
$sitemap_types = array( 'page', 'post', 'category', 'author', 'archive', 'media', 'additional', 'image', 'video' );
$custom_posts = ( new Controller() )->get_cpt();
$grim_sg_rules = array(
$stylesheet_url => 'index.php?sitemap_xsl=true',
);
if ( ! empty( $settings->enable_sitemap ) && ! apply_filters( 'sgg_disable_xml_sitemap', false ) ) {
$sitemap_url = str_replace( '.', '\.', $settings->sitemap_url ) . '$';
$grim_sg_rules[ $sitemap_url ] = 'index.php?sitemap_xml=true';
if ( ! empty( $settings->sitemap_view ) ) {
foreach ( $sitemap_types as $type ) {
$regex_pattern = in_array( $type, array( 'image', 'video' ) )
? "{$type}-sitemap([0-9]+)\.xml$"
: "{$type}-sitemap([0-9]+)?\.xml$";
$grim_sg_rules[ $regex_pattern ] = "index.php?sitemap_xml=true&inner_sitemap={$type}&page=\$matches[1]";
}
foreach ( $custom_posts as $cpt ) {
$grim_sg_rules[ "{$cpt}-sitemap([0-9]+)?\.xml$" ] = "index.php?sitemap_xml=true&inner_sitemap={$cpt}&page=\$matches[1]";
}
}
}
if ( sgg_pro_enabled() && $settings->enable_html_sitemap ) {
$html_sitemap_url = str_replace( '.', '\.', $settings->html_sitemap_url ) . '$';
$grim_sg_rules[ $html_sitemap_url ] = 'index.php?sitemap_html=true';
foreach ( $sitemap_types as $type ) {
$regex_pattern = in_array( $type, array( 'image', 'video' ) )
? "{$type}-sitemap([0-9]+)\.html$"
: "{$type}-sitemap([0-9]+)?\.html$";
$grim_sg_rules[ $regex_pattern ] = "index.php?sitemap_html=true&inner_sitemap={$type}&page=\$matches[1]";
}
foreach ( $custom_posts as $cpt ) {
$grim_sg_rules[ "{$cpt}-sitemap([0-9]+)?\.html$" ] = "index.php?sitemap_html=true&inner_sitemap={$cpt}&page=\$matches[1]";
}
}
if ( $settings->enable_google_news ) {
$google_news_url = str_replace( '.', '\.', $settings->google_news_url ) . '$';
$grim_sg_rules[ $google_news_url ] = 'index.php?google_news=true';
}
if ( $settings->enable_image_sitemap ) {
$image_sitemap_url = str_replace( '.', '\.', $settings->image_sitemap_url ) . '$';
$grim_sg_rules[ $image_sitemap_url ] = 'index.php?image_sitemap=true';
}
if ( $settings->enable_video_sitemap ) {
$video_sitemap_url = str_replace( '.', '\.', $settings->video_sitemap_url ) . '$';
$grim_sg_rules[ $video_sitemap_url ] = 'index.php?video_sitemap=true';
}
if ( sgg_is_multilingual() ) {
$languages = sgg_get_languages( true );
if ( ! empty( $languages ) ) {
global $wp_rewrite;
$lang_slug = $wp_rewrite->root . '^(' . implode( '|', $languages ) . ')?/?';
$lang_rules = array();
foreach ( $grim_sg_rules as $key => $rule ) {
$lang_rules[ $lang_slug . $key ] = preg_replace( '/matches\[1\]/', 'matches[2]', $rule );
}
$grim_sg_rules = $lang_rules;
}
$grim_sg_rules['multilingual-sitemap.xml'] = 'index.php?multilingual_sitemap=true';
}
if ( empty( $wp_rules ) ) {
return $grim_sg_rules;
}
return array_merge( $grim_sg_rules, $wp_rules );
}
/**
* Set Rewrite Hooks
*/
public static function set_rewrite_hooks() {
add_filter( 'option_rewrite_rules', array( self::class, 'add_rewrite_rules' ), 100, 1 );
}
/**
* Activate Rewrite Rules
*/
public static function activate_rewrite_rules() {
global $wp_rewrite;
$wp_rewrite->flush_rules( false );
update_option( self::$rules_option, self::$rules_version );
}
/**
* Run on Plugin Activate
*/
public static function activate_plugin() {
self::set_rewrite_hooks();
self::activate_rewrite_rules();
flush_rewrite_rules();
}
/**
* Run on Rules Version Updated
*/
public function reset_rewrite_rules() {
$rules_version = get_option( self::$rules_option, false );
if ( $rules_version !== self::$rules_version ) {
self::activate_plugin();
}
}
}

View File

@@ -0,0 +1,171 @@
<?php
namespace GRIM_SG;
use GRIM_SG\Vendor\QueryBuilder;
class GoogleNews extends Sitemap {
public static $template = 'google-news';
private $blog_language = null;
/**
* Add URLS Callback function
*/
public function urlsCallback() {
return 'addNewsUrl';
}
/**
* Adding Google News Sitemap Headers
*/
public function extraSitemapHeader() {
return array( 'xmlns:news' => 'http://www.google.com/schemas/sitemap-news/0.9' );
}
/**
* Collect Sitemap URLs
*/
public function collect_urls( $template = 'sitemap', $inner_sitemap = null, $current_page = null ) {
$this->add_posts();
}
/**
* Add all Posts to Sitemap
*/
public function add_posts( $post_type = null, $current_page = null, $is_sitemap_index = false ) {
global $wpdb;
$front_page_id = get_option( 'page_on_front' );
$post_types = array( 'page', 'post' );
$exclude_post_ids = apply_filters( 'sgg_sitemap_exclude_post_ids', apply_filters( 'sgg_sitemap_exclude_ids', array(), $this->settings->google_news_exclude ?? '' ) );
$exclude_term_ids = apply_filters( 'sgg_sitemap_exclude_term_ids', apply_filters( 'sgg_sitemap_exclude_ids', array(), $this->settings->google_news_exclude_terms ?? '' ) );
$include_term_ids = apply_filters( 'sgg_sitemap_include_only_term_ids', apply_filters( 'sgg_sitemap_exclude_ids', array(), $this->settings->google_news_include_only_terms ?? '' ) );
if ( ! empty( $front_page_id ) ) {
$exclude_post_ids[] = $front_page_id;
}
foreach ( $post_types as $key => $post_type ) {
if ( isset( $this->settings->{$post_type}->google_news ) && ! $this->settings->{$post_type}->google_news ) {
unset( $post_types[ $key ] );
}
}
if ( sgg_pro_enabled() ) {
foreach ( $this->get_cpt() as $cpt ) {
if ( ! empty( $this->settings->cpt[ $cpt ] ) && ! empty( $this->settings->cpt[ $cpt ]->google_news ) ) {
$post_types[] = $cpt;
}
}
}
if ( empty( $post_types ) ) {
$post_types = array( 'post' );
}
$exclude_old_posts_sql = '';
if ( empty( $this->settings->google_news_old_posts ) ) {
$exclude_old_posts_sql = 'AND post_date_gmt >= DATE_SUB(NOW(), INTERVAL 48 HOUR)';
}
$exclude_posts_sql = '';
if ( ! empty( $exclude_post_ids ) ) {
$exclude_posts_sql = 'AND posts.ID NOT IN (' . implode( ',', array_unique( $exclude_post_ids ) ) . ')';
}
$terms_join_sql = '';
$terms_where_sql = '';
if ( ! empty( $include_term_ids ) ) {
$terms_join_sql = "INNER JOIN (
SELECT DISTINCT tr.object_id
FROM {$wpdb->term_relationships} tr
INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
WHERE tt.term_id IN (" . implode( ',', array_unique( $include_term_ids ) ) . ')
) included_posts ON posts.ID = included_posts.object_id';
} elseif ( ! empty( $exclude_term_ids ) ) {
$terms_join_sql = "LEFT JOIN (
SELECT DISTINCT tr.object_id
FROM {$wpdb->term_relationships} tr
INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
WHERE tt.term_id IN (" . implode( ',', array_unique( $exclude_term_ids ) ) . ')
) excluded_posts ON posts.ID = excluded_posts.object_id';
$terms_where_sql = ' AND excluded_posts.object_id IS NULL';
}
$sql_post_types = "('" . implode( "','", $post_types ) . "')";
$multilingual_sql = $this->multilingual_sql( $post_types );
$where_clause = ! empty( $multilingual_sql ) ? 'AND ' : 'WHERE ';
$sql = "SELECT
posts.ID,
posts.post_title,
posts.post_name,
posts.post_parent,
posts.post_type,
posts.post_date,
posts.post_date_gmt
FROM $wpdb->posts as posts
$terms_join_sql
$multilingual_sql
$where_clause post_status = 'publish' AND post_type IN $sql_post_types AND posts.post_password = ''
$exclude_old_posts_sql
$exclude_posts_sql
$terms_where_sql
GROUP BY posts.ID
ORDER BY posts.post_modified DESC";
$posts = QueryBuilder::run_query( $sql );
foreach ( $posts as $post ) {
if ( apply_filters( 'xml_sitemap_include_post', true, $post->ID ) ) {
$post_date = '0000-00-00 00:00:00' !== $post->post_date_gmt ? $post->post_date_gmt : $post->post_date;
$this->add_url(
get_permalink( $post ),
$post->ID,
apply_filters( 'xml_sitemap_google_news_title', $post->post_title, $post->ID ),
gmdate( DATE_W3C, strtotime( $post_date ) ),
$post->post_type
);
}
}
}
/**
* Add Google News Sitemap Url
*
* @param string $url
* @param int $id
* @param string $title
* @param string $last_modified
* @param string $post_type
*/
public function add_url( $url, $id, $title, $last_modified = '', $post_type = 'post' ) {
$this->urls[] = array(
$url, // URL
! empty( $this->settings->google_news_name ) ? $this->settings->google_news_name : get_bloginfo( 'name' ), // Publication Name
apply_filters( 'xml_sitemap_news_language', $this->get_blog_language(), $id, $post_type ), // Publication Language
$title, // Title
$last_modified, // Last Modified
$id, // ID
);
}
/**
* Get Blog Language
*/
public function get_blog_language() {
if ( null === $this->blog_language ) {
$this->blog_language = sgg_parse_language( get_bloginfo( 'language' ) );
}
return $this->blog_language;
}
public static function is_older_than_48h( $date ) {
$accepted_time = time() - ( 48 * 3600 );
$last_modified = strtotime( $date );
return $last_modified < $accepted_time;
}
}

View File

@@ -0,0 +1,50 @@
<?php
namespace GRIM_SG;
class ImageSitemap extends MediaSitemap {
public static $template = 'image-sitemap';
private $image_mime_types = array(
'image/jpeg',
'image/png',
'image/bmp',
'image/gif',
'image/webp',
'image/avif',
);
/**
* Adding Google News Sitemap Headers
*/
public function extraSitemapHeader() {
return array( 'xmlns:image' => 'http://www.google.com/schemas/sitemap-image/1.1' );
}
public function get_post_media( int $post_id, string $post_type ): array {
return apply_filters( 'sgg_image_sitemap_urls', array(), $post_id, $post_type );
}
public function add_urls( string $url, array $media ): void {
// Remove old URL if it exists
$this->urls = array_filter(
$this->urls,
function( $item ) use ( $url ) {
return $item[0] !== $url;
}
);
$this->urls[] = array(
$url, // URL
$media, // Images
);
}
public function filter_value( string $value ): bool {
$value = strtok( $value, '?' );
$filetype = wp_check_filetype( $value );
$filtered = in_array( $filetype['type'], $this->image_mime_types, true );
return apply_filters( 'sgg_filter_image_url', $filtered, $value );
}
}

View File

@@ -0,0 +1,62 @@
<?php
namespace GRIM_SG;
use GRIM_SG\Vendor\Controller;
class ImportExport extends Controller {
public function __construct() {
add_action( 'wp_ajax_export_sitemap_settings', array( $this, 'export_settings' ) );
}
public static function import_settings() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
if ( ! empty( $_FILES['import_file']['tmp_name'] ) ) {
// phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
$import_data = file_get_contents( $_FILES['import_file']['tmp_name'] );
if ( ! empty( $import_data ) ) {
$import_data = json_decode( $import_data, true );
if ( ! empty( $import_data ) ) {
update_option( self::$slug, $import_data );
add_settings_error( self::$slug, 'import_settings', esc_html__( 'Settings imported successfully.', 'xml-sitemap-generator-for-google' ), 'success' );
return;
}
}
}
add_settings_error(
self::$slug,
'import_settings',
esc_html__( 'Invalid settings file or data.', 'xml-sitemap-generator-for-google' ),
);
}
public function export_settings() {
if ( ! current_user_can( 'manage_options' ) || ! wp_verify_nonce( $_REQUEST['nonce'], 'sgg_export_settings' ) ) {
return;
}
$settings = get_option( self::$slug );
$export_data = wp_json_encode( $settings );
header( 'Content-Description: File Transfer' );
header( 'Content-type: application/txt' );
header( 'Content-Disposition: attachment; filename="sitemap_settings.json"' );
header( 'Content-Transfer-Encoding: binary' );
header( 'Expires: 0' );
header( 'Cache-Control: must-revalidate' );
header( 'Pragma: public' );
//phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
echo $export_data;
exit;
}
}

View File

@@ -0,0 +1,144 @@
<?php
namespace GRIM_SG;
use GRIM_SG\Vendor\Controller;
class IndexNow extends Controller {
private $site_url;
private $api_key;
private static $api_name = 'sgg_indexnow_api_key';
public function __construct() {
$this->set_site_url();
$this->set_api_key();
}
public function ping_site_url() {
return $this->ping_url( $this->site_url );
}
public function ping_url( $url ) {
$response = $this->request( $url );
return $this->handle_response( $response );
}
public function request( $index_url ) {
$data = wp_json_encode(
array(
'host' => $this->remove_url_scheme( $this->site_url ),
'key' => $this->api_key,
'keyLocation' => $this->get_api_key_location(),
'urlList' => array( $index_url ),
)
);
return wp_remote_post(
'https://api.indexnow.org/indexnow/',
array(
'body' => $data,
'headers' => array(
'Content-Type' => 'application/json',
),
)
);
}
private function handle_response( $response ) {
$error = array(
'status' => 'error',
'message' => __( 'IndexNow Protocol unknown error occurred', 'google-sitemap-generator' ),
);
if ( is_wp_error( $response ) ) {
return array(
'status' => 'error',
'message' => $response->get_error_message(),
);
}
if ( isset( $response['errors'] ) ) {
return $error;
}
if ( ! empty( $response['response']['code'] ) ) {
if ( in_array( $response['response']['code'], array( 200, 202 ), true ) ) {
return array(
'status' => 'success',
'message' => __( 'Changes successfully submitted to IndexNow', 'google-sitemap-generator' ),
);
} else {
if ( 400 === $response['response']['code'] ) {
$error['message'] = __( 'IndexNow Protocol Invalid Request', 'google-sitemap-generator' );
} elseif ( 403 === $response['response']['code'] ) {
$error['message'] = __( 'IndexNow Protocol Invalid Api Key', 'google-sitemap-generator' );
} elseif ( 422 === $response['response']['code'] ) {
$error['message'] = __( 'IndexNow Protocol Invalid URL', 'google-sitemap-generator' );
} elseif ( ! empty( $response['response']['message'] ) ) {
$error['message'] = sprintf(
/* translators: %s: error message */
__( 'IndexNow Protocol Error: %s', 'google-sitemap-generator' ),
$response['response']['message']
);
}
}
}
return $error;
}
public function get_api_key() {
if ( ! empty( $this->api_key ) ) {
return $this->api_key;
}
$api_key = is_multisite() ? get_site_option( self::$api_name ) : get_option( self::$api_name );
if ( $api_key ) {
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
return base64_decode( $api_key );
}
return apply_filters( 'sgg_indexnow_api_key', $api_key );
}
public function set_api_key() {
$api_key = $this->get_api_key();
if ( empty( $api_key ) ) {
$api_key = preg_replace( '[-]', '', wp_generate_uuid4() );
// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
$base64_api_key = base64_encode( $api_key );
if ( is_multisite() ) {
update_site_option( self::$api_name, $base64_api_key );
} else {
update_option( self::$api_name, $base64_api_key );
}
}
$this->api_key = $api_key;
}
public static function delete_api_key() {
if ( is_multisite() ) {
delete_site_option( self::$api_name );
} else {
delete_option( self::$api_name );
}
}
public function set_site_url() {
$this->site_url = get_home_url();
}
public function get_api_key_location() {
return trailingslashit( $this->site_url ) . $this->api_key . '.txt';
}
public function remove_url_scheme( $url ) {
return preg_replace( '/^https?:\/\//', '', $url );
}
}

View File

@@ -0,0 +1,225 @@
<?php
namespace GRIM_SG;
use GRIM_SG\Vendor\QueryBuilder;
abstract class MediaSitemap extends Sitemap {
abstract public function add_urls( string $url, array $media ): void;
abstract public function filter_value( string $value ): bool;
/**
* Add URLS Callback function
*/
public function urlsCallback() {
return 'addMediaUrl';
}
public function get_post_media( int $post_id, string $post_type ): array {
return apply_filters( 'sgg_media_post_urls', array(), $post_id, $post_type );
}
/**
* Collect Media URLs for Sitemap
*/
public function collect_urls( $template = 'sitemap', $inner_sitemap = null, $current_page = null ) {
global $wpdb;
$post_types = array( 'page', 'post' );
$template = sgg_maybe_remove_inner_suffix( $template );
$cache_enabled = ! $this->settings->disable_media_sitemap_cache;
if ( $cache_enabled ) {
$cache = new Cache( "media-$template" );
// Set URLs from cache if available.
$cached_urls = $cache->get();
if ( $cached_urls ) {
$this->urls = $cached_urls;
}
}
$sitemap_key = 'video-sitemap' === $template ? 'video_sitemap' : 'image_sitemap';
foreach ( $post_types as $key => $post_type ) {
if ( isset( $this->settings->{$post_type}->{$sitemap_key} ) && ! $this->settings->{$post_type}->{$sitemap_key} ) {
unset( $post_types[ $key ] );
}
}
if ( sgg_pro_enabled() ) {
foreach ( $this->get_cpt() as $cpt ) {
if ( ! empty( $this->settings->cpt[ $cpt ] ) && ! empty( $this->settings->cpt[ $cpt ]->{$sitemap_key} ) ) {
$post_types[] = $cpt;
}
}
}
$sql_post_types = "('" . implode( "','", $post_types ) . "')";
$multilingual_sql = $this->multilingual_sql( $post_types );
$where_clause = ! empty( $multilingual_sql ) ? 'AND ' : 'WHERE ';
$limit = 5000;
if ( $cache_enabled ) {
$last_mod_time = get_option( $this->get_option_name( $template, 'latest_mod_time' ), '1970-01-01 00:00:00' );
$last_id = get_option( $this->get_option_name( $template, 'latest_post_id' ), 0 );
}
while ( true ) {
$last_mod_sql = '';
$limit_sql = '';
if ( $cache_enabled ) {
$last_mod_sql = $wpdb->prepare(
'AND (posts.post_modified > %s OR (posts.post_modified = %s AND posts.ID > %d))',
$last_mod_time,
$last_mod_time,
$last_id
);
$limit_sql = $wpdb->prepare( 'LIMIT %d', $limit );
}
$sql = "SELECT posts.ID, posts.post_name, posts.post_parent, posts.post_content, posts.post_type, posts.post_modified
FROM {$wpdb->posts} as posts $multilingual_sql $where_clause posts.post_status = 'publish'
AND posts.post_type IN $sql_post_types AND posts.post_password = ''
$last_mod_sql ORDER BY posts.post_modified ASC, posts.ID ASC $limit_sql";
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$posts = $wpdb->get_results( $sql );
// If no posts are returned, then all posts have been processed.
if ( empty( $posts ) ) {
break;
}
// Loop through the posts and add the URLs to the sitemap.
foreach ( $posts as $post ) {
if ( ! apply_filters( 'xml_sitemap_include_post', true, $post->ID ) ) {
continue;
}
$content = apply_filters( 'xml_media_sitemap_post_content', $post->post_content, $post );
if ( ! empty( $content ) && preg_match( '/\[.+?\]/im', $content ) ) {
preg_match_all( '/\[.+?\]/im', $content, $shortcode_matches );
foreach ( $shortcode_matches as $shortcodes ) {
foreach ( $shortcodes as $shortcode ) {
// Skip HTML Sitemap Shortcode
if ( 0 !== strpos( $shortcode, '[html-sitemap' ) ) {
ob_start();
$do_shortcode = do_shortcode( $shortcode );
$output = ob_get_clean();
$final_output = $do_shortcode . $output;
$content = str_replace( $shortcode, $final_output, $content );
}
}
}
}
$media = $this->get_post_media( $post->ID, $post->post_type );
$urls = array();
if ( preg_match_all( '(https?://[-_.!~*()a-zA-Z0-9;/?:@&=+$%#纊-黑亜-熙ぁ-んァ-ヶ]+)', $content, $result ) !== false ) {
$unique_urls = array();
// Remove duplicate Image sizes URLs
foreach ( array_unique( $result[0] ) as $url ) {
$base_url = preg_replace( '/-\d+x\d+(?=\.\w{3,4}$)/', '', $url );
if ( ! isset( $unique_urls[ $base_url ] ) ) {
$unique_urls[ $base_url ] = strtok( $url, '#' );
}
}
$urls = array_values( $unique_urls );
}
$urls = apply_filters( 'sgg_sitemap_post_media_urls', $urls, $post->ID );
if ( ! empty( $urls ) ) {
foreach ( $urls as $url ) {
if ( $this->filter_value( $url ) ) {
$media[] = $url;
}
}
}
if ( ! empty( $media ) ) {
$this->add_urls( get_permalink( $post ), array_unique( $media ) );
}
$last_mod_time = $post->post_modified;
$last_id = $post->ID;
}
if ( $cache_enabled ) {
// Cache the collected URLs.
$cache->set( $this->urls );
// Save the new markers for the latest post processed.
update_option( $this->get_option_name( $template, 'latest_mod_time' ), $last_mod_time );
update_option( $this->get_option_name( $template, 'latest_post_id' ), $last_id );
}
// If fewer posts than the limit were returned, we've reached the final batch.
if ( ! $cache_enabled || count( $posts ) < $limit ) {
break;
}
}
// Set cached URLs to the sitemap URLs.
if ( $cache_enabled ) {
$this->urls = $cache->get();
if ( is_array( $this->urls ) ) {
$this->urls = array_reverse( $this->urls );
} else {
$this->urls = array();
// Delete cache if no URLs are found
self::delete_all_cache();
}
}
// Check Index Sitemap
$links_per_page = $this->settings->links_per_page ?? 1000;
$has_many_links = count( $this->urls ) > $links_per_page;
// Update Media Sitemap Structure option
update_option( "sgg_{$template}_structure", $has_many_links ? 'multiple' : 'single' );
if ( sgg_is_sitemap_index( $template, $this->settings ) && $has_many_links ) {
if ( ! empty( $inner_sitemap ) && ! empty( $current_page ) ) {
$chunks = array_chunk( $this->urls, $links_per_page );
$this->urls = $chunks[ $current_page - 1 ] ?? array();
} else {
$this->urls = array(
str_replace( '-sitemap', '', $template ) =>
array_map(
function ( $chunk ) {
return $chunk[0] ?? array();
},
array_chunk( $this->urls, $links_per_page )
),
);
}
}
}
private function get_option_name( string $template, string $name ): string {
return "sgg_{$template}_{$name}";
}
public static function delete_all_cache(): void {
delete_option( 'sgg_image-sitemap_latest_mod_time' );
delete_option( 'sgg_image-sitemap_latest_post_id' );
delete_option( 'sgg_video-sitemap_latest_mod_time' );
delete_option( 'sgg_video-sitemap_latest_post_id' );
Cache::delete( 'image-sitemap' );
Cache::delete( 'media-image-sitemap' );
Cache::delete( 'video-sitemap' );
Cache::delete( 'media-video-sitemap' );
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace GRIM_SG;
use GRIM_SG\Vendor\SitemapGenerator;
class MultilingualSitemap extends Sitemap {
public static $template = 'multilingual-sitemap';
public function show_sitemap( $template, $is_xml = true, $inner_sitemap = null, $current_page = null ) {
remove_all_filters( 'pre_get_posts' );
$sitemap = new SitemapGenerator( sgg_get_home_url() );
$this->collect_urls( $template, $inner_sitemap, $current_page );
try {
$sitemap->createMultilingualSitemap( $this->urls );
} catch ( \Exception $exc ) {
echo $exc->getTraceAsString(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
try {
$sitemap->outputSitemap( $template, $is_xml, $inner_sitemap );
} catch ( \Exception $exc ) {
echo $exc->getTraceAsString(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}
/**
* Collect Sitemap URLs
*/
public function collect_urls( $template = 'sitemap', $inner_sitemap = null, $current_page = null ) {
$this->urls[] = sgg_get_sitemap_url( $this->settings->sitemap_url, 'sitemap_xml' );
// Polylang, TranslatePress
$languages = sgg_get_languages();
$default_language = sgg_get_default_language_code();
if ( ! empty( $languages ) ) {
foreach ( $languages as $language ) {
$url = sgg_get_sitemap_url( "{$language}/{$this->settings->sitemap_url}", 'sitemap_xml' );
$this->urls[] = ! empty( $default_language )
? str_replace( "/{$default_language}/", '/', $url )
: $url;
}
}
// WPML
if ( defined( 'ICL_SITEPRESS_VERSION' ) ) {
$wpml_languages = apply_filters( 'wpml_active_languages', array() );
foreach ( $wpml_languages as $language ) {
if ( apply_filters( 'wpml_default_language', null ) === $language['code'] ) {
continue;
}
$this->urls[] = esc_url( "{$language['url']}{$this->settings->sitemap_url}" );
}
}
}
}

View File

@@ -0,0 +1,108 @@
<?php
namespace GRIM_SG;
use GRIM_SG\Vendor\Controller;
class Notices extends Controller {
private const RATE = 'sgg_rate';
private const BUY_PRO = 'sgg_buy_pro';
public function __construct() {
add_action( 'admin_init', array( $this, 'init_notices' ) );
add_action( 'wp_ajax_sgg_disable_notice', array( $this, 'disable_notice' ) );
}
public function disable_notice() {
if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'sgg-notice' ) ) {
wp_send_json_error( array( 'message' => esc_html__( 'Invalid nonce.', 'xml-sitemap-generator-for-google' ) ) );
}
$notice = sanitize_text_field( $_POST['notice'] ?? '' );
if ( in_array( $notice, array( self::RATE, self::BUY_PRO ), true ) ) {
update_option( "sgg_disable_notice_{$notice}", true, false );
}
wp_send_json_success();
}
public function init_notices() {
$installation_time = get_option( 'sgg_installation_time' );
if ( ! $installation_time ) {
update_option( 'sgg_installation_time', time() );
} else {
$days = round( ( time() - $installation_time ) / DAY_IN_SECONDS );
$disable_rate = get_option( 'sgg_disable_notice_' . self::RATE, false );
$disable_pro = get_option( 'sgg_disable_notice_' . self::BUY_PRO, false );
add_action( 'current_screen', function ( $screen ) use ( $days, $disable_rate, $disable_pro ) {
if ( strpos( $screen->id, 'xml-sitemap-generator-for-google' ) === false ) {
return;
}
remove_all_actions( 'admin_notices' );
remove_all_actions( 'all_admin_notices' );
if ( $days >= 3 && ! $disable_rate ) {
add_action( 'admin_notices', array( $this, 'rate_notice' ) );
}
if ( $days >= 5 && ! sgg_pro_enabled() && ! $disable_pro ) {
add_action( 'admin_notices', array( $this, 'pro_notice' ) );
}
});
}
}
public function rate_notice() {
$this->enqueue_scripts();
Dashboard::render(
'partials/rate-banner.php',
array(
'label' => esc_html__( 'Hi, thank you for using Google XML Sitemaps Generator!', 'xml-sitemap-generator-for-google' ),
'description' => sprintf(
esc_html__( 'If you like the plugin, please leave us a %s rating. A huge thank you from the team in advance!', 'xml-sitemap-generator-for-google' ),
'<span><a href="' . esc_url( sgg_get_review_url() ) . '" target="_blank">★★★★★</a></span>'
),
'button_text' => esc_html__( 'Yes, Rate Now', 'xml-sitemap-generator-for-google' ),
'button_url' => esc_url( sgg_get_review_url() ),
'data_notice' => self::RATE,
'notice_class' => 'grim-dynamic-notice',
)
);
}
public function pro_notice() {
$this->enqueue_scripts();
Dashboard::render(
'partials/rate-banner.php',
array(
'label' => esc_html__( 'Hi, thank you for using Google XML Sitemaps Generator!', 'xml-sitemap-generator-for-google' ),
'description' => sprintf(
esc_html__( 'If you want to unlock more features, please check out our %s.', 'xml-sitemap-generator-for-google' ),
'<a href="' . esc_url( sgg_get_pro_url( 'notice' ) ) . '" target="_blank">' . esc_html__( 'Pro version', 'xml-sitemap-generator-for-google' ) . '</a>'
),
'button_text' => esc_html__( 'Yes, Read More', 'xml-sitemap-generator-for-google' ),
'button_url' => esc_url( sgg_get_pro_url( 'notice' ) ),
'data_notice' => self::BUY_PRO,
'notice_class' => 'grim-pro-notice grim-dynamic-notice',
)
);
}
public function enqueue_scripts() {
wp_enqueue_script( 'sgg-notices', GRIM_SG_URL . 'assets/js/notices.js', array( 'jquery' ), GRIM_SG_VERSION, true );
wp_localize_script(
'sgg-notices',
'sggNotice',
array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'sgg-notice' ),
)
);
}
}

View File

@@ -0,0 +1,147 @@
<?php
namespace GRIM_SG;
class PostSettings {
public function __construct() {
add_action( 'init', array( $this, 'register_post_meta' ) );
add_action( 'enqueue_block_editor_assets', array( $this, 'register_plugin_sidebar_block' ) );
add_action( 'add_meta_boxes', array( $this, 'add_meta_box' ) );
add_action( 'save_post', array( $this, 'save_meta_box' ) );
add_action( 'edit_attachment', array( $this, 'save_meta_box' ) );
add_filter( 'user_has_cap', array( $this, 'allow_edit_post_meta' ), 10, 3 );
}
/**
* Registers protected Post Meta fields.
*/
public function register_post_meta() {
register_post_meta(
'',
'_sitemap_exclude',
array(
'show_in_rest' => true,
'single' => true,
'type' => 'boolean',
)
);
register_post_meta(
'',
'_sitemap_priority',
array(
'show_in_rest' => true,
'single' => true,
'type' => 'string',
)
);
register_post_meta(
'',
'_sitemap_frequency',
array(
'show_in_rest' => true,
'single' => true,
'type' => 'string',
)
);
}
/**
* Registers Gutenberg Plugin Sidebar Block.
*/
public function register_plugin_sidebar_block() {
$assets_file = GRIM_SG_PATH . '/assets/gutenberg/build/plugin-sidebar.asset.php';
if ( file_exists( $assets_file ) ) {
$assets = include $assets_file;
wp_enqueue_script(
'sitemap-settings',
GRIM_SG_URL . 'assets/gutenberg/build/plugin-sidebar.js',
$assets['dependencies'],
$assets['version'],
true
);
wp_localize_script(
'sitemap-settings',
'sitemapSettings',
array(
'isProEnabled' => sgg_pro_enabled(),
'isExcludeEnabled' => intval( ! apply_filters( 'xml_sitemap_disable_post_meta__exclude_sitemap', false ) ),
'isPriorityEnabled' => intval( ! apply_filters( 'xml_sitemap_disable_post_meta__sitemap_priority', false ) ),
'isFrequencyEnabled' => intval( ! apply_filters( 'xml_sitemap_disable_post_meta__sitemap_frequency', false ) ),
)
);
}
}
/**
* Registers Post Metabox.
*/
public function add_meta_box() {
// Disable Meta Box fields.
if ( apply_filters( 'xml_sitemap_disable_post_meta_fields', false ) ) {
return;
}
$public_post_types = get_post_types(
array(
'public' => true,
)
);
foreach ( $public_post_types as $post_type ) {
add_meta_box(
'sgg_pro_meta_box',
esc_html__( 'XML Sitemap Options', 'xml-sitemap-generator-for-google' ),
array( $this, 'meta_box_render' ),
$post_type,
);
}
}
/**
* Renders Post Metabox.
*/
public function meta_box_render( $post ) {
load_template(
GRIM_SG_PATH . '/templates/post-meta-box.php',
false,
compact( 'post' )
);
}
/**
* Saves Post Metabox.
*/
public function save_meta_box( $post_id ) {
if ( ! isset( $_POST['sgg_pro_meta_box_nonce'] ) || ! wp_verify_nonce( $_POST['sgg_pro_meta_box_nonce'], 'sgg_pro_meta_box' ) ) {
return;
}
if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || ! current_user_can( 'edit_posts' ) || ! sgg_pro_enabled() ) {
return;
}
update_post_meta( $post_id, '_sitemap_exclude', $_POST['_sitemap_exclude'] ?? '' );
if ( isset( $_POST['_sitemap_priority'] ) ) {
update_post_meta( $post_id, '_sitemap_priority', sanitize_text_field( $_POST['_sitemap_priority'] ) );
}
if ( isset( $_POST['_sitemap_frequency'] ) ) {
update_post_meta( $post_id, '_sitemap_frequency', sanitize_text_field( $_POST['_sitemap_frequency'] ) );
}
}
/**
* Allow editing protected Post Meta fields.
*/
public function allow_edit_post_meta( $allcaps, $caps, $args ) {
if ( ! empty( $args[0] ) && ! empty( $args[3] ) && 'edit_post_meta' === $args[0]
&& in_array( $args[3], array( '_sitemap_exclude', '_sitemap_priority', '_sitemap_frequency' ), true ) ) {
$allcaps['edit_post_meta'] = current_user_can( 'edit_posts' );
}
return $allcaps;
}
}

View File

@@ -0,0 +1,906 @@
<?php
namespace GRIM_SG;
use GRIM_SG\Vendor\Controller;
use GRIM_SG\Vendor\QueryBuilder;
use GRIM_SG\Vendor\SitemapGenerator;
class Sitemap extends Controller {
public static $template = 'sitemap';
public $urls = array();
public $settings;
public function __construct() {
$this->settings = $this->get_settings();
}
/**
* Generate Sitemap
*/
public function show_sitemap( $template, $is_xml = true, $inner_sitemap = null, $current_page = null ) {
if ( sgg_is_sitemap_index( $template, $this->settings ) && ! empty( $inner_sitemap ) ) {
if ( in_array( $template, array( ImageSitemap::$template, VideoSitemap::$template ), true ) ) {
$template .= '-inner-sitemap';
} else {
$template = 'inner-sitemap';
}
}
$sitemap = $this->generate_sitemap( $template, $is_xml, $inner_sitemap, $current_page );
try {
$sitemap->outputSitemap( $template, $is_xml, $inner_sitemap );
} catch ( \Exception $exc ) {
echo $exc->getTraceAsString(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
}
/**
* Generate Sitemap
*/
public function generate_sitemap( $template = 'sitemap', $is_xml = true, $inner_sitemap = null, $current_page = null ) {
remove_all_filters( 'pre_get_posts' );
$sitemap = new SitemapGenerator( sgg_get_home_url() );
if ( $this->settings->enable_cache ) {
$cache = new Cache( $template, $inner_sitemap, $current_page );
$urls = $cache->get();
if ( $urls ) {
$this->urls = $urls;
} else {
$this->collect_urls( $template, $inner_sitemap, $current_page );
$cache->set( $this->urls );
}
} else {
$this->collect_urls( $template, $inner_sitemap, $current_page );
}
$sitemap->addUrls( apply_filters( 'sgg_sitemap_urls', $this->urls ), $this->urlsCallback(), $template );
try {
$sitemap->createSitemap( $template, $this->extraSitemapHeader(), $is_xml );
} catch ( \Exception $exc ) {
echo $exc->getTraceAsString(); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
}
return $sitemap;
}
/**
* Generate Sitemap XSL Template
*/
public static function generate_sitemap_xsl( $template = 'sitemap' ) {
ob_get_clean();
header( 'Content-Type: text/xsl; charset=utf-8' );
header( 'X-Robots-Tag: noindex' );
ob_start();
global $wp_version;
if ( version_compare( $wp_version, '5.5.0', '<' ) ) {
set_query_var( 'args', array( 'template' => $template ) );
}
load_template(
GRIM_SG_PATH . '/templates/xsl/sitemap.php',
false,
compact( 'template' )
);
ob_end_flush();
}
/**
* Get Sitemap Table by Template
*/
public static function get_sitemap_table( $template = 'sitemap', $args = array() ) {
switch ( $template ) {
case GoogleNews::$template:
$table = 'google-news';
break;
case ImageSitemap::$template:
$table = 'image-sitemap';
break;
case VideoSitemap::$template:
$table = 'video-sitemap';
break;
case 'sitemap-index':
case MultilingualSitemap::$template:
$table = 'sitemap-index';
break;
default:
$table = 'sitemap';
break;
}
load_template( GRIM_SG_PATH . "/templates/xsl/tables/{$table}.php", false, $args );
}
/**
* Get Sitemap Title by Template
*/
public static function get_sitemap_title( $template ) {
$title = __( 'Sitemap', 'xml-sitemap-generator-for-google' );
switch ( $template ) {
case \GRIM_SG\GoogleNews::$template:
$title = __( 'Google News', 'xml-sitemap-generator-for-google' );
break;
case \GRIM_SG\ImageSitemap::$template:
$title = __( 'Image Sitemap', 'xml-sitemap-generator-for-google' );
break;
case \GRIM_SG\VideoSitemap::$template:
$title = __( 'Video Sitemap', 'xml-sitemap-generator-for-google' );
break;
case \GRIM_SG\MultilingualSitemap::$template:
$title = __( 'Multilingual Sitemap', 'xml-sitemap-generator-for-google' );
break;
case 'inner-sitemap':
$title = __( 'Sitemap', 'xml-sitemap-generator-for-google' );
break;
case 'sitemap-index':
$title = __( 'Sitemap Index', 'xml-sitemap-generator-for-google' );
break;
}
return $title;
}
/**
* Add URLS Callback function
*/
public function urlsCallback() {
return 'addUrl';
}
/**
* Adding Google News Sitemap Headers
*/
public function extraSitemapHeader() {
return array();
}
/**
* Collect Sitemap URLs
*/
public function collect_urls( $template = 'sitemap', $inner_sitemap = null, $current_page = null ) {
if ( $this->settings->home->include && ! $this->settings->page->include ) {
$this->add_home();
}
if ( sgg_is_sitemap_index( $template, $this->settings ) ) {
$current_page = ! empty( $current_page ) ? intval( $current_page ) - 1 : $current_page;
if ( 'sitemap' === $template ) {
$post_types = $this->get_post_types_list( array( 'page', 'post' ), $this->settings );
foreach ( $post_types as $post_type ) {
$this->add_posts( $post_type, $current_page, true );
}
$this->add_categories( $current_page, true );
$this->add_authors( $current_page, true );
$this->add_archives( true );
$this->add_media( $current_page, true );
$this->add_additional_pages( true );
} else {
switch ( $inner_sitemap ) {
case 'category':
$this->add_categories( $current_page );
break;
case 'author':
$this->add_authors( $current_page );
break;
case 'archive':
$this->add_archives();
break;
case 'media':
$this->add_media( $current_page );
break;
case 'additional':
$this->add_additional_pages();
break;
default:
$this->add_posts( $inner_sitemap, $current_page );
break;
}
}
} else {
$this->add_posts();
$this->add_not_translatable_posts(); // Add Not Translatable Post Types to All Sitemaps
$this->add_categories();
$this->add_authors();
$this->add_archives();
$this->add_media();
$this->add_additional_pages();
}
}
/**
* Add Home Page to Sitemap
*/
public function add_home() {
$home = $this->settings->home;
if ( $home->include ) {
$front_page_id = get_option( 'page_on_front' );
$last_modified = ( $front_page_id ) ? get_post_modified_time( DATE_W3C, false, $front_page_id ) : gmdate( 'c' );
$this->add_url(
sgg_get_home_url_with_trailing_slash(),
$home->priority,
$home->frequency,
$last_modified,
'page'
);
}
}
/**
* Add all Posts to Sitemap
*/
public function add_posts( $post_type = null, $current_page = null, $is_sitemap_index = false ) {
global $wpdb;
$front_page_id = get_option( 'page_on_front' );
$exclude_post_ids = apply_filters( 'sgg_sitemap_exclude_post_ids', apply_filters( 'sgg_sitemap_exclude_ids', array(), $this->settings->exclude_posts ?? '' ) );
$exclude_term_ids = apply_filters( 'sgg_sitemap_exclude_term_ids', apply_filters( 'sgg_sitemap_exclude_ids', array(), $this->settings->exclude_terms ?? '' ) );
$include_term_ids = apply_filters( 'sgg_sitemap_include_only_term_ids', apply_filters( 'sgg_sitemap_exclude_ids', array(), $this->settings->include_only_terms ?? '' ) );
$per_page = intval( $this->settings->links_per_page ?? 1000 );
if ( ! empty( $front_page_id ) ) {
$exclude_post_ids[] = $front_page_id;
}
$exclude_posts_sql = '';
if ( ! empty( $exclude_post_ids ) ) {
$exclude_posts_sql = 'AND posts.ID NOT IN (' . implode( ',', array_unique( $exclude_post_ids ) ) . ')';
}
$terms_join_sql = '';
$terms_where_sql = '';
if ( ! empty( $include_term_ids ) ) {
$terms_join_sql = "INNER JOIN (
SELECT DISTINCT tr.object_id
FROM {$wpdb->term_relationships} tr
INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
WHERE tt.term_id IN (" . implode( ',', array_unique( $include_term_ids ) ) . ')
) included_posts ON posts.ID = included_posts.object_id';
} elseif ( ! empty( $exclude_term_ids ) ) {
$terms_join_sql = "LEFT JOIN (
SELECT DISTINCT tr.object_id
FROM {$wpdb->term_relationships} tr
INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_taxonomy_id = tr.term_taxonomy_id
WHERE tt.term_id IN (" . implode( ',', array_unique( $exclude_term_ids ) ) . ')
) excluded_posts ON posts.ID = excluded_posts.object_id';
$terms_where_sql = ' AND excluded_posts.object_id IS NULL';
}
if ( ! empty( $post_type ) ) {
if ( ( isset( $this->settings->{$post_type}->include ) && $this->settings->{$post_type}->include )
|| ( ! empty( $this->settings->cpt[ $post_type ] ) && ! empty( $this->settings->cpt[ $post_type ]->include ) ) ) {
$post_types = array( $post_type );
}
} else {
$post_types = $this->get_post_types_list( array( 'page', 'post' ), $this->settings );
}
if ( empty( $post_types ) ) {
return;
}
if ( in_array( 'page', $post_types, true ) && 1 > $current_page ) {
$this->add_home();
}
$sql_extra_select = '';
$sql_post_types = "('" . implode( "','", $post_types ) . "')";
$multilingual_sql = $this->multilingual_sql( $post_types );
$where_clause = ! empty( $multilingual_sql ) ? 'AND ' : 'WHERE ';
if ( sgg_pro_enabled() && ! empty( $this->settings->posts_priority ) ) {
$sql_extra_select .= ', posts.comment_count';
}
$sql = "SELECT
posts.ID,
posts.post_name,
posts.post_parent,
posts.post_type,
posts.post_date,
posts.post_modified
$sql_extra_select
FROM $wpdb->posts as posts
$terms_join_sql
$multilingual_sql
$where_clause posts.post_status = 'publish' AND posts.post_type IN $sql_post_types AND posts.post_password = ''
$exclude_posts_sql
$terms_where_sql
GROUP BY posts.ID
ORDER BY posts.post_modified DESC";
if ( is_null( $current_page ) && ! $is_sitemap_index ) {
$this->add_post_urls( $sql, false );
return;
}
if ( $is_sitemap_index ) {
// Calculate total number of posts
$total_posts_sql = "SELECT COUNT(*) FROM $wpdb->posts as posts
$terms_join_sql
$multilingual_sql
$where_clause posts.post_status = 'publish' AND posts.post_type IN $sql_post_types AND posts.post_password = ''
$exclude_posts_sql
$terms_where_sql
ORDER BY posts.post_modified DESC";
$total_posts = $wpdb->get_var( $total_posts_sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
// Calculate the number of chunks
$num_chunks = ceil( $total_posts / $per_page );
for ( $chunk_index = 0; $chunk_index < $num_chunks; $chunk_index++ ) {
$offset = $chunk_index * $per_page;
$chunk_sql = $sql . " LIMIT $offset, 1";
$this->add_post_urls( $chunk_sql, true );
}
} else {
$offset = $current_page * $per_page;
$sql .= " LIMIT $offset, $per_page";
$this->add_post_urls( $sql, false );
}
}
/**
* Add Post URLs to Sitemap
*/
public function add_post_urls( $sql, $is_sitemap_index ) {
$priority_provider = $this->get_posts_priority_provider();
$posts = QueryBuilder::run_query( $sql );
foreach ( $posts as $post ) {
if ( $is_sitemap_index || apply_filters( 'xml_sitemap_include_post', true, $post->ID ) ) {
$this->add_url(
get_permalink( $post ),
( null !== $priority_provider && 'post' === $post->post_type )
? apply_filters( 'sitemap_post_priority', $priority_provider->get_post_priority( $post->comment_count ), $post->ID )
: apply_filters( 'sitemap_post_priority', $this->get_post_settings( $post->post_type, 'priority' ), $post->ID ),
apply_filters( 'sitemap_post_frequency', $this->get_post_settings( $post->post_type, 'frequency' ), $post->ID ),
gmdate( DATE_W3C, strtotime( $post->post_modified ) ),
$post->post_type
);
}
}
}
/**
* Add all Categories & Tags
*/
public function add_categories( $current_page = null, $is_sitemap_index = false ) {
global $wpdb;
$taxonomy_types = array();
$per_page = intval( $this->settings->links_per_page ?? 1000 );
foreach ( $this->get_taxonomy_types( 'names' ) as $taxonomy_type ) {
if ( ! empty( $this->settings->taxonomies[ $taxonomy_type ] ) && $this->settings->taxonomies[ $taxonomy_type ]->include ) {
$taxonomy_types[] = $taxonomy_type;
}
}
if ( empty( $taxonomy_types ) ) {
return;
}
$exclude_term_ids = apply_filters( 'sgg_sitemap_exclude_ids', array(), $this->settings->exclude_terms ?? '' );
$include_term_ids = apply_filters( 'sgg_sitemap_exclude_ids', array(), $this->settings->include_only_terms ?? '' );
$terms_where_sql = '';
if ( ! empty( $include_term_ids ) ) {
$terms_where_sql = 'AND terms.term_id IN (' . implode( ',', array_unique( $include_term_ids ) ) . ')';
} elseif ( ! empty( $exclude_term_ids ) ) {
$terms_where_sql = 'AND terms.term_id NOT IN (' . implode( ',', array_unique( $exclude_term_ids ) ) . ')';
}
$post_types = $this->get_post_types_list( array( 'post' ), $this->settings );
$sql_post_types = "('" . implode( "','", $post_types ) . "')";
$sql_taxonomies = "('" . implode( "','", $taxonomy_types ) . "')";
$multilingual_sql = $this->multilingual_sql( $taxonomy_types, true );
$where_clause = ! empty( $multilingual_sql ) ? 'AND ' : 'WHERE ';
$terms_query = "
SELECT terms.*, term_taxonomy.taxonomy, term_taxonomy.count, (
SELECT MAX(p.post_modified)
FROM $wpdb->posts AS p
INNER JOIN $wpdb->term_relationships AS tr ON p.ID = tr.object_id
WHERE p.post_type IN $sql_post_types
AND p.post_status = 'publish'
AND tr.term_taxonomy_id = term_taxonomy.term_taxonomy_id
) AS post_modified
FROM $wpdb->terms AS terms
INNER JOIN $wpdb->term_taxonomy AS term_taxonomy ON terms.term_id = term_taxonomy.term_id
$multilingual_sql
$where_clause term_taxonomy.taxonomy IN $sql_taxonomies
$terms_where_sql
AND term_taxonomy.count > 0
GROUP BY terms.term_id
ORDER BY post_modified DESC, terms.name ASC
";
if ( is_null( $current_page ) && ! $is_sitemap_index ) {
$this->add_category_urls( $terms_query, false );
return;
}
if ( $is_sitemap_index ) {
// Calculate total number of terms
$total_terms_sql = "
SELECT COUNT(*) FROM $wpdb->terms AS terms
INNER JOIN $wpdb->term_taxonomy AS term_taxonomy ON terms.term_id = term_taxonomy.term_id
$multilingual_sql
$where_clause term_taxonomy.taxonomy IN $sql_taxonomies
$terms_where_sql
AND term_taxonomy.count > 0
";
$total_terms = $wpdb->get_var( $total_terms_sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
// Calculate the number of chunks
$num_chunks = ceil( $total_terms / $per_page );
for ( $chunk_index = 0; $chunk_index < $num_chunks; $chunk_index++ ) {
$offset = $chunk_index * $per_page;
$chunk_sql = $terms_query . " LIMIT $offset, 1";
$this->add_category_urls( $chunk_sql, true );
}
} else {
$offset = $current_page * $per_page;
$terms_query .= " LIMIT $offset, $per_page";
$this->add_category_urls( $terms_query, false );
}
}
/**
* Add Category URLs to Sitemap
*/
public function add_category_urls( $terms_query, $is_sitemap_index ) {
global $wpdb;
$terms = $wpdb->get_results( $terms_query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
foreach ( $terms as $term ) {
if ( ! $is_sitemap_index && apply_filters( 'sgg_sitemap_exclude_single_term', false, intval( $term->term_id ), $term->taxonomy ) ) {
continue;
}
$term_modified = ! empty( $term->post_modified )
? strtotime( $term->post_modified )
: time();
$this->add_url(
get_term_link( $term ),
apply_filters( 'sitemap_term_priority', $this->get_taxonomy_settings( $term->taxonomy, 'priority' ), $term->term_id ),
apply_filters( 'sitemap_term_frequency', $this->get_taxonomy_settings( $term->taxonomy, 'frequency' ), $term->term_id ),
gmdate( DATE_W3C, $term_modified ),
'category'
);
}
}
/**
* Add all Authors of Posts
*/
public function add_authors( $current_page = null, $is_sitemap_index = false ) {
if ( ! $this->settings->authors->include ) {
return;
}
$args = array(
'has_published_posts' => $this->get_post_types_list( array( 'post' ), $this->settings ),
'fields' => 'ids',
'orderby' => 'post_count',
'order' => 'DESC',
'number' => -1,
);
if ( is_null( $current_page ) && ! $is_sitemap_index ) {
$this->add_author_urls( $args );
return;
}
$per_page = intval( $this->settings->links_per_page ?? 1000 );
if ( $is_sitemap_index ) {
$authors_query = new \WP_User_Query( $args );
if ( empty( $authors_query->get_total() ) ) {
return;
}
$num_chunks = ceil( $authors_query->get_total() / $per_page );
for ( $chunk_index = 0; $chunk_index < $num_chunks; $chunk_index++ ) {
$offset = $chunk_index * $per_page;
$args = array_merge(
$args,
array(
'number' => 1,
'offset' => $offset,
)
);
$this->add_author_urls( $args );
}
} else {
$offset = $current_page * $per_page;
$args = array_merge(
$args,
array(
'number' => $per_page,
'offset' => $offset,
)
);
$this->add_author_urls( $args );
}
}
public function add_author_urls( $args ) {
$authors_query = new \WP_User_Query( $args );
$authors = $authors_query->get_results();
if ( ! empty( $authors ) ) {
global $wpdb;
foreach ( $authors as $author_id ) {
$latest_post_query = "SELECT ID, post_modified
FROM {$wpdb->posts}
WHERE post_author = {$author_id}
AND post_type = 'post' AND post_status = 'publish'
ORDER BY post_modified DESC
LIMIT 1";
$latest_post = $wpdb->get_row( $latest_post_query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$modified_time = ! empty( $latest_post->post_modified )
? gmdate( DATE_W3C, strtotime( $latest_post->post_modified ) )
: gmdate( DATE_W3C );
$this->add_url(
get_author_posts_url( $author_id ),
$this->settings->authors->priority,
$this->settings->authors->frequency,
$modified_time,
'author'
);
}
}
}
/**
* Add all Archives
*/
public function add_archives( $is_sitemap_index = false ) {
global $wpdb;
$sql_multilingual = $this->multilingual_sql( array( 'post' ) );
$where_clause = ! empty( $sql_multilingual ) ? 'AND ' : 'WHERE ';
$sql = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, post_date AS post_date, count(ID) as count_posts
FROM $wpdb->posts as posts
$sql_multilingual
$where_clause post_type = 'post' AND post_status = 'publish' AND post_password = ''
GROUP BY YEAR(post_date), MONTH(post_date)
ORDER BY post_date DESC";
if ( $is_sitemap_index ) {
$sql .= ' LIMIT 1';
}
$archives = QueryBuilder::run_query( $sql );
foreach ( $archives as $archive ) {
$option = ( gmdate( 'n' ) === $archive->month && gmdate( 'Y' ) === $archive->year ) ? 'archive' : 'archive_older';
if ( $this->settings->{$option}->include ) {
$this->add_url(
get_month_link( $archive->year, $archive->month ),
$this->settings->{$option}->priority,
$this->settings->{$option}->frequency,
gmdate( DATE_W3C, strtotime( $archive->post_date ) ),
'archive'
);
}
}
}
/**
* Add Media Pages (Attachments)
*/
public function add_media( $current_page = null, $is_sitemap_index = false ) {
if ( ! $this->settings->media->include ) {
return;
}
if ( ! get_option( 'wp_attachment_pages_enabled' ) ) {
return;
}
global $wpdb;
$per_page = intval( $this->settings->links_per_page ?? 1000 );
$multilingual_sql = $this->multilingual_sql( array( 'attachment' ) );
$where_clause = ! empty( $multilingual_sql ) ? 'AND ' : 'WHERE ';
$sql = "SELECT
posts.ID,
posts.post_name,
posts.post_parent,
posts.post_type,
posts.post_date,
posts.post_modified
FROM $wpdb->posts as posts
$multilingual_sql
$where_clause posts.post_status = 'inherit' AND posts.post_type = 'attachment' AND posts.post_password = ''
GROUP BY posts.ID
ORDER BY posts.post_modified DESC";
if ( is_null( $current_page ) && ! $is_sitemap_index ) {
$this->add_media_urls( $sql, false );
return;
}
if ( $is_sitemap_index ) {
// Calculate total number of attachments
$total_media_sql = "SELECT COUNT(*) FROM $wpdb->posts as posts
$multilingual_sql
$where_clause posts.post_status = 'inherit' AND posts.post_type = 'attachment' AND posts.post_password = ''
ORDER BY posts.post_modified DESC";
$total_media = $wpdb->get_var( $total_media_sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
// Calculate the number of chunks
$num_chunks = ceil( $total_media / $per_page );
for ( $chunk_index = 0; $chunk_index < $num_chunks; $chunk_index++ ) {
$offset = $chunk_index * $per_page;
$chunk_sql = $sql . " LIMIT $offset, 1";
$this->add_media_urls( $chunk_sql, true );
}
} else {
$offset = $current_page * $per_page;
$sql .= " LIMIT $offset, $per_page";
$this->add_media_urls( $sql, false );
}
}
/**
* Add Media URLs to Sitemap
*/
public function add_media_urls( $sql, $is_sitemap_index ) {
$attachments = QueryBuilder::run_query( $sql );
foreach ( $attachments as $attachment ) {
if ( $is_sitemap_index || apply_filters( 'xml_sitemap_include_post', true, $attachment->ID ) ) {
$attachment_link = get_attachment_link( $attachment->ID );
// Only add if attachment link is valid (attachment pages are enabled)
if ( $attachment_link ) {
$this->add_url(
$attachment_link,
apply_filters( 'sitemap_media_priority', apply_filters( 'sitemap_post_priority', $this->settings->media->priority, $attachment->ID ), $attachment->ID ),
apply_filters( 'sitemap_media_frequency', apply_filters( 'sitemap_post_frequency', $this->settings->media->frequency, $attachment->ID ), $attachment->ID ),
gmdate( DATE_W3C, strtotime( $attachment->post_modified ) ),
'media'
);
}
}
}
}
/**
* Add Additional Pages
*/
public function add_additional_pages( $is_sitemap_index = false ) {
$pages = $this->settings->additional_pages;
if ( empty( $pages ) ) {
return;
}
if ( $is_sitemap_index ) {
usort(
$pages,
function ( $a, $b ) {
return strtotime( $b['lastmod'] ) - strtotime( $a['lastmod'] );
}
);
$pages = array( $pages[0] );
}
foreach ( $pages as $page ) {
if ( empty( $page['url'] ) ) {
continue;
}
$last_modified = ! empty( $page['lastmod'] )
? gmdate( DATE_W3C, strtotime( $page['lastmod'] ) )
: gmdate( DATE_W3C );
$this->add_url(
$page['url'],
$page['priority'],
$page['frequency'],
$last_modified,
'additional'
);
}
}
/**
* Add Sitemap Url
*
* @param $url
* @param $priority
* @param $frequency
* @param string $last_modified
*/
public function add_url( $url, $priority, $frequency, $last_modified = '', $inner_sitemap = '' ) {
$item = array(
$url, // URL
$last_modified, // Last Modified
$frequency, // Frequency
number_format( floatval( $priority / 10 ), 1, '.', '' ), // Priority
);
if ( sgg_is_sitemap_index( 'sitemap', $this->settings ) ) {
$this->urls[ $inner_sitemap ][] = $item;
} else {
$this->urls[] = $item;
}
}
public function multilingual_sql( $element_types, $is_taxonomy = false ) {
global $wpdb;
$multilingual_sql = '';
$element_id_column = $is_taxonomy ? 'term_taxonomy.term_taxonomy_id' : 'posts.ID';
if ( function_exists( 'pll_languages_list' ) && ! empty( $GLOBALS['polylang'] ) ) {
global $polylang;
$current_language = pll_current_language();
$polylang_model = $polylang->model;
$lang_model = $polylang_model->get_language( $current_language );
$tax_language = $is_taxonomy ? $polylang_model->term->get_tax_language() : $polylang_model->post->get_tax_language();
$polylang_term_ids = array();
foreach ( $element_types as $key => $element_type ) {
$translatable = $is_taxonomy
? $polylang_model->is_translated_taxonomy( $element_type )
: $polylang_model->is_translated_post_type( $element_type );
if ( ! $translatable ) {
continue;
}
if ( $lang_model ) {
$polylang_term_ids[] = absint( $lang_model->get_tax_prop( $tax_language, 'term_taxonomy_id' ) );
}
}
if ( ! empty( $polylang_term_ids ) ) {
$polylang_term_ids = '(' . implode( ',', array_unique( $polylang_term_ids ) ) . ')';
$multilingual_sql = "INNER JOIN {$wpdb->term_relationships} AS pll_tr ON $element_id_column = pll_tr.object_id WHERE ( pll_tr.term_taxonomy_id IN $polylang_term_ids )";
}
}
if ( defined( 'ICL_SITEPRESS_VERSION' ) && ! empty( $element_types ) ) {
$current_language = apply_filters( 'wpml_current_language', null );
$element_type = $is_taxonomy ? 'tax' : 'post';
$wpml_element_types = array_map(
function ( $post_type ) use ( $element_type ) {
return "{$element_type}_{$post_type}";
},
$element_types
);
$sql_element_types = "('" . implode( "','", $wpml_element_types ) . "')";
$multilingual_sql = "INNER JOIN {$wpdb->prefix}icl_translations AS translations ON $element_id_column = translations.element_id
WHERE translations.language_code = '$current_language' AND translations.element_type IN $sql_element_types";
}
return $multilingual_sql;
}
public function add_not_translatable_posts() {
// Add Polylang Not Translatable Post Types
if ( function_exists( 'pll_languages_list' ) && ! empty( $GLOBALS['polylang'] ) ) {
$options = get_option( 'polylang' );
$post_types = $this->get_post_types_list( array(), $this->settings );
$not_translatable = ! empty( $options['post_types'] ) && ! empty( $post_types )
? array_diff( $post_types, $options['post_types'] )
: array();
if ( ! empty( $not_translatable ) ) {
foreach ( $not_translatable as $post_type ) {
$this->add_posts( $post_type );
}
}
}
}
/**
* Posts Priority
*/
public function get_posts_priority_provider() {
$class_name = str_replace( '/', '\\', $this->settings->posts_priority ?? '' );
return class_exists( $class_name ) ? new $class_name( $this->get_comments_count(), $this->get_posts_count() ) : null;
}
public function get_comments_count() {
global $wpdb;
$cache_key = self::$slug . '_comments_count';
$comments_count = wp_cache_get( $cache_key, self::$slug );
if ( false === $comments_count ) {
$comments_count = $wpdb->get_var( "SELECT COUNT(*) as `comments_count` FROM {$wpdb->comments} WHERE `comment_approved`='1'" );
wp_cache_set( $cache_key, $comments_count, self::$slug, 20 );
}
return $comments_count;
}
public function get_posts_count() {
global $wpdb;
$cache_key = self::$slug . '_posts_count';
$posts_count = wp_cache_get( $cache_key, self::$slug );
if ( false === $posts_count ) {
$posts_count = $wpdb->get_var( "SELECT COUNT(*) FROM {$wpdb->posts} p WHERE p.post_password = '' AND p.post_type = 'post' AND p.post_status = 'publish' " );
wp_cache_set( $cache_key, $posts_count, self::$slug, 20 );
}
return $posts_count;
}
/**
* Get Post Field Value
*
* @param $post_type
* @param $field
* @return mixed
*/
public function get_post_settings( $post_type, $field ) {
if ( ! empty( $this->settings->cpt ) && in_array( $post_type, array_keys( $this->settings->cpt ), true ) ) {
return $this->settings->cpt[ $post_type ]->{$field} ?? null;
}
return $this->settings->{$post_type}->{$field} ?? null;
}
/**
* Get Taxonomy Field Value
*
* @param $taxonomy_type
* @param $field
* @return mixed
*/
public function get_taxonomy_settings( $taxonomy_type, $field ) {
if ( ! empty( $this->settings->taxonomies ) && in_array( $taxonomy_type, array_keys( $this->settings->taxonomies ), true ) ) {
return $this->settings->taxonomies[ $taxonomy_type ]->{$field} ?? null;
}
return $this->settings->{$taxonomy_type}->{$field} ?? null;
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace GRIM_SG;
if ( ! defined( 'WP_CLI' ) || ! WP_CLI ) {
return;
}
use \WP_CLI;
class SitemapCLI {
/**
* Generate a sitemap.
*
* ## OPTIONS
*
* [--template=<template>]
* : The sitemap template to use. Default: 'sitemap'
*
* ## EXAMPLES
*
* # Generate a sitemap
* $ wp sitemap generate
*
* # Generate a specific Sitemap type
* $ wp sitemap generate --template=sitemap
* $ wp sitemap generate --template=image-sitemap
* $ wp sitemap generate --template=video-sitemap
* $ wp sitemap generate --template=google-news
*
* @param array $args Command arguments
* @param array $assoc_args Command associative arguments
*/
public function generate( $args, $assoc_args ) {
$template = $assoc_args['template'] ?? 'sitemap';
$inner_sitemap = null;
$current_page = null;
$allowed_templates = array(
'sitemap',
'image-sitemap',
'video-sitemap',
'google-news',
);
if ( ! in_array( $template, $allowed_templates, true ) ) {
WP_CLI::error( 'Invalid Sitemap Template. Allowed templates: ' . implode( ', ', $allowed_templates ) );
}
// Initialize the Sitemap class
$sitemap = null;
if ( 'sitemap' === $template ) {
$sitemap = new Sitemap();
} elseif ( 'image-sitemap' === $template ) {
$sitemap = new ImageSitemap();
} elseif ( 'video-sitemap' === $template ) {
$sitemap = new VideoSitemap();
} elseif ( 'google-news' === $template ) {
$sitemap = new GoogleNews();
}
if ( ! $sitemap ) {
WP_CLI::error( 'Sitemap class not found.' );
}
if ( $sitemap->settings->enable_cache ) {
$cache = new Cache( $template, $inner_sitemap, $current_page );
$sitemap->collect_urls( $template, $inner_sitemap, $current_page );
$cache->set( $sitemap->urls );
} else {
$sitemap->collect_urls( $template, $inner_sitemap, $current_page );
}
WP_CLI::success(
"Sitemap generated successfully for template: {$template}. URLs count: " . count( $sitemap->urls )
);
}
}
// Register the command
WP_CLI::add_command( 'sitemap', 'GRIM_SG\SitemapCLI' );

View File

@@ -0,0 +1,53 @@
<?php
namespace GRIM_SG;
use GRIM_SG\Vendor\Controller;
class Tools extends Controller {
public function __construct() {
add_action( 'transition_post_status', array( $this, 'transition_post_status' ), 100, 3 );
}
public function transition_post_status( $new_status, $old_status, $post ) {
$settings = $this->get_settings();
// Ping IndexNow
if ( $settings->enable_indexnow && 'publish' === $new_status ) {
( new IndexNow() )->ping_url( get_permalink( $post ) );
}
}
public static function run_tools_actions( $data = array() ) {
if ( ! empty( $data['sgg-indexnow'] ) ) {
$response = ( new IndexNow() )->ping_site_url();
add_settings_error(
Controller::$slug,
'indexnow_notice',
$response['message'],
$response['status']
);
return true;
} elseif ( ! empty( $data['sgg-flush-rewrite-rules'] ) ) {
Frontend::activate_plugin();
self::add_admin_notice( __( 'WordPress Rewrite Rules flushed.', 'xml-sitemap-generator-for-google' ) );
return true;
} elseif ( ! empty( $data['sgg-clear-cache'] ) ) {
Cache::clear();
self::add_admin_notice( __( 'Sitemaps Cache cleared.', 'xml-sitemap-generator-for-google' ) );
return true;
}
return false;
}
public static function add_admin_notice( $message ) {
add_settings_error( Controller::$slug, 'sitemap_tools', $message, 'success' );
}
}

View File

@@ -0,0 +1,133 @@
<?php
namespace GRIM_SG;
use SGG_PRO\Classes\Video_Sitemap;
class VideoSitemap extends MediaSitemap {
public static $template = 'video-sitemap';
/**
* Adding Google News Sitemap Headers
*/
public function extraSitemapHeader() {
return array( 'xmlns:video' => 'http://www.google.com/schemas/sitemap-video/1.1' );
}
public function add_urls( string $url, array $media ): void {
$videos = array();
foreach ( $media as $video ) {
$extensions = explode( '.', $video );
$extension = end( $extensions );
if ( 'video' === wp_ext2type( $extension ) ) {
$attachment_id = attachment_url_to_postid( $video );
if ( $attachment_id ) {
$thumbnail = get_the_post_thumbnail_url( $attachment_id, 'thumbnail' );
$metadata = get_post_meta( $attachment_id, '_wp_attachment_metadata', true );
$videos[] = array(
'thumbnail' => ! empty( $thumbnail ) ? $thumbnail : trailingslashit( includes_url() ) . 'images/media/video.png',
'title' => get_the_title( $attachment_id ),
'description' => wp_get_attachment_caption( $attachment_id ),
'player_loc' => $video,
'duration' => $metadata['length'] ?? '',
);
}
} elseif ( sgg_pro_enabled() && class_exists( 'SGG_PRO\Classes\Video_Sitemap' ) ) {
if ( ! empty( $this->settings->youtube_api_key ) && $this->is_youtube_url( $video ) ) {
$youtube_data = Video_Sitemap::get_youtube_data( $video, $this->settings->youtube_api_key, $this->settings->enable_video_api_cache );
if ( ! empty( $youtube_data ) ) {
$videos[] = $youtube_data;
}
} elseif ( ! empty( $this->settings->vimeo_api_key ) && $this->is_vimeo_url( $video ) ) {
$vimeo_data = Video_Sitemap::get_vimeo_data( $video, $this->settings->vimeo_api_key, $this->settings->enable_video_api_cache );
if ( ! empty( $vimeo_data ) ) {
$videos[] = $vimeo_data;
}
} elseif ( $this->is_twitter_url( $video ) ) {
$twitter_data = Video_Sitemap::get_twitter_data( $video, $this->settings->enable_video_api_cache );
if ( ! empty( $twitter_data ) ) {
$videos[] = $twitter_data;
}
}
}
}
if ( ! empty( $videos ) ) {
// Remove old URL if it exists
$this->urls = array_filter(
$this->urls,
function( $item ) use ( $url ) {
return $item[0] !== $url;
}
);
$this->urls[] = array(
$url, // URL
$videos, // Videos
);
}
}
public function filter_value( string $value ): bool {
$extensions = explode( '.', $value );
$extension = end( $extensions );
return 'video' === wp_ext2type( $extension )
|| ( sgg_pro_enabled() && (
$this->is_youtube_url( $value ) ||
$this->is_vimeo_url( $value ) ||
$this->is_twitter_url( $value )
) );
}
/**
* Detect a YouTube video URL
*/
public function is_youtube_url( $url ) {
return (
false !== strpos( $url, 'https://www.youtube.com/embed/' ) ||
false !== strpos( $url, 'https://youtu.be/' ) ||
false !== strpos( $url, 'https://www.youtube.com/watch?v=' ) ||
false !== strpos( $url, '//www.youtube.com/embed/' ) ||
false !== strpos( $url, '//youtu.be/' ) ||
false !== strpos( $url, '//www.youtube.com/watch?v=' )
);
}
/**
* Detect a Vimeo video URL
*/
public function is_vimeo_url( $url ) {
return (
false !== strpos( $url, 'https://vimeo.com/' ) ||
false !== strpos( $url, 'https://player.vimeo.com/video/' )
);
}
/**
* Detect a Twitter post URL
*/
public function is_twitter_url( $url ) {
return preg_match( '#https?://(?:www\.)?(?:twitter\.com|x\.com)/[^/]+/status/(\d+)#i', $url );
}
/**
* Detect an Instagram post, Reel or IGTV URL
*/
public function is_instagram_url( $url ) {
return preg_match( '#https?://(?:www\.)?instagram\.com/(?:p|reel|tv)/([A-Za-z0-9_-]+)/?#i', $url );
}
/**
* Detect a TikTok video URL (full or "vm." shortlink)
*/
public function is_tiktok_url( $url ): bool {
return (bool) preg_match( '~^https?://(?:www\.|m\.)?tiktok\.com/@[A-Za-z0-9._-]+/video/\d+(?:[/?].*)?$~i', $url )
|| (bool) preg_match( '~^https?://vm\.tiktok\.com/[A-Za-z0-9_-]+/?$~i', $url );
}
}

View File

@@ -0,0 +1,100 @@
<?php
namespace GRIM_SG;
class Wizard extends Dashboard {
public static $activation_redirect = 'sgg_activation_redirect';
private static $wizard_completed = 'sgg_wizard_completed';
public function __construct() {
add_action( 'admin_menu', array( $this, 'add_admin_menu' ) );
add_action( 'admin_init', array( $this, 'maybe_redirect_to_wizard' ) );
add_action( 'wp_ajax_save_wizard_settings', array( $this, 'save_wizard_settings' ) );
}
public function add_admin_menu() {
add_submenu_page(
self::$slug,
esc_html__( 'Google XML Sitemaps Generator Wizard', 'xml-sitemap-generator-for-google' ),
esc_html__( 'Sitemaps Wizard', 'xml-sitemap-generator-for-google' ),
'manage_options',
$this->get_wizard_page_slug(),
array( $this, 'render_wizard_page' )
);
}
public function render_wizard_page() {
if ( ! current_user_can( 'manage_options' ) ) {
return;
}
wp_enqueue_style( 'sgg-wizard-styles', GRIM_SG_URL . 'assets/css/wizard.min.css', array(), GRIM_SG_VERSION );
wp_enqueue_style( 'sgg-icons', GRIM_SG_URL . 'assets/fonts/icons/style.css', array(), GRIM_SG_VERSION );
wp_enqueue_script( 'sgg-wizard-scripts', GRIM_SG_URL . 'assets/js/wizard.js', array( 'jquery' ), GRIM_SG_VERSION, true );
wp_localize_script(
'sgg-wizard-scripts',
'sggWizard',
array(
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'sgg_wizard_nonce' ),
'continue' => esc_html__( 'Continue', 'xml-sitemap-generator-for-google' ),
'finish' => esc_html__( 'Finish', 'xml-sitemap-generator-for-google' ),
)
);
self::render(
'wizard/main.php',
array(
'settings' => $this->get_settings(),
)
);
}
public function maybe_redirect_to_wizard() {
if ( ! get_transient( self::$activation_redirect ) || ! current_user_can( 'manage_options' )
|| wp_doing_ajax() || is_network_admin() ) {
return;
}
delete_transient( self::$activation_redirect );
if ( ! get_option( self::$wizard_completed ) ) {
wp_safe_redirect( admin_url( 'admin.php?page=' . $this->get_wizard_page_slug() ) );
exit;
}
}
public function save_wizard_settings() {
if ( ! current_user_can( 'manage_options' ) || ! wp_verify_nonce( $_POST['nonce'] ?? '', 'sgg_wizard_nonce' ) ) {
return;
}
$settings = $this->get_settings();
$settings->enable_sitemap = sanitize_text_field( $_POST['enable_sitemap'] ?? 0 );
$settings->enable_html_sitemap = sanitize_text_field( $_POST['enable_html_sitemap'] ?? 0 );
$settings->enable_google_news = sanitize_text_field( $_POST['enable_google_news'] ?? 0 );
$settings->enable_image_sitemap = sanitize_text_field( $_POST['enable_image_sitemap'] ?? 0 );
$settings->enable_video_sitemap = sanitize_text_field( $_POST['enable_video_sitemap'] ?? 0 );
$settings->sitemap_view = sanitize_text_field( $_POST['sitemap_view'] ?? '' );
$settings->enable_cache = sanitize_text_field( $_POST['enable_cache'] ?? 0 );
$settings->cache_timeout = sanitize_text_field( $_POST['cache_timeout'] ?? $settings->cache_timeout );
$settings->cache_timeout_period = sanitize_text_field( $_POST['cache_timeout_period'] ?? $settings->cache_timeout_period );
update_option( self::$slug, $settings );
flush_rewrite_rules();
wp_send_json(
array(
'success' => true,
'redirect' => admin_url( 'admin.php?page=' . self::$slug ),
)
);
}
public function get_wizard_page_slug() {
return self::$slug . '-wizard';
}
}

View File

@@ -0,0 +1,55 @@
<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Language Files
if ( ! is_textdomain_loaded( 'xml-sitemap-generator-for-google' ) ) {
load_plugin_textdomain( 'xml-sitemap-generator-for-google', false, 'xml-sitemap-generator-for-google/languages' );
}
// Autoload Files
require_once GRIM_SG_INCLUDES . 'hooks.php';
require_once GRIM_SG_INCLUDES . 'helpers.php';
require_once GRIM_SG_INCLUDES . 'search-settings.php';
// Autoload
require_once GRIM_SG_INCLUDES . 'vendor/Controller.php';
require_once GRIM_SG_INCLUDES . 'vendor/SitemapGenerator.php';
require_once GRIM_SG_INCLUDES . 'vendor/QueryBuilder.php';
require_once GRIM_SG_INCLUDES . 'vendor/PTSettings.php';
require_once GRIM_SG_INCLUDES . 'vendor/Settings.php';
require_once GRIM_SG_INCLUDES . 'vendor/Migration.php';
require_once GRIM_SG_INCLUDES . 'IndexNow.php';
require_once GRIM_SG_INCLUDES . 'Cache.php';
require_once GRIM_SG_INCLUDES . 'Sitemap.php';
require_once GRIM_SG_INCLUDES . 'Frontend.php';
require_once GRIM_SG_INCLUDES . 'GoogleNews.php';
require_once GRIM_SG_INCLUDES . 'MediaSitemap.php';
require_once GRIM_SG_INCLUDES . 'ImageSitemap.php';
require_once GRIM_SG_INCLUDES . 'VideoSitemap.php';
require_once GRIM_SG_INCLUDES . 'MultilingualSitemap.php';
require_once GRIM_SG_INCLUDES . 'Tools.php';
require_once GRIM_SG_INCLUDES . 'ImportExport.php';
require_once GRIM_SG_INCLUDES . 'PostSettings.php';
require_once GRIM_SG_INCLUDES . 'SitemapCLI.php';
function sgg_init() {
new GRIM_SG\Vendor\Migration();
new GRIM_SG\Frontend();
new GRIM_SG\Tools();
new GRIM_SG\PostSettings();
// WP Admin
if ( is_admin() ) {
require_once GRIM_SG_INCLUDES . 'Dashboard.php';
require_once GRIM_SG_INCLUDES . 'Notices.php';
require_once GRIM_SG_INCLUDES . 'Wizard.php';
new GRIM_SG\Dashboard();
new GRIM_SG\Notices();
new GRIM_SG\ImportExport();
new GRIM_SG\Wizard();
}
}
add_action( 'plugins_loaded', 'sgg_init', 1 );

View File

@@ -0,0 +1,174 @@
<?php
use GRIM_SG\ImageSitemap;
use GRIM_SG\VideoSitemap;
function sgg_pro_enabled() {
return defined( 'SGG_PRO_VERSION' );
}
function sgg_get_pro_url( $utm = 'buy-now' ) {
return "https://wpgrim.com/google-xml-sitemaps-generator-pro/?utm_source=sgg-plugin&utm_medium={$utm}&utm_campaign=xml_sitemap";
}
function sgg_get_support_url() {
return 'https://wordpress.org/support/plugin/xml-sitemap-generator-for-google/';
}
function sgg_get_review_url() {
return sgg_get_support_url() . 'reviews/?filter=5#new-post';
}
function sgg_show_pro_badge() {
if ( ! sgg_pro_enabled() ) {
load_template( GRIM_SG_PATH . '/templates/partials/pro-badge.php', false );
}
}
function sgg_show_pro_overlay( $args = array() ) {
if ( ! sgg_pro_enabled() ) {
load_template( GRIM_SG_PATH . '/templates/partials/pro-overlay.php', false, $args );
}
}
function sgg_pro_class() {
return sgg_pro_enabled() ? 'active' : 'inactive';
}
function sgg_parse_language( $lang ) {
$lang = str_replace( '_', '-', convert_chars( strtolower( strip_tags( $lang ) ) ) );
if ( 0 === strpos( $lang, 'zh' ) ) {
$lang = strpos( $lang, 'hk' ) || strpos( $lang, 'hant' ) || strpos( $lang, 'tw' ) ? 'zh-tw' : 'zh-cn';
} else {
$explode = explode( '-', $lang );
$lang = $explode[0];
}
return ! empty( $lang ) ? $lang : 'en';
}
function sgg_is_sitemap_index( $template, $settings = null ) {
if ( ! $settings ) {
$settings = ( new \GRIM_SG\Vendor\Controller() )->get_settings();
}
// Detect Media Sitemap Structure
if ( in_array( $template, array( ImageSitemap::$template, VideoSitemap::$template ), true ) && ! empty( $settings->sitemap_view ) ) {
$sitemap_structure = get_option( "sgg_{$template}_structure" );
return 'multiple' === $sitemap_structure;
}
return in_array( $template, array( 'sitemap', 'inner-sitemap' ), true ) && ! empty( $settings->sitemap_view );
}
function sgg_get_home_url( $path = '' ) {
$home_url = function_exists( 'pll_home_url' ) ? pll_home_url() : get_home_url();
$home_url = trim( apply_filters( 'wpml_home_urls', $home_url ), '/' );
if ( function_exists( 'trp_get_locale' ) ) {
$trp_settings = get_option( 'trp_settings' );
$current_locale = trp_get_locale();
if ( $current_locale !== $trp_settings['default-language'] ?? null ) {
$lang = substr( $current_locale, 0, 2 );
$home_url = get_site_url( null, $lang );
}
}
if ( defined( 'ICL_SITEPRESS_VERSION' ) && ! empty( $path ) && ! empty( $_GET['lang'] ) && false !== strpos( $home_url, '?lang=' ) ) {
$home_url = trim( strtok( $home_url, '?' ), '/' );
$home_url = add_query_arg( 'lang', sanitize_text_field( $_GET['lang'] ), "{$home_url}/{$path}" );
$path = ''; // Reset path to avoid duplication
}
$home_url = apply_filters( 'xml_sitemaps_site_url', $home_url );
return ! empty( $path ) ? "{$home_url}/{$path}" : $home_url;
}
function sgg_get_home_url_with_trailing_slash() {
return trailingslashit( untrailingslashit( sgg_get_home_url() ) );
}
function sgg_is_nginx() {
return isset( $_SERVER['SERVER_SOFTWARE'] ) && stristr( sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ), 'nginx' ) !== false;
}
function sgg_is_using_mod_rewrite() {
global $wp_rewrite;
return $wp_rewrite->using_mod_rewrite_permalinks();
}
function sgg_get_sitemap_url( $sitemap_url, $sitemap_type, $suffix = true ) {
if ( sgg_is_using_mod_rewrite() ) {
return sgg_get_home_url( $sitemap_url );
} else {
return sgg_get_home_url( "index.php?{$sitemap_type}" . ( $suffix ? '=true' : '' ) );
}
}
function sgg_get_languages( $with_default = false ) {
$languages = array();
if ( function_exists( 'pll_languages_list' ) ) {
$languages = pll_languages_list( array( 'fields' => 'slug' ) );
$default_lang = array_search( pll_default_language(), $languages, true );
if ( false !== $default_lang && ! $with_default ) {
unset( $languages[ $default_lang ] );
}
}
if ( function_exists( 'trp_get_languages' ) ) {
$trp_settings = get_option( 'trp_settings' );
$trp_slugs = $trp_settings['url-slugs'] ?? array();
if ( ! $with_default ) {
unset( $trp_slugs[ $trp_settings['default-language'] ?? '' ] );
}
$languages = array_values( $trp_slugs );
}
return $languages;
}
function sgg_get_default_language_code() {
if ( function_exists( 'pll_default_language' ) ) {
return pll_default_language();
}
return apply_filters( 'wpml_default_language', null );
}
function sgg_is_multilingual() {
return function_exists( 'pll_languages_list' )
|| function_exists( 'trp_get_languages' )
|| defined( 'ICL_SITEPRESS_VERSION' );
}
function sgg_maybe_remove_inner_suffix( $template ) {
if ( strpos( $template, '-inner-sitemap' ) !== false ) {
$template = str_replace( '-inner-sitemap', '', $template );
}
return $template;
}
function sgg_pro_features_page_tab( $tabs ) {
if ( ! sgg_pro_enabled() ) {
$tabs['xml_sitemap_generator_for_google_pro'] = sprintf(
'<a href="%s" target="_blank" class="sgg-plugin-pro-feature">%s</a>',
esc_url( 'https://wpgrim.com/google-xml-sitemaps-generator-pro/?utm_source=sgg-plugin&utm_medium=plugins&utm_campaign=xml_sitemap' ),
esc_html__( 'XML Sitemaps Generator Pro Features', 'xml-sitemap-generator-for-google' )
);
wp_enqueue_style( 'sgg-styles', GRIM_SG_URL . 'assets/css/styles.min.css', array(), GRIM_SG_VERSION );
}
return $tabs;
}
add_filter( 'install_plugins_tabs', 'sgg_pro_features_page_tab' );

View File

@@ -0,0 +1,234 @@
<?php
/**
* Plugin Activation Hook
*/
function sgg_activation() {
\GRIM_SG\Frontend::activate_plugin();
\GRIM_SG\Vendor\Migration::update_version();
( new \GRIM_SG\IndexNow() )->set_api_key();
update_option( 'sgg_installation_time', time(), false );
// Set the activation redirect transient.
set_transient( 'sgg_activation_redirect', true, MINUTE_IN_SECONDS );
}
/**
* Plugin Deactivation Hook
*/
function sgg_deactivation() {
\GRIM_SG\IndexNow::delete_api_key();
delete_option( 'sgg_installation_time' );
}
/**
* Get Polylang language for a post.
*/
function sgg_polylang_post_language( $language, $post_id ) {
if ( function_exists( 'pll_get_post_language' ) ) {
$language = pll_get_post_language( $post_id, 'slug' );
}
return $language;
}
add_filter( 'xml_sitemap_news_language', 'sgg_polylang_post_language', 10, 2 );
/**
* Get the WPML language for a post.
*/
function sgg_wpml_post_language( $language, $post_id, $post_type = 'post' ) {
global $sitepress;
if ( $sitepress ) {
$language = apply_filters(
'wpml_element_language_code',
$language,
array(
'element_id' => $post_id,
'element_type' => $post_type,
)
);
}
return $language;
}
add_filter( 'xml_sitemap_news_language', 'sgg_wpml_post_language', 10, 3 );
/**
* Exclude posts with noindex from the sitemap.
*/
function sgg_exclude_noindex_posts( $value, $post_id ) {
// Yoast SEO noindex
if ( defined( 'WPSEO_VERSION' ) && '1' === get_post_meta( $post_id, '_yoast_wpseo_meta-robots-noindex', true ) ) {
return false;
}
// Rank Math noindex
if ( class_exists( 'RankMath' ) ) {
$rank_math_robots = get_post_meta( $post_id, 'rank_math_robots', true );
if ( ! empty( $rank_math_robots ) && is_array( $rank_math_robots ) && in_array( 'noindex', $rank_math_robots, true ) ) {
return false;
}
}
// SEO Framework noindex
if ( defined( 'THE_SEO_FRAMEWORK_VERSION' ) ) {
$seo_framework_noindex = get_post_meta( $post_id, '_genesis_noindex', true );
if ( '1' === $seo_framework_noindex ) {
return false;
}
}
return $value;
}
add_filter( 'xml_sitemap_include_post', 'sgg_exclude_noindex_posts', 99, 2 );
/**
* Exclude terms with noindex from the sitemap.
*/
function sgg_exclude_noindex_terms( $value, $term_id, $taxonomy ) {
// Yoast SEO noindex
if ( is_callable( '\WPSEO_Taxonomy_Meta::get_term_meta' ) ) {
$noindex = \WPSEO_Taxonomy_Meta::get_term_meta( $term_id, $taxonomy, 'noindex' );
if ( 'noindex' === $noindex ) {
return true;
}
}
// Rank Math noindex
if ( class_exists( 'RankMath' ) ) {
$rank_math_robots = get_term_meta( $term_id, 'rank_math_robots', true );
if ( ! empty( $rank_math_robots ) && is_array( $rank_math_robots ) && in_array( 'noindex', $rank_math_robots, true ) ) {
return true;
}
}
// SEO Framework noindex
if ( defined( 'THE_SEO_FRAMEWORK_VERSION' ) ) {
$seo_framework_meta = get_term_meta( $term_id, 'autodescription-term-settings', true );
if ( ! empty( $seo_framework_meta ) && isset( $seo_framework_meta['noindex'] ) && 1 === $seo_framework_meta['noindex'] ) {
return true;
}
}
return $value;
}
add_filter( 'sgg_sitemap_exclude_single_term', 'sgg_exclude_noindex_terms', 99, 3 );
/**
* Add FooGallery image URLs to the sitemap.
*/
function sgg_add_foogallery_image_urls( $urls, $post_id ) {
if ( defined( 'FOOGALLERY_CPT_GALLERY' ) && class_exists( 'FooGallery' ) ) {
$content = get_post_field( 'post_content', $post_id );
if ( false !== strpos( $content, FOOGALLERY_CPT_GALLERY ) ) {
$gallery_ids = array();
preg_match_all( '/<!--\s*wp:fooplugins\/foogallery\s*{"id":\s*(\d+)\s*}\s*\/-->/', $content, $matches );
if ( ! empty( $matches[1] ) ) {
$gallery_ids = array_map( 'intval', $matches[1] );
}
if ( ! empty( $gallery_ids ) ) {
$gallery_ids = array_unique( $gallery_ids );
foreach ( $gallery_ids as $gallery_id ) {
$gallery = FooGallery::get_by_id( $gallery_id );
if ( $gallery ) {
foreach ( $gallery->attachments() as $attachment ) {
$urls[] = wp_get_attachment_url( $attachment->ID );
}
}
}
}
}
}
return $urls;
}
add_filter( 'sgg_sitemap_post_media_urls', 'sgg_add_foogallery_image_urls', 10, 2 );
/**
* BeeTheme Compatibility
*/
function sgg_be_theme_compatibility( $content, $post ) {
if ( defined( 'MFN_THEME_VERSION' ) ) {
$mfn_items = get_post_meta( $post->ID, 'mfn-builder-preview', true );
if ( ! empty( $mfn_items ) ) {
if ( ! is_array( $mfn_items ) ) {
$mfn_items = call_user_func( 'base64_decode', $mfn_items );
}
if ( ! empty( $mfn_items ) ) {
$content = is_array( $mfn_items ) ? implode( '', $mfn_items ) : $mfn_items;
}
}
}
return $content;
}
add_filter( 'xml_media_sitemap_post_content', 'sgg_be_theme_compatibility', 10, 2 );
/**
* Serve IndexNow API key.
*/
function sgg_serve_indexnow_api_key() {
global $wp;
$indexnow = new \GRIM_SG\IndexNow();
$current_url = home_url( $wp->request );
if ( ! empty( $current_url ) && $indexnow->get_api_key_location() === $current_url ) {
header( 'Content-Type: text/plain' );
header( 'X-Robots-Tag: noindex' );
status_header( 200 );
echo esc_html( $indexnow->get_api_key() );
exit();
}
}
add_action( 'wp', 'sgg_serve_indexnow_api_key' );
/**
* Clear Media Sitemap cache when a post status is changed.
*/
function sgg_clear_media_sitemap_cache( $new_status, $old_status, $post ) {
$settings = get_option( \GRIM_SG\Vendor\Controller::$slug );
if ( is_object( $settings ) && ! empty( $settings->disable_media_sitemap_cache ) ) {
return;
}
if ( ( 'publish' === $old_status && 'publish' !== $new_status )
|| ( 'publish' === $new_status && 'publish' !== $old_status ) ) {
\GRIM_SG\MediaSitemap::delete_all_cache();
}
}
add_action( 'transition_post_status', 'sgg_clear_media_sitemap_cache', 10, 3 );
/**
* Disable default WordPress Sitemaps.
*/
add_filter( 'wp_sitemaps_enabled', '__return_false' );
/**
* Google New Title Filter
*/
function sgg_google_news_title( $title, $post_id ) {
// SEO Framework title
if ( defined( 'THE_SEO_FRAMEWORK_VERSION' ) ) {
$seo_title = get_post_meta( $post_id, '_genesis_title', true );
if ( ! empty( $seo_title ) ) {
$title = $seo_title;
}
}
return $title;
}
add_filter( 'xml_sitemap_google_news_title', 'sgg_google_news_title', 10, 2 );

View File

@@ -0,0 +1,260 @@
<?php
function xml_sitemap_generate_settings() {
$settings_array = array(
'general' => array(
array(
'id' => 'sitemap_to_robots',
'label' => 'Add Sitemap Output URLs to site "robots.txt" file',
'tags' => 'robots.txt',
),
array(
'id' => 'enable_indexnow',
'label' => 'Enable IndexNow Protocol (Microsoft Bing, Seznam.cz, Naver, Yandex)',
'tags' => 'Posts',
),
array(
'id' => 'enable_sitemap',
'label' => 'XML Sitemap',
),
array(
'id' => 'enable_html_sitemap',
'label' => 'HTML Sitemap',
),
array(
'id' => 'webserver_configuration',
'label' => 'Webserver Configuration' ,
),
array(
'id' => 'sitemap_structure',
'label' => 'Sitemap Structure' ,
),
array(
'id' => 'links_per_page',
'label' => 'Links per page:' ,
),
array(
'id' => 'custom_sitemaps',
'label' => 'Custom Sitemaps' ,
),
array(
'id' => 'additional_urls',
'label' => 'Additional URLs',
),
array(
'id' => 'general_exclude_posts',
'label' => 'Exclude',
),
array(
'id' => 'general_include_only_terms',
'label' => 'Include only selected Terms',
),
array(
'id' => 'posts_priority',
'label' => 'Posts Priority',
),
array(
'id' => 'sitemap_options',
'label' => 'Sitemap Options',
),
array(
'id' => 'general_custom_post_types',
'label' => 'General Custom Post Types',
),
),
'google-news' => array(
array(
'id' => 'enable_google_news',
'label' => 'Google News',
'tags' => '',
),
array(
'id' => 'google_news_name',
'label' => 'Publication Name',
),
array(
'id' => 'google_news_old_posts',
'label' => 'Include Older Posts',
),
array(
'id' => 'google__url',
'label' => 'Google News URL',
),
array(
'id' => 'google_keywords',
'label' => 'Keywords',
),
array(
'id' => 'google_news_stocks',
'label' => 'Stock Tickers',
),
array(
'id' => 'google_exclude',
'label' => 'Exclude',
),
array(
'id' => 'google_include',
'label' => 'Include only selected Terms',
),
array(
'id' => 'google_content_post',
'label' => 'Content Options',
),
array(
'id' => 'google_custom_post_types',
'label' => 'Google Custom Post Types',
),
),
'image-sitemap' => array(
array(
'id' => 'enable_image_sitemap',
'label' => 'Image Sitemap',
),
array(
'id' => 'image_sitemap_url',
'label' => 'Image Sitemap URL:',
),
array(
'id' => 'hide_image_previews',
'label' => 'Hide Image Previews',
),
array(
'id' => 'hide_image_sitemap_xsl',
'label' => 'Hide XSL Template',
),
array(
'id' => 'image_mime_types',
'label' => 'Image MIME Types',
),
array(
'id' => 'exclude_broken_images',
'label' => 'Exclude Broken Images',
),
array(
'id' => 'include_featured_images',
'label' => 'Include Featured Images',
),
array(
'id' => 'include_woo_gallery',
'label' => 'Include WooCommerce Gallery Images',
),
array(
'id' => 'image_content_option',
'label' => 'Content Options',
),
array(
'id' => 'image_custom_post_types',
'label' => 'Image Custom Post Types',
),
),
'video-sitemap' => array(
array(
'id' => 'enable_video_sitemap',
'label' => 'Enable Video Sitemap',
),
array(
'id' => 'video_sitemap_url',
'label' => 'Video Sitemap URL:',
),
array(
'id' => 'hide_video_sitemap_xsl',
'label' => 'Hide XSL Template',
),
array(
'id' => 'sgg_youtube',
'label' => 'YouTube',
),
array(
'id' => 'youtube_api_key',
'label' => 'YouTube Data API v3 Key:',
),
array(
'id' => 'sgg_vimeo',
'label' => 'Vimeo',
),
array(
'id' => 'vimeo_check_api_key',
'label' => 'Vimeo Access Token:',
),
array(
'id' => 'enable_video_api_cache',
'label' => 'API Data Cache',
),
array(
'id' => 'video_content_option',
'label' => 'Content Options',
),
array(
'id' => 'video_custom_post_types',
'label' => 'Custom Post Types',
),
),
'advanced' => array(
array(
'id' => 'enable_cache',
'label' => 'Cache',
),
array(
'id' => 'cache_timeout_period',
'label' => 'Cache Expiration Time:',
),
array(
'id' => 'last_cached_time',
'label' => 'Last Cached Time:',
),
array(
'id' => 'clear_cache',
'label' => 'Clear Cache',
),
array(
'id' => 'clear_cache_on_save_post',
'label' => 'Smart Caching',
),
array(
'id' => 'disable_media_sitemap_cache',
'label' => 'Disable Media Sitemap Cache Collection',
),
array(
'id' => 'minimize_sitemap',
'label' => 'Minimize Sitemap source code',
),
array(
'id' => 'advanced_sitemap_styles',
'label' => 'Sitemap Styles',
),
array(
'id' => 'hide_branding',
'label' => 'Hide Branding Marks',
),
array(
'id' => 'enable_cronjob',
'label' => 'Cron Job',
),
array(
'id' => 'cronjob_runtime',
'label' => 'Cron Job Run Time:',
),
array(
'id' => 'advanced_wp_cli',
'label' => 'WP CLI',
),
array(
'id' => 'advanced_import_settings',
'label' => 'Import Settings',
),
array(
'id' => 'advanced_export_settings',
'label' => 'Export Settings',
),
),
);
$flat_settings_array = array();
foreach ( $settings_array as $tab => $settings ) {
foreach ( $settings as $setting ) {
$setting['tab'] = $tab;
$flat_settings_array[] = $setting;
}
}
return $flat_settings_array;
}

View File

@@ -0,0 +1,87 @@
<?php
namespace GRIM_SG\Vendor;
use GRIM_SG\Settings;
class Controller {
public static $slug = 'xml-sitemap-generator-for-google';
public function get_settings() {
$settings = new Settings();
$saved_options = get_option( self::$slug );
// TODO Remove this after Refactoring
if ( is_array( $saved_options ) ) {
$saved_options = (object) $saved_options;
}
if ( ! empty( $saved_options ) ) {
foreach ( $settings as $key => &$option ) {
if ( isset( $saved_options->{$key} ) ) {
if ( in_array( $key, array( 'cpt', 'taxonomies' ), true ) ) {
if ( is_array( $saved_options->{$key} ) ) {
foreach ( $saved_options->{$key} as $inner_key => $value ) {
$option[ $inner_key ] = is_array( $value )
? (object) $value // TODO Remove this after Refactoring
: $value;
}
}
} else {
$option = ( is_object( $option ) && is_array( $saved_options->{$key} ) )
? (object) $saved_options->{$key} // TODO Remove this after Refactoring
: $saved_options->{$key};
}
}
}
}
return $settings;
}
/**
* Get Custom Post Types
* @return string[]|\WP_Post_Type[]
*/
public function get_cpt( $output = 'names' ) {
$args = array(
'public' => true,
'_builtin' => false,
);
return get_post_types( $args, $output );
}
/**
* Get Taxonomy Types
* @return string[]|\WP_Taxonomy[]
*/
public function get_taxonomy_types( $output = 'names' ) {
$args = array(
'public' => true,
'show_ui' => true,
);
return get_taxonomies( $args, $output );
}
/**
* Get Allowed Post Types List
* @return array
*/
public function get_post_types_list( $post_types, $settings ) {
foreach ( $post_types as $key => $post_type ) {
if ( isset( $settings->{$post_type}->include ) && ! $settings->{$post_type}->include ) {
unset( $post_types[ $key ] );
}
}
foreach ( $this->get_cpt() as $cpt ) {
if ( ! empty( $settings->cpt[ $cpt ] ) && ! empty( $settings->cpt[ $cpt ]->include ) ) {
$post_types[] = $cpt;
}
}
return $post_types;
}
}

View File

@@ -0,0 +1,97 @@
<?php
namespace GRIM_SG\vendor;
class Migration extends Controller {
const DB_VERSION_KEY = 'sgg_version';
/**
* Database updates and callbacks that need to be run per Migration version.
*/
private $migrations = array(
'1.8.4' => array(
'migrate_settings_dynamic_created_objects',
),
'1.9.9' => array(
'migrate_video_settings',
),
);
public function __construct() {
add_action( 'admin_init', array( $this, 'run_migrations' ) );
}
/**
* Runs all the necessary migrations.
*/
public function run_migrations() {
$current_db_version = get_option( self::DB_VERSION_KEY, '' );
if ( version_compare( $current_db_version, GRIM_SG_VERSION, '>=' ) ) {
return;
}
foreach ( $this->migrations as $version => $update_callbacks ) {
if ( version_compare( $current_db_version, $version, '<' ) ) {
foreach ( $update_callbacks as $update_callback ) {
$this->{$update_callback}();
}
}
}
self::update_version();
}
/**
* Update Version in Database
*/
public static function update_version() {
update_option( self::DB_VERSION_KEY, GRIM_SG_VERSION );
}
/**
* Migrate Dynamic created CPT and Taxonomies to array
*/
public function migrate_settings_dynamic_created_objects() {
$settings = get_option( self::$slug, array() );
if ( ! empty( $settings ) ) {
$settings->cpt = array();
$settings->taxonomies = array();
foreach ( $this->get_cpt() as $cpt ) {
if ( ! empty( $settings->{$cpt} ) ) {
$settings->cpt[ $cpt ] = $settings->{$cpt};
}
}
foreach ( $this->get_taxonomy_types() as $taxonomy ) {
if ( ! empty( $settings->{$taxonomy} ) ) {
$settings->taxonomies[ $taxonomy ] = $settings->{$taxonomy};
}
}
update_option( Controller::$slug, $settings );
}
}
/**
* Migrate Video Sitemap Settings
*/
public function migrate_video_settings() {
$settings = get_option( self::$slug, array() );
if ( ! empty( $settings ) ) {
$settings->enable_video_api_cache = $settings->enable_youtube_cache ?? true;
update_option( Controller::$slug, $settings );
}
// Migrate YouTube Data to Video API Data
$youtube_data = get_option( 'sgg_youtube_data', array() );
if ( ! empty( $youtube_data ) ) {
update_option( 'sgg_video_api_data', $youtube_data );
}
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace GRIM_SG;
class PTSettings {
public $include = true;
public $priority = 3;
public $frequency = 'weekly';
public $google_news = false;
public $image_sitemap = true;
public $video_sitemap = true;
// Frequencies
public static $ALWAYS = 'always';
public static $HOURLY = 'hourly';
public static $DAILY = 'daily';
public static $WEEKLY = 'weekly';
public static $MONTHLY = 'monthly';
public static $YEARLY = 'yearly';
public static $NEVER = 'never';
/**
* DefaultSettings constructor.
*
* @param int $priority
* @param int $frequency
*/
public function __construct( $priority = 3, $frequency = 1, $google_news = false, $image_sitemap = false, $video_sitemap = false ) {
$this->priority = $priority;
$this->frequency = $frequency;
$this->google_news = $google_news;
$this->image_sitemap = $image_sitemap;
$this->video_sitemap = $video_sitemap;
}
}

View File

@@ -0,0 +1,17 @@
<?php
namespace GRIM_SG\Vendor;
class QueryBuilder {
/**
* Run SQL Query
*
* @param $sql
* @return array|null|object
*/
public static function run_query( $sql ) {
global $wpdb;
return $wpdb->get_results( $sql );
}
}

View File

@@ -0,0 +1,130 @@
<?php
namespace GRIM_SG;
use GRIM_SG\Vendor\Controller;
class Settings extends Controller {
// Global Settings
public $sitemap_url = 'sitemap.xml';
public $html_sitemap_url = 'sitemap.html';
public $enable_sitemap = true;
public $links_per_page = 1000;
public $enable_html_sitemap = false;
public $sitemap_to_robots = true;
public $enable_indexnow = true;
public $sitemap_view = 'sitemap-index';
// Sitemap Data Settings
public $home;
public $page;
public $post;
public $archive;
public $archive_older;
public $authors;
public $media;
public $exclude_posts;
public $exclude_terms;
public $include_only_terms;
public $posts_priority;
public $custom_sitemaps = array();
public $additional_pages = array();
public $cpt = array();
public $taxonomies = array();
// Google News Data Settings
public $enable_google_news = false;
public $google_news_old_posts = false;
public $google_news_name = '';
public $google_news_url = 'google-news.xml';
public $google_news_keywords = '';
public $google_news_stocks = false;
public $google_news_exclude;
public $google_news_exclude_terms;
public $google_news_include_only_terms;
// Media Sitemap Data Settings
public $enable_image_sitemap = false;
public $enable_video_sitemap = false;
public $image_sitemap_url = 'image-sitemap.xml';
public $video_sitemap_url = 'video-sitemap.xml';
public $hide_image_previews = false;
public $hide_image_sitemap_xsl = false;
public $hide_video_sitemap_xsl = false;
public $image_mime_types = array(
'image/jpeg' => true,
'image/png' => true,
'image/gif' => true,
'image/bmp' => true,
'image/webp' => true,
'image/avif' => true,
);
public $youtube_api_key = '';
public $vimeo_api_key = '';
public $exclude_broken_images = false;
public $include_featured_images = false;
public $include_woo_gallery = false;
// Cache Settings
public $enable_cache = false;
public $cache_timeout = 24;
public $cache_timeout_period = 3600;
public $clear_cache_on_save_post = false;
public $enable_video_api_cache = true;
public $disable_media_sitemap_cache = false;
public $minimize_sitemap = false;
public $hide_branding = true;
public $enable_cronjob = false;
public $cronjob_runtime = 'daily';
public $colors = array(
'header_background_color' => '#82a745',
'header_text_color' => '#ffffff',
'sitemap_background_color' => '#ecf4db',
'sitemap_text_color' => '#444444',
'sitemap_link_color' => '#2d89c7',
'footer_text_color' => '#666666',
);
/**
* Settings constructor.
*/
public function __construct() {
$this->home = new PTSettings( 10, PTSettings::$DAILY );
$this->page = new PTSettings( 6, PTSettings::$WEEKLY, false, true, true );
$this->post = new PTSettings( 6, PTSettings::$MONTHLY, true, true, true );
$this->archive = new PTSettings( 6, PTSettings::$DAILY );
$this->archive_older = new PTSettings( 3, PTSettings::$YEARLY );
$this->authors = new PTSettings( 3, PTSettings::$WEEKLY );
$this->media = new PTSettings( 3, PTSettings::$WEEKLY );
// Media Pages are disabled by default
$this->media->include = false;
foreach ( $this->get_cpt() as $cpt ) {
$this->cpt[ $cpt ] = new PTSettings( 6, PTSettings::$MONTHLY );
}
foreach ( $this->get_taxonomy_types() as $taxonomy ) {
$this->taxonomies[ $taxonomy ] = new PTSettings( 3, PTSettings::$WEEKLY );
}
}
/**
* Get Default Settings
* @param $option
* @return PTSettings
*/
public function get_row_value( $option ) {
$settings = new PTSettings();
$settings->include = isset( $_POST[ $option . '_include' ] ) ? sanitize_text_field( $_POST[ $option . '_include' ] ) : false;
$settings->priority = isset( $_POST[ $option . '_priority' ] ) ? sanitize_text_field( $_POST[ $option . '_priority' ] ) : 0;
$settings->frequency = isset( $_POST[ $option . '_frequency' ] ) ? sanitize_text_field( $_POST[ $option . '_frequency' ] ) : $settings->frequency;
$settings->google_news = isset( $_POST[ $option . '_google_news' ] ) ? sanitize_text_field( $_POST[ $option . '_google_news' ] ) : 0;
$settings->image_sitemap = isset( $_POST[ $option . '_image_sitemap' ] ) ? sanitize_text_field( $_POST[ $option . '_image_sitemap' ] ) : 0;
$settings->video_sitemap = isset( $_POST[ $option . '_video_sitemap' ] ) ? sanitize_text_field( $_POST[ $option . '_video_sitemap' ] ) : 0;
return $settings;
}
}

View File

@@ -0,0 +1,636 @@
<?php
namespace GRIM_SG\Vendor;
use GRIM_SG\GoogleNews;
use GRIM_SG\ImageSitemap;
use GRIM_SG\MultilingualSitemap;
use GRIM_SG\VideoSitemap;
class SitemapGenerator {
public $sitemapFileName = 'sitemap.xml';
public $sitemapIndexFileName = 'sitemap-index.xml';
public $robotsFileName = 'robots.txt';
public $maxURLsPerSitemap = 50000;
public $createGZipFile = false;
private $baseURL;
private $searchEngines = array(
array(
'https://search.yahooapis.com/SiteExplorerService/V1/updateNotification?appid=USERID&url=',
'https://search.yahooapis.com/SiteExplorerService/V1/ping?sitemap=',
),
'https://www.google.com/webmasters/tools/ping?sitemap=',
'https://submissions.ask.com/ping?sitemap=',
'https://www.bing.com/webmaster/ping.aspx?siteMap=',
);
private $urls = array();
private $sitemaps;
private $sitemapIndex;
private $sitemapFullURL;
/**
* Constructor.
*
* @param string $baseURL You site URL, with / at the end.
*/
public function __construct( $baseURL ) {
$this->baseURL = $baseURL;
}
public function addUrls( $urls_array, $callback = 'addUrl', $template = 'sitemap' ) {
if ( ! is_array( $urls_array ) ) {
throw new \InvalidArgumentException( 'Array as argument should be given.' );
}
if ( sgg_is_sitemap_index( $template ) ) {
foreach ( $urls_array as $sitemap => $inner_urls ) {
foreach ( $inner_urls as $url ) {
$this->{$callback}( $url, $sitemap );
}
}
} else {
foreach ( $urls_array as $url ) {
$this->{$callback}( $url );
}
}
}
/**
* Use this to add single URL to Google News Sitemap.
*/
public function addNewsUrl( $item ) {
$url = $item[0] ?? null;
$this->validateUrl( $url );
$tmp = array();
$tmp['loc'] = $url;
$tmp['publication_name'] = $item[1] ?? null;
$tmp['language'] = $item[2] ?? null;
$tmp['title'] = $item[3] ?? null;
if ( isset( $item[4] ) ) {
$tmp['lastmod'] = $item[4];
}
if ( sgg_pro_enabled() ) {
$tmp['keywords'] = implode( ', ', apply_filters( 'sgg_news_keywords', array(), $item[5] ?? null ) );
$tmp['stock_tickers'] = implode( ', ', apply_filters( 'sgg_news_stock_tickers', array(), $item[5] ?? null ) );
}
$this->urls[] = $tmp;
}
/**
* Use this to add single URL to Sitemap.
*/
public function addMediaUrl( $item, $sitemap = null ) {
$url = $item[0] ?? null;
$this->validateUrl( $url );
$tmp = array();
$tmp['loc'] = $url;
if ( isset( $item[1] ) ) {
$tmp['media'] = $item[1];
}
if ( ! empty( $sitemap ) ) {
$tmp['lastmod'] = gmdate( 'c' );
$this->urls[ $sitemap ][] = $tmp;
} else {
$this->urls[] = $tmp;
}
}
/**
* Use this to add single URL to Sitemap.
*/
public function addUrl( $item, $sitemap = null ) {
$url = $item[0] ?? null;
$this->validateUrl( $url );
$tmp = array();
$tmp['loc'] = $url;
if ( isset( $item[1] ) ) {
$tmp['lastmod'] = $item[1];
}
if ( isset( $item[2] ) ) {
$tmp['changefreq'] = $item[2];
}
if ( isset( $item[3] ) ) {
$tmp['priority'] = $item[3];
}
if ( ! empty( $sitemap ) ) {
$this->urls[ $sitemap ][] = $tmp;
} else {
$this->urls[] = $tmp;
}
}
/**
* Validate Sitemap Item URL
*/
public function validateUrl( $url ) {
if ( null === $url ) {
throw new \InvalidArgumentException( 'URL is mandatory. At least one argument should be given.' );
}
$urlLenght = extension_loaded( 'mbstring' ) ? mb_strlen( $url ) : strlen( $url );
if ( $urlLenght > 2048 ) {
throw new \InvalidArgumentException(
"URL lenght can't be bigger than 2048 characters.
Note, that precise url length check is guaranteed only using mb_string extension.
Make sure Your server allow to use mbstring extension."
);
}
}
/**
* Create sitemap in memory.
*/
public function createSitemap( $template = 'sitemap', $headers = array(), $is_xml = true ) {
if ( ! isset( $this->urls ) ) {
throw new \BadMethodCallException( 'To create sitemap, call addUrl or addUrls function first.' );
}
if ( $this->maxURLsPerSitemap > 50000 ) {
throw new \InvalidArgumentException( 'More than 50,000 URLs per single sitemap is not allowed.' );
}
$settings = ( new Controller() )->get_settings();
$stylesheet_template = sgg_maybe_remove_inner_suffix( $template );
$stylesheet_path = apply_filters( 'sitemap_xsl_template_path', 'sitemap-stylesheet.xsl' );
$stylesheet_url = sgg_get_sitemap_url( "{$stylesheet_path}?template={$stylesheet_template}", "sitemap_xsl={$stylesheet_template}", false );
$stylesheet_url = strtok( $stylesheet_url, '&' ); // remove & query string
$is_sitemap_index = sgg_is_sitemap_index( $template, $settings );
$is_media_template = in_array( $template, array( 'image-sitemap', 'video-sitemap' ), true );
$is_index_template = $is_media_template || 'sitemap' === $template;
$sitemap_urls = $is_sitemap_index
? $this->urls
: array_chunk( $this->urls, $this->maxURLsPerSitemap );
foreach ( $sitemap_urls as $sitemap_key => $sitemap ) {
$dom = $this->create_sitemap_dom( $stylesheet_url, $template, $settings );
$urlset = $this->create_sitemap_urlset( $dom, $headers );
$dom->appendChild( $urlset );
foreach ( $sitemap as $url ) {
$url_element = $dom->createElement( 'url' );
$url_element->appendChild(
$dom->createElement( 'loc', htmlspecialchars( $url['loc'], ENT_QUOTES, 'UTF-8' ) )
);
if ( GoogleNews::$template === $template ) {
if ( $settings->google_news_old_posts && GoogleNews::is_older_than_48h( $url['lastmod'] ) ) {
$url_element->appendChild(
$dom->createElement( 'lastmod', $url['lastmod'] )
);
} else {
$news = $url_element->appendChild( $dom->createElement( 'news:news' ) );
if ( isset( $url['publication_name'] ) ) {
$publication = $dom->createElement( 'news:publication' );
$news->appendChild( $publication );
$publication->appendChild(
$dom->createElement( 'news:name', esc_html( $url['publication_name'] ) )
);
$publication->appendChild(
$dom->createElement( 'news:language', $url['language'] )
);
}
if ( isset( $url['lastmod'] ) ) {
$news->appendChild(
$dom->createElement( 'news:publication_date', $url['lastmod'] )
);
}
if ( isset( $url['title'] ) ) {
$news->appendChild(
$dom->createElement( 'news:title', esc_html( $url['title'] ) )
);
}
if ( sgg_pro_enabled() ) {
$news->appendChild(
$dom->createElement( 'news:keywords', $url['keywords'] )
);
$news->appendChild(
$dom->createElement( 'news:stock_tickers', $url['stock_tickers'] )
);
}
}
} elseif ( strpos( $template, ImageSitemap::$template ) !== false ) {
if ( isset( $url['lastmod'] ) ) {
$url_element->appendChild(
$dom->createElement( 'lastmod', $url['lastmod'] )
);
} else {
if ( ! empty( $url['media'] ) ) {
foreach ( $url['media'] as $image ) {
$image_element = $url_element->appendChild( $dom->createElement( 'image:image' ) );
$image_element->appendChild(
$dom->createElement( 'image:loc', $image )
);
}
}
}
} elseif ( strpos( $template, VideoSitemap::$template ) !== false ) {
if ( isset( $url['lastmod'] ) ) {
$url_element->appendChild(
$dom->createElement( 'lastmod', $url['lastmod'] )
);
} else {
if ( ! empty( $url['media'] ) ) {
foreach ( $url['media'] as $video ) {
$video_element = $url_element->appendChild( $dom->createElement( 'video:video' ) );
$video_element->appendChild(
$dom->createElement( 'video:thumbnail_loc', esc_url( $video['thumbnail'] ?? '' ) )
);
// Decode HTML entities first, then DOMDocument will handle XML escaping properly
$video_title = html_entity_decode( $video['title'] ?? '', ENT_QUOTES | ENT_HTML5, 'UTF-8' );
$video_element->appendChild(
$dom->createElement( 'video:title', $video_title )
);
// Decode HTML entities first, then DOMDocument will handle XML escaping properly
$video_description = html_entity_decode( $video['description'] ?? '', ENT_QUOTES | ENT_HTML5, 'UTF-8' );
$video_element->appendChild(
$dom->createElement( 'video:description', $video_description )
);
$video_element->appendChild(
$dom->createElement( 'video:player_loc', esc_url( $video['player_loc'] ?? '' ) )
);
// Decode HTML entities first, then DOMDocument will handle XML escaping properly
$video_duration = html_entity_decode( $video['duration'] ?? '', ENT_QUOTES | ENT_HTML5, 'UTF-8' );
$video_element->appendChild(
$dom->createElement( 'video:duration', $video_duration )
);
}
}
}
} else {
if ( isset( $url['lastmod'] ) ) {
$url_element->appendChild(
$dom->createElement( 'lastmod', $url['lastmod'] )
);
}
if ( isset( $url['changefreq'] ) ) {
$url_element->appendChild(
$dom->createElement( 'changefreq', $url['changefreq'] )
);
}
if ( isset( $url['priority'] ) ) {
$url_element->appendChild(
$dom->createElement( 'priority', $url['priority'] )
);
}
}
$urlset->appendChild( $url_element );
if ( $is_sitemap_index && $is_index_template ) {
$this->sitemaps[ $sitemap_key ][] = array(
'loc' => $url['loc'],
'lastmod' => $url['lastmod'],
);
}
}
if ( ! sgg_pro_enabled() || ! $settings->minimize_sitemap ) {
$dom->formatOutput = true;
}
if ( ! $is_index_template || ! $is_sitemap_index ) {
$ready_sitemap = $dom->saveXML();
if ( $is_sitemap_index ) {
$this->sitemaps[ $sitemap_key ][] = $ready_sitemap;
$this->sitemaps[ $sitemap_key ]['lastmod'] = $url['lastmod'] ?? gmdate( 'c' );
} else {
$this->sitemaps[] = $ready_sitemap;
}
}
}
if ( null === $this->sitemaps ) {
$dom = $this->create_sitemap_dom( $stylesheet_url, $template, $settings );
$urlset = $this->create_sitemap_urlset( $dom, $headers );
$dom->appendChild( $urlset );
$this->sitemaps[] = $dom->saveXML();
}
if ( count( $this->sitemaps ) > 1000 ) {
throw new \LengthException( 'Sitemap index can contains 1000 single sitemaps. Perhaps You trying to submit too many URLs.' );
}
$is_media_index = $is_media_template && $is_sitemap_index;
if ( ! empty( $settings->sitemap_view ) && ( ( 'sitemap' === $template && count( $this->sitemaps ) > 0 ) || $is_media_index ) ) {
$stylesheet_url = sgg_get_sitemap_url( "{$stylesheet_path}?template=sitemap-index", 'sitemap_xsl=sitemap-index', false );
$stylesheet_url = strtok( $stylesheet_url, '&' ); // remove & query string
$dom = $this->create_sitemap_dom( $stylesheet_url, $template, $settings );
$sitemapindex = $dom->createElement( 'sitemapindex' );
$sitemapindex->setAttribute( 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance' );
$sitemapindex->setAttribute( 'xsi:schemaLocation', 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/siteindex.xsd' );
$sitemapindex->setAttribute( 'xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9' );
$dom->appendChild( $sitemapindex );
foreach ( $this->sitemaps as $sitemap_key => $sitemap ) {
if ( ! empty( $sitemap ) && is_array( $sitemap ) ) {
if ( 'page' === $sitemap_key && sgg_get_home_url_with_trailing_slash() === ( $sitemap[0]['loc'] ?? '' ) && 1 < count( $sitemap ) ) {
$sitemap[0] = array(
'loc' => $sitemap[0]['loc'],
'lastmod' => max( $sitemap[0]['lastmod'] ?? '', $sitemap[1]['lastmod'] ?? '' ),
);
if ( ! empty( $sitemap[1] ) ) {
unset( $sitemap[1] );
}
$sitemap = array_values( $sitemap );
}
foreach ( $sitemap as $index => $url ) {
$sitemap_element = $dom->createElement( 'sitemap' );
$sitemap_type = $is_xml ? 'xml' : 'html';
$default_number = $is_media_template ? 1 : '';
$sitemap_number = 0 < $index ? intval( $index ) + 1 : $default_number;
$page_param = 0 < $sitemap_number ? "&page={$sitemap_number}" : '';
$sitemap_element->appendChild(
$dom->createElement(
'loc',
esc_url( sgg_get_sitemap_url( "{$sitemap_key}-sitemap{$sitemap_number}.{$sitemap_type}", "sitemap_{$sitemap_type}=true&inner_sitemap={$sitemap_key}{$page_param}", false ) )
)
);
$sitemap_element->appendChild(
$dom->createElement( 'lastmod', $url['lastmod'] ?? gmdate( 'c' ) )
);
$sitemapindex->appendChild( $sitemap_element );
}
}
}
if ( $is_xml && 'sitemap' === $template ) {
$additional_sitemaps = apply_filters( 'sgg_additional_index_sitemaps', $settings->custom_sitemaps ?? array() );
if ( $settings->enable_image_sitemap ) {
$additional_sitemaps[] = array(
'url' => sgg_get_sitemap_url( $settings->image_sitemap_url, 'image_sitemap' ),
);
}
if ( $settings->enable_video_sitemap ) {
$additional_sitemaps[] = array(
'url' => sgg_get_sitemap_url( $settings->video_sitemap_url, 'video_sitemap' ),
);
}
foreach ( $additional_sitemaps as $additional_sitemap ) {
$sitemap_element = $dom->createElement( 'sitemap' );
$sitemap_url = ! empty( $additional_sitemap['url'] ) ? esc_url( $additional_sitemap['url'] ) : $additional_sitemap;
if ( ! is_string( $sitemap_url ) ) {
continue;
}
$sitemap_element->appendChild(
$dom->createElement( 'loc', $sitemap_url )
);
$sitemap_element->appendChild(
$dom->createElement(
'lastmod',
! empty( $additional_sitemap['lastmod'] ) ? $additional_sitemap['lastmod'] : gmdate( 'c' )
)
);
$sitemapindex->appendChild( $sitemap_element );
}
}
if ( ! sgg_pro_enabled() || ! $settings->minimize_sitemap ) {
$dom->formatOutput = true;
}
$this->sitemapFullURL = $this->baseURL . $this->sitemapIndexFileName;
$this->sitemapIndex = array(
$this->sitemapIndexFileName,
$dom->saveXML(),
);
} else {
if ( $this->createGZipFile ) {
$this->sitemapFullURL = $this->baseURL . $this->sitemapFileName . '.gz';
} else {
$this->sitemapFullURL = $this->baseURL . $this->sitemapFileName;
}
if ( ! empty( $this->sitemaps[0] ) ) {
$this->sitemaps[0] = array(
$this->sitemapFileName,
$this->sitemaps[0],
);
}
}
}
/**
* Create Multilingual Sitemap in memory.
*/
public function createMultilingualSitemap( $urls = array(), $is_xml = true ) {
if ( ! isset( $this->urls ) ) {
throw new \BadMethodCallException( 'To create sitemap, call addUrl or addUrls function first.' );
}
if ( $this->maxURLsPerSitemap > 50000 ) {
throw new \InvalidArgumentException( 'More than 50,000 URLs per single sitemap is not allowed.' );
}
$stylesheet_path = apply_filters( 'sitemap_xsl_template_path', 'sitemap-stylesheet.xsl' );
$settings = ( new Controller() )->get_settings();
$dom = $this->create_sitemap_dom(
sgg_get_sitemap_url( "{$stylesheet_path}?template=multilingual-sitemap", 'sitemap_xsl=sitemap-index', false ),
'',
$settings
);
$sitemapindex = $dom->createElement( 'sitemapindex' );
$sitemapindex->setAttribute( 'xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance' );
$sitemapindex->setAttribute( 'xsi:schemaLocation', 'http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/siteindex.xsd' );
$sitemapindex->setAttribute( 'xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9' );
$dom->appendChild( $sitemapindex );
foreach ( $urls as $url ) {
$sitemap_element = $dom->createElement( 'sitemap' );
$sitemap_element->appendChild(
$dom->createElement(
'loc',
esc_url( $url )
)
);
$sitemap_element->appendChild(
$dom->createElement( 'lastmod', gmdate( DATE_W3C ) )
);
$sitemapindex->appendChild( $sitemap_element );
}
if ( ! sgg_pro_enabled() || ! $settings->minimize_sitemap ) {
$dom->formatOutput = true;
}
$this->sitemapFullURL = $this->baseURL . $this->sitemapIndexFileName;
$this->sitemapIndex = array(
$this->sitemapIndexFileName,
$dom->saveXML(),
);
}
public function create_sitemap_dom( $stylesheet_url, $template = '', $settings = null ) {
$generator_info = 'sitemap-generator-url="https://wpgrim.com" sitemap-generator-version="' . GRIM_SG_VERSION . '"';
$dom = new \DOMDocument( '1.0', 'UTF-8' );
// Get settings if not provided
if ( null === $settings ) {
$settings = ( new Controller() )->get_settings();
}
// Skip XSL stylesheet reference based on settings
$is_index_sitemap = strpos( $stylesheet_url, 'sitemap-index' ) !== false;
$skip_xsl = false;
if ( ! $is_index_sitemap ) {
if ( strpos( $template, VideoSitemap::$template ) !== false ) {
$skip_xsl = ! empty( $settings->hide_video_sitemap_xsl );
} elseif ( strpos( $template, ImageSitemap::$template ) !== false ) {
$skip_xsl = ! empty( $settings->hide_image_sitemap_xsl );
}
}
if ( ! $skip_xsl ) {
$dom->appendChild(
$dom->createProcessingInstruction(
'xml-stylesheet',
'type="text/xsl" href="' . $stylesheet_url . '"'
)
);
}
$dom->appendChild( $dom->createComment( $generator_info ) );
return $dom;
}
public function create_sitemap_urlset( $dom, $headers = array() ) {
$urlset = $dom->createElement( 'urlset' );
$urlset->setAttribute( 'xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9' );
if ( ! empty( $headers ) ) {
foreach ( $headers as $key => $header ) {
$urlset->setAttribute( $key, $header );
}
}
return $urlset;
}
/**
* Returns created sitemaps as array of strings.
* Use it You want to work with sitemap without saving it as files.
* @return array of strings
* @access public
*/
public function toArray() {
if ( isset( $this->sitemapIndex ) ) {
return array_merge( array( $this->sitemapIndex ), $this->sitemaps );
} else {
return $this->sitemaps;
}
}
/**
* Will print sitemaps.
* @access public
*/
public function outputSitemap( $template, $is_xml, $inner_sitemap = null ) {
add_filter( 'trp_stop_translating_page', '__return_true' );
ob_get_clean();
if ( $is_xml ) {
header( 'Content-Type: text/xml; charset=utf-8' );
} else {
ob_start();
}
if ( ! empty( $inner_sitemap ) && sgg_is_sitemap_index( $template ) && ! empty( $this->sitemaps[ $inner_sitemap ][0] ) ) {
echo $this->sitemaps[ $inner_sitemap ][0];
} else {
if ( ! empty( $this->sitemapIndex[1] ) ) {
$template = 'sitemap-index';
echo $this->sitemapIndex[1];
} else {
echo $this->sitemaps[0][1] ?? '';
}
}
if ( ! $is_xml ) {
$xml_source = ob_get_clean();
$xml = new \DOMDocument();
$xml->loadXML( $xml_source );
ob_start();
load_template(
GRIM_SG_PATH . '/templates/xsl/sitemap.php',
false,
compact( 'template', 'is_xml' )
);
$xsl_content = ob_get_clean();
$xsl = new \DOMDocument();
$xsl->loadXML( $xsl_content );
$proc = new \XSLTProcessor();
$proc->importStyleSheet( $xsl );
$dom_tran_obj = $proc->transformToDoc( $xml );
foreach ( $dom_tran_obj->childNodes as $node ) {
echo $dom_tran_obj->saveXML( $node ) . "\n";
}
}
if ( ob_get_contents() ) {
ob_end_flush();
}
}
}