Changeset 3423502
- Timestamp:
- 12/19/2025 09:24:14 AM (6 weeks ago)
- Location:
- easy-xml-sitemap
- Files:
-
- 3 deleted
- 14 edited
- 1 copied
-
assets/wordpress-org (deleted)
-
tags/1.2 (copied) (copied from easy-xml-sitemap/trunk)
-
tags/1.2/changelog.md (modified) (9 diffs)
-
tags/1.2/easy-xml-sitemap.php (modified) (7 diffs)
-
tags/1.2/icon-header.png (deleted)
-
tags/1.2/inc/class-admin-settings.php (modified) (17 diffs)
-
tags/1.2/inc/class-cache.php (modified) (3 diffs)
-
tags/1.2/inc/class-sitemap-controller.php (modified) (13 diffs)
-
tags/1.2/inc/class-xml-renderer.php (modified) (5 diffs)
-
tags/1.2/readme.txt (modified) (3 diffs)
-
trunk/changelog.md (modified) (9 diffs)
-
trunk/easy-xml-sitemap.php (modified) (7 diffs)
-
trunk/icon-header.png (deleted)
-
trunk/inc/class-admin-settings.php (modified) (17 diffs)
-
trunk/inc/class-cache.php (modified) (3 diffs)
-
trunk/inc/class-sitemap-controller.php (modified) (13 diffs)
-
trunk/inc/class-xml-renderer.php (modified) (5 diffs)
-
trunk/readme.txt (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
easy-xml-sitemap/tags/1.2/changelog.md
r3422280 r3423502 9 9 10 10 ### Planned Features 11 - Custom post type support 12 - Sitemap index file generation 11 - Custom post type support with UI controls 13 12 - Image sitemap support 14 13 - Video sitemap support … … 17 16 - REST API endpoints for programmatic access 18 17 - Sitemap statistics and analytics 19 - Integration with popular SEO plugins 18 - Integration with popular SEO plugins (Yoast, Rank Math) 19 - Automatic ping to search engines on content update 20 21 --- 22 23 ## [1.2.0] - 2024-12-19 24 25 ### Added 26 - **Posts Organization Options**: Choose how to organize posts in sitemaps 27 - Single sitemap (all posts in one file) 28 - Organize by date (one sitemap per month/year) 29 - Organize by category (one sitemap per category) 30 - **Dynamic Posts Index**: Automatically generates index based on organization method 31 - **Posts by Date Sitemaps**: `/easy-sitemap/posts-YYYY-MM.xml` for date-based organization 32 - **Posts by Category Sitemaps**: `/easy-sitemap/posts-{category-slug}.xml` for category-based organization 33 - **Radio Button UI**: New settings interface for posts organization selection 34 - **Automatic Cache Regeneration**: Cache clears automatically when organization settings change 35 36 ### Changed 37 - **Posts Sitemap Endpoint**: Now serves as an index when date/category organization is enabled 38 - **Admin Settings Page**: Enhanced UI with organization options and better help text 39 - **Cache Invalidation**: Improved logic to handle dynamic sitemap types 40 - **Rewrite Rules**: Added support for dynamic URL patterns (date and category slugs) 41 42 ### Fixed 43 - **Critical**: Fixed fatal error in `class-sitemap-controller.php` caused by malformed rewrite rules 44 - **Critical**: Resolved duplicate function declarations and incomplete code blocks 45 - **Performance**: Optimized database queries for date-based and category-based sitemaps 46 - **Cache Keys**: Fixed cache key generation for dynamic sitemap types 47 48 ### Technical Details 49 - Added validation for year/month parameters in date-based sitemaps 50 - Added category existence validation for category-based sitemaps 51 - Enhanced `XML_Renderer` with new methods: 52 - `generate_posts_by_date($year, $month)` 53 - `generate_posts_by_category($cat_slug)` 54 - Updated cache clearing to handle dynamic cache keys with wildcards 55 - Improved SQL query performance with direct database cleanup for dynamic caches 56 57 --- 58 59 ## [1.1.3] - 2024-12-15 60 61 ### Added 62 - Plugin icons for WordPress.org directory 63 - Enhanced visual branding 64 65 --- 66 67 ## [1.1.0] - 2024-12-05 68 69 ### Added 70 - **Sitemap Index**: Main sitemap index file at `/easy-sitemap/sitemap.xml` 71 - **robots.txt Integration**: Automatic sitemap URL addition to virtual robots.txt 72 - **Sitemap Index Priority**: Index now appears first in the admin URLs table with visual highlighting 73 - **Physical robots.txt Detection**: Warning in admin when physical robots.txt file exists 74 - **robots.txt Viewer**: Direct link to view current robots.txt from settings page 75 - **Enhanced Admin Interface**: Improved settings page with better organization and help text 76 77 ### Changed 78 - **Sitemap Base Path**: Changed from `/easy-xml-sitemap/` to `/easy-sitemap/` for cleaner URLs 79 - **Settings Menu Name**: Changed from "XML Sitemap" to "Easy Sitemap" 80 - **Main Sitemap URL**: Now `/easy-sitemap/sitemap.xml` (sitemap index) 81 - **Individual Sitemaps**: All other sitemaps listed in the index 82 - **Admin Table Layout**: Sitemap index highlighted with blue background 83 - **robots.txt Implementation**: Uses WordPress filter instead of manual file manipulation 84 85 ### Updated 86 - All rewrite rules updated for new base path 87 - Cache system updated to handle sitemap index 88 - Documentation updated with new URLs and features 89 - Install.md with comprehensive robots.txt troubleshooting 90 91 ### Fixed 92 - robots.txt integration now uses proper WordPress filters 93 - Duplicate sitemap entries prevention in robots.txt 94 - Permalink flushing on activation and settings changes 20 95 21 96 --- … … 46 121 - Manual regeneration button 47 122 - Clean, SEO-friendly URLs 48 - Pattern: `/easy- xml-sitemap/{type}.xml`123 - Pattern: `/easy-sitemap/{type}.xml` 49 124 - Custom rewrite rules 50 125 - Proper XML content-type headers … … 76 151 - Comprehensive `readme.txt` for WordPress.org 77 152 - Detailed `Install.md` with installation and troubleshooting 78 - This`CHANGELOG.md` file153 - `CHANGELOG.md` file 79 154 - `CONTRIBUTING.md` guidelines 80 155 - Proper plugin lifecycle … … 89 164 - Pretty permalinks recommended 90 165 - **File Structure**: 91 - Main plugin file: `easy-xml-sitemap.php` (< 200 lines)166 - Main plugin file: `easy-xml-sitemap.php` 92 167 - Core classes in `inc/` directory 93 168 - Modular architecture for easy maintenance … … 121 196 ### Known Limitations 122 197 - No custom post type support (planned for future release) 123 - No sitemap index file (uses individual sitemaps) 124 - No image or video sitemap support 125 - No automatic ping to search engines 126 - Post priority is static (not calculated based on factors) 198 - No image or video sitemap support (planned) 199 - No automatic ping to search engines (planned) 127 200 128 201 --- … … 148 221 ## Migration Guide 149 222 150 ### From Version X.X to 1.0 151 152 Not applicable for initial release. 153 154 ### Future Upgrades 155 156 When upgrading to future versions: 157 1. Backup your database before upgrading 158 2. Check the changelog for breaking changes 159 3. Test on a staging site first 160 4. Clear all caches after upgrade 161 5. Regenerate sitemaps from Settings > XML Sitemap 162 6. Resubmit sitemaps to search engines if structure changes 223 ### From Version 1.1.x to 1.2.0 224 225 1. **Backup your database** before upgrading 226 2. The plugin will automatically migrate to the new structure 227 3. **Default behavior**: Posts organization defaults to "single" (all posts in one file) 228 4. If you have a large site (10,000+ posts), consider: 229 - Changing to "date" organization for better performance 230 - Changing to "category" organization if content is well-categorized 231 5. **Clear all caches** after upgrade: 232 - Plugin cache (automatic) 233 - WordPress object cache 234 - Page cache (WP Super Cache, W3 Total Cache, etc.) 235 - CDN cache (Cloudflare, etc.) 236 6. **Flush permalinks**: Go to Settings > Permalinks and click "Save Changes" 237 7. **Regenerate sitemaps**: Go to Settings > Easy Sitemap and click "Regenerate All Sitemaps" 238 8. **Resubmit to search engines**: Update your sitemap in Google Search Console and Bing Webmaster Tools 239 9. The main sitemap URL remains the same: `/easy-sitemap/sitemap.xml` 240 241 ### From Version 1.0.x to 1.1.0 242 243 1. **Main sitemap URL changed**: Update from `/easy-xml-sitemap/sitemap-index.xml` to `/easy-sitemap/sitemap.xml` 244 2. **Resubmit to search engines**: Update sitemap URL in Google Search Console and Bing 245 3. All individual sitemap URLs changed from `/easy-xml-sitemap/` to `/easy-sitemap/` 246 4. robots.txt integration now automatic (if no physical robots.txt exists) 247 5. Check Settings > Easy Sitemap for robots.txt status 163 248 164 249 --- … … 168 253 ### Reporting Issues 169 254 If you encounter bugs or have feature requests: 170 1. Check existing issues on the plugin repository171 2. Search the WordPress.org support forum255 1. Check existing issues on the [GitHub repository](https://github.com/andremoura/easy-xml-sitemap/issues) 256 2. Search the [WordPress.org support forum](https://wordpress.org/support/plugin/easy-xml-sitemap/) 172 257 3. Create a new issue with detailed information: 173 258 - Plugin version … … 183 268 2. Describe the use case and benefit 184 269 3. Provide examples or mockups if applicable 270 4. Submit via GitHub Issues or WordPress.org support forum 185 271 186 272 --- -
easy-xml-sitemap/tags/1.2/easy-xml-sitemap.php
r3422350 r3423502 3 3 * Plugin Name: Easy XML Sitemap 4 4 * Description: Lightweight, modular XML sitemap generator for posts, pages, taxonomies, and Google News. 5 * Version: 1. 1.35 * Version: 1.2.0 6 6 * Author: André Moura 7 7 * Author URI: https://www.andremoura.com … … 38 38 * @var string 39 39 */ 40 const VERSION = '1. 1.0';40 const VERSION = '1.2.0'; 41 41 42 42 /** … … 109 109 */ 110 110 private function init_hooks() { 111 // Disable WordPress native sitemap 112 add_filter( 'wp_sitemaps_enabled', '__return_false', 9999 ); 113 114 // Redirect native WP sitemap to our plugin 115 add_action( 'template_redirect', array( $this, 'redirect_native_sitemap' ), 0 ); 116 111 117 // Initialize core components 112 118 add_action( 'init', array( $this, 'init_components' ) ); … … 116 122 register_deactivation_hook( EASY_XML_SITEMAP_FILE, array( $this, 'deactivate' ) ); 117 123 118 // Add sitemap to robots.txt if enabled 119 add_ action( 'do_robots', array( $this, 'add_robots_sitemap' ), 0);124 // Add sitemap to robots.txt if enabled (using filter for proper formatting) 125 add_filter( 'robots_txt', array( $this, 'add_robots_sitemap' ), 10, 2 ); 120 126 } 121 127 … … 130 136 131 137 /** 138 * Redirect native WordPress sitemap to our plugin sitemap 139 */ 140 public function redirect_native_sitemap() { 141 if ( ! isset( $_SERVER['REQUEST_URI'] ) ) { 142 return; 143 } 144 145 $request_uri = sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ); 146 147 // Redirect /wp-sitemap.xml to our sitemap 148 if ( false !== strpos( $request_uri, 'wp-sitemap.xml' ) ) { 149 wp_redirect( home_url( '/easy-sitemap/sitemap.xml' ), 301 ); 150 exit; 151 } 152 } 153 154 /** 132 155 * Plugin activation 133 156 */ … … 139 162 flush_rewrite_rules(); 140 163 141 // Optionally, initialize default settings164 // Initialize default settings 142 165 $defaults = array( 143 'enable_posts' => true, 144 'enable_pages' => true, 145 'enable_categories' => true, 146 'enable_tags' => true, 147 'enable_news' => false, 148 'add_to_robots' => true, 149 'cache_duration' => 3600, 166 'enable_posts' => true, 167 'posts_organization' => 'single', 168 'enable_pages' => true, 169 'enable_categories' => true, 170 'enable_tags' => true, 171 'enable_news' => false, 172 'enable_general' => true, 173 'add_to_robots' => true, 174 'cache_duration' => 3600, 150 175 ); 151 176 … … 171 196 172 197 /** 173 * Add sitemap index to robots.txt 174 */ 175 public function add_robots_sitemap() { 198 * Add sitemap to robots.txt (via filter for proper formatting) 199 * 200 * @param string $output The robots.txt output 201 * @param bool $public Whether the site is public 202 * @return string Modified robots.txt output 203 */ 204 public function add_robots_sitemap( $output, $public ) { 176 205 $settings = get_option( 'easy_xml_sitemap_settings', array() ); 177 206 178 207 // Check if option is enabled 179 208 if ( empty( $settings['add_to_robots'] ) ) { 180 return; 181 } 182 183 // Get sitemap index URL 184 $sitemap_url = Sitemap_Controller::get_sitemap_url( 'sitemap-index' ); 185 186 echo "Sitemap: " . esc_url( $sitemap_url ) . "\n"; 209 return $output; 210 } 211 212 // Get sitemap URL 213 $sitemap_url = home_url( '/easy-sitemap/sitemap.xml' ); 214 215 // Check if sitemap line already exists to avoid duplication 216 if ( false !== strpos( $output, $sitemap_url ) ) { 217 return $output; 218 } 219 220 // Add sitemap at the end with proper formatting 221 $output .= "\nSitemap: " . esc_url( $sitemap_url ) . "\n"; 222 223 return $output; 187 224 } 188 225 } -
easy-xml-sitemap/tags/1.2/inc/class-admin-settings.php
r3422280 r3423502 72 72 public function add_settings_page() { 73 73 add_options_page( 74 __( 'Easy XMLSitemap', 'easy-xml-sitemap' ),75 __( 'Easy XMLSitemap', 'easy-xml-sitemap' ),74 __( 'Easy Sitemap', 'easy-xml-sitemap' ), 75 __( 'Easy Sitemap', 'easy-xml-sitemap' ), 76 76 'manage_options', 77 77 self::PAGE_SLUG, … … 92 92 add_settings_section( 93 93 'easy_xml_sitemap_general_section', 94 __( ' General Settings', 'easy-xml-sitemap' ),94 __( 'Sitemap Configuration', 'easy-xml-sitemap' ), 95 95 array( $this, 'render_general_section' ), 96 96 self::PAGE_SLUG … … 110 110 111 111 add_settings_field( 112 'posts_organization', 113 __( 'Posts Organization', 'easy-xml-sitemap' ), 114 array( $this, 'render_radio_field' ), 115 self::PAGE_SLUG, 116 'easy_xml_sitemap_general_section', 117 array( 118 'label_for' => 'posts_organization', 119 'options' => array( 120 'single' => __( 'Single sitemap (all posts in one file)', 'easy-xml-sitemap' ), 121 'date' => __( 'Organize by date (one sitemap per month/year)', 'easy-xml-sitemap' ), 122 'category' => __( 'Organize by category (one sitemap per category)', 'easy-xml-sitemap' ), 123 ), 124 'default' => 'single', 125 'description' => __( 'How to organize posts in the sitemap. For large sites, organizing by date or category improves performance.', 'easy-xml-sitemap' ), 126 ) 127 ); 128 129 add_settings_field( 112 130 'enable_pages', 113 131 __( 'Pages Sitemap', 'easy-xml-sitemap' ), … … 146 164 147 165 add_settings_field( 166 'enable_general', 167 __( 'General Sitemap', 'easy-xml-sitemap' ), 168 array( $this, 'render_checkbox_field' ), 169 self::PAGE_SLUG, 170 'easy_xml_sitemap_general_section', 171 array( 172 'label_for' => 'enable_general', 173 'description' => __( 'Generate a comprehensive sitemap with all URLs (homepage, posts, pages, categories, tags)', 'easy-xml-sitemap' ), 174 ) 175 ); 176 177 add_settings_field( 148 178 'enable_news', 149 179 __( 'Google News Sitemap', 'easy-xml-sitemap' ), … … 153 183 array( 154 184 'label_for' => 'enable_news', 155 'description' => __( 'Enable Google News compatible sitemap for recent posts ', 'easy-xml-sitemap' ),185 'description' => __( 'Enable Google News compatible sitemap for recent posts (last 2 days)', 'easy-xml-sitemap' ), 156 186 ) 157 187 ); … … 165 195 array( 166 196 'label_for' => 'add_to_robots', 167 'description' => __( 'Automatically add sitemap index URL torobots.txt', 'easy-xml-sitemap' ),197 'description' => __( 'Automatically add sitemap URL to virtual robots.txt', 'easy-xml-sitemap' ), 168 198 ) 169 199 ); … … 177 207 array( 178 208 'label_for' => 'cache_duration', 179 'description' => __( 'How long to cache sitemap output ( default: 3600 seconds)', 'easy-xml-sitemap' ),209 'description' => __( 'How long to cache sitemap output (60 seconds to 1 week, default: 3600)', 'easy-xml-sitemap' ), 180 210 'min' => 60, 181 211 'max' => 604800, … … 192 222 public function sanitize_settings( $input ) { 193 223 $output = array(); 194 195 $output['enable_posts'] = isset( $input['enable_posts'] ) ? (bool) $input['enable_posts'] : false; 196 $output['enable_pages'] = isset( $input['enable_pages'] ) ? (bool) $input['enable_pages'] : false; 197 $output['enable_categories'] = isset( $input['enable_categories'] ) ? (bool) $input['enable_categories'] : false; 198 $output['enable_tags'] = isset( $input['enable_tags'] ) ? (bool) $input['enable_tags'] : false; 199 $output['enable_news'] = isset( $input['enable_news'] ) ? (bool) $input['enable_news'] : false; 200 $output['add_to_robots'] = isset( $input['add_to_robots'] ) ? (bool) $input['add_to_robots'] : false; 224 225 // Store old values to check if posts organization changed 226 $old_settings = get_option( self::OPTION_NAME, array() ); 227 $old_organization = isset( $old_settings['posts_organization'] ) ? $old_settings['posts_organization'] : 'single'; 228 229 $output['enable_posts'] = isset( $input['enable_posts'] ) ? (bool) $input['enable_posts'] : false; 230 $output['posts_organization'] = isset( $input['posts_organization'] ) ? sanitize_key( $input['posts_organization'] ) : 'single'; 231 $output['enable_pages'] = isset( $input['enable_pages'] ) ? (bool) $input['enable_pages'] : false; 232 $output['enable_categories'] = isset( $input['enable_categories'] ) ? (bool) $input['enable_categories'] : false; 233 $output['enable_tags'] = isset( $input['enable_tags'] ) ? (bool) $input['enable_tags'] : false; 234 $output['enable_general'] = isset( $input['enable_general'] ) ? (bool) $input['enable_general'] : false; 235 $output['enable_news'] = isset( $input['enable_news'] ) ? (bool) $input['enable_news'] : false; 236 $output['add_to_robots'] = isset( $input['add_to_robots'] ) ? (bool) $input['add_to_robots'] : false; 201 237 202 238 $output['cache_duration'] = isset( $input['cache_duration'] ) ? absint( $input['cache_duration'] ) : 3600; … … 208 244 if ( $output['cache_duration'] > 604800 ) { 209 245 $output['cache_duration'] = 604800; 246 } 247 248 // Validate posts_organization 249 if ( ! in_array( $output['posts_organization'], array( 'single', 'date', 'category' ), true ) ) { 250 $output['posts_organization'] = 'single'; 251 } 252 253 // Automatically regenerate cache if settings changed 254 $should_regenerate = false; 255 256 // Check if any sitemap enable/disable changed 257 $enable_keys = array( 'enable_posts', 'enable_pages', 'enable_categories', 'enable_tags', 'enable_general', 'enable_news' ); 258 foreach ( $enable_keys as $key ) { 259 $old_value = isset( $old_settings[ $key ] ) ? $old_settings[ $key ] : true; 260 $new_value = $output[ $key ]; 261 if ( $old_value !== $new_value ) { 262 $should_regenerate = true; 263 break; 264 } 265 } 266 267 // Check if posts organization changed 268 if ( $old_organization !== $output['posts_organization'] ) { 269 $should_regenerate = true; 270 } 271 272 // Check if cache duration changed significantly 273 $old_duration = isset( $old_settings['cache_duration'] ) ? $old_settings['cache_duration'] : 3600; 274 if ( $old_duration !== $output['cache_duration'] ) { 275 $should_regenerate = true; 276 } 277 278 // Regenerate cache if needed 279 if ( $should_regenerate ) { 280 // Clear all caches 281 Cache::clear_all(); 282 283 // Also clear any WordPress object cache 284 if ( function_exists( 'wp_cache_flush' ) ) { 285 wp_cache_flush(); 286 } 287 288 // Set a transient to show regeneration happened 289 set_transient( 'easy_xml_sitemap_regenerated', '1', 30 ); 210 290 } 211 291 … … 224 304 ?> 225 305 <div class="wrap"> 226 <h1><?php esc_html_e( 'Easy XML Sitemap ', 'easy-xml-sitemap' ); ?></h1>227 <p><?php esc_html_e( 'Configure the XML sitemap settings for yoursite.', 'easy-xml-sitemap' ); ?></p>228 229 <form method="post" action="options.php" >306 <h1><?php esc_html_e( 'Easy XML Sitemap Settings', 'easy-xml-sitemap' ); ?></h1> 307 <p><?php esc_html_e( 'Configure XML sitemap generation for your WordPress site.', 'easy-xml-sitemap' ); ?></p> 308 309 <form method="post" action="options.php" id="easy-xml-sitemap-settings-form"> 230 310 <?php 231 311 settings_fields( 'easy_xml_sitemap_settings' ); … … 237 317 <hr /> 238 318 239 <h2><?php esc_html_e( 'Sitemap Tools', 'easy-xml-sitemap' ); ?></h2> 240 <p><?php esc_html_e( 'Use the tools below to manually regenerate XML sitemaps.', 'easy-xml-sitemap' ); ?></p> 319 <h2><?php esc_html_e( 'robots.txt Configuration', 'easy-xml-sitemap' ); ?></h2> 320 321 <?php 322 // Check if physical robots.txt exists 323 $robots_file = ABSPATH . 'robots.txt'; 324 if ( file_exists( $robots_file ) ) : 325 ?> 326 <div class="notice notice-warning inline"> 327 <p> 328 <strong><?php esc_html_e( '⚠️ Physical robots.txt detected', 'easy-xml-sitemap' ); ?></strong><br /> 329 <?php esc_html_e( 'A physical robots.txt file exists in your site root. The "Add to robots.txt" option will not work automatically.', 'easy-xml-sitemap' ); ?> 330 </p> 331 <p> 332 <?php esc_html_e( 'To use automatic robots.txt integration, please delete or rename the physical robots.txt file.', 'easy-xml-sitemap' ); ?><br /> 333 <?php esc_html_e( 'Alternatively, manually add this line to your robots.txt:', 'easy-xml-sitemap' ); ?> 334 </p> 335 <p> 336 <code>Sitemap: <?php echo esc_html( home_url( '/easy-sitemap/sitemap.xml' ) ); ?></code> 337 </p> 338 </div> 339 <?php else : ?> 340 <div class="notice notice-success inline"> 341 <p> 342 <strong><?php esc_html_e( '✓ No physical robots.txt found', 'easy-xml-sitemap' ); ?></strong><br /> 343 <?php esc_html_e( 'Virtual robots.txt integration will work correctly if enabled above.', 'easy-xml-sitemap' ); ?> 344 </p> 345 <p> 346 <a href="<?php echo esc_url( home_url( '/robots.txt' ) ); ?>" target="_blank" rel="noopener noreferrer"> 347 <?php esc_html_e( 'View your robots.txt', 'easy-xml-sitemap' ); ?> 348 </a> 349 </p> 350 </div> 351 <?php endif; ?> 352 353 <hr /> 354 355 <h2><?php esc_html_e( 'Cache Management', 'easy-xml-sitemap' ); ?></h2> 356 <p><?php esc_html_e( 'Manually regenerate all sitemap files to clear the cache and rebuild from current content.', 'easy-xml-sitemap' ); ?></p> 241 357 242 358 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>"> 243 359 <?php wp_nonce_field( self::NONCE_ACTION, self::NONCE_FIELD ); ?> 244 360 <input type="hidden" name="action" value="easy_xml_sitemap_regenerate" /> 245 <?php submit_button( __( 'Regenerate Sitemaps', 'easy-xml-sitemap' ), 'secondary' ); ?>361 <?php submit_button( __( 'Regenerate All Sitemaps', 'easy-xml-sitemap' ), 'secondary' ); ?> 246 362 </form> 247 363 364 <hr /> 365 248 366 <h2><?php esc_html_e( 'Sitemap URLs', 'easy-xml-sitemap' ); ?></h2> 249 <p><?php esc_html_e( ' Below are the main sitemap URLs generated by this plugin.', 'easy-xml-sitemap' ); ?></p>367 <p><?php esc_html_e( 'These are the sitemap URLs generated by this plugin. Submit the main sitemap to search engines.', 'easy-xml-sitemap' ); ?></p> 250 368 251 369 <?php 252 370 $sitemap_types = array( 253 'sitemap-index' => __( 'Sitemap Index', 'easy-xml-sitemap' ), 254 'posts' => __( 'Posts Sitemap', 'easy-xml-sitemap' ), 255 'pages' => __( 'Pages Sitemap', 'easy-xml-sitemap' ), 256 'categories' => __( 'Categories Sitemap', 'easy-xml-sitemap' ), 257 'tags' => __( 'Tags Sitemap', 'easy-xml-sitemap' ), 258 'news' => __( 'Google News Sitemap', 'easy-xml-sitemap' ), 371 'sitemap' => array( 372 'label' => __( 'Main Sitemap', 'easy-xml-sitemap' ), 373 'enabled' => true, 374 'url' => home_url( '/easy-sitemap/sitemap.xml' ), 375 ), 376 'posts-index' => array( 377 'label' => __( 'Posts Sitemap', 'easy-xml-sitemap' ), 378 'enabled' => ! empty( $options['enable_posts'] ), 379 'url' => Sitemap_Controller::get_sitemap_url( 'posts-index' ), 380 ), 381 'pages' => array( 382 'label' => __( 'Pages Sitemap', 'easy-xml-sitemap' ), 383 'enabled' => ! empty( $options['enable_pages'] ), 384 'url' => Sitemap_Controller::get_sitemap_url( 'pages' ), 385 ), 386 'categories' => array( 387 'label' => __( 'Categories Sitemap', 'easy-xml-sitemap' ), 388 'enabled' => ! empty( $options['enable_categories'] ), 389 'url' => Sitemap_Controller::get_sitemap_url( 'categories' ), 390 ), 391 'tags' => array( 392 'label' => __( 'Tags Sitemap', 'easy-xml-sitemap' ), 393 'enabled' => ! empty( $options['enable_tags'] ), 394 'url' => Sitemap_Controller::get_sitemap_url( 'tags' ), 395 ), 396 'general' => array( 397 'label' => __( 'General Sitemap', 'easy-xml-sitemap' ), 398 'enabled' => ! empty( $options['enable_general'] ), 399 'url' => Sitemap_Controller::get_sitemap_url( 'general' ), 400 ), 401 'news' => array( 402 'label' => __( 'Google News Sitemap', 'easy-xml-sitemap' ), 403 'enabled' => ! empty( $options['enable_news'] ), 404 'url' => Sitemap_Controller::get_sitemap_url( 'news' ), 405 ), 259 406 ); 260 407 ?> … … 263 410 <thead> 264 411 <tr> 265 <th ><?php esc_html_e( 'Sitemap Type', 'easy-xml-sitemap' ); ?></th>412 <th style="width: 40%;"><?php esc_html_e( 'Sitemap Type', 'easy-xml-sitemap' ); ?></th> 266 413 <th><?php esc_html_e( 'URL', 'easy-xml-sitemap' ); ?></th> 267 414 </tr> 268 415 </thead> 269 416 <tbody> 270 <?php foreach ( $sitemap_types as $type => $ label) : ?>417 <?php foreach ( $sitemap_types as $type => $data ) : ?> 271 418 <?php 272 $enabled = true; 273 if ( 'sitemap-index' !== $type ) { 274 if ( 'news' === $type ) { 275 $enabled = ! empty( $options['enable_news'] ); 276 } elseif ( 'posts' === $type ) { 277 $enabled = ! empty( $options['enable_posts'] ); 278 } elseif ( 'pages' === $type ) { 279 $enabled = ! empty( $options['enable_pages'] ); 280 } elseif ( 'categories' === $type ) { 281 $enabled = ! empty( $options['enable_categories'] ); 282 } elseif ( 'tags' === $type ) { 283 $enabled = ! empty( $options['enable_tags'] ); 284 } 285 } 286 287 $url = Sitemap_Controller::get_sitemap_url( $type ); 288 289 $highlight_style = ( 'sitemap-index' === $type ) ? 'background-color: #f0f6fc;' : ''; 419 $url = $data['url']; 420 $highlight_style = ( 'sitemap' === $type ) ? 'background-color: #f0f6fc; font-weight: 600;' : ''; 290 421 ?> 291 422 <tr<?php if ( $highlight_style ) : ?> style="<?php echo esc_attr( $highlight_style ); ?>"<?php endif; ?>> 292 <td><strong><?php echo esc_html( $label ); ?></strong></td>293 423 <td> 294 <?php if ( $enabled ) : ?> 424 <?php echo esc_html( $data['label'] ); ?> 425 <?php if ( 'sitemap' === $type ) : ?> 426 <br /><small style="color: #2271b1;"><?php esc_html_e( '← Submit this URL to Google Search Console and Bing Webmaster Tools', 'easy-xml-sitemap' ); ?></small> 427 <?php endif; ?> 428 </td> 429 <td> 430 <?php if ( $data['enabled'] ) : ?> 295 431 <a href="<?php echo esc_url( $url ); ?>" target="_blank" rel="noopener noreferrer"> 296 432 <?php echo esc_html( $url ); ?> 297 433 </a> 298 434 <?php else : ?> 299 <em><?php esc_html_e( 'Disabled', 'easy-xml-sitemap' ); ?></em> 435 <span style="color: #999;"> 436 <?php esc_html_e( 'Disabled', 'easy-xml-sitemap' ); ?> 437 <small>(<?php echo esc_html( $url ); ?>)</small> 438 </span> 300 439 <?php endif; ?> 301 440 </td> … … 304 443 </tbody> 305 444 </table> 445 446 <hr /> 447 448 <h2><?php esc_html_e( 'Search Engine Submission', 'easy-xml-sitemap' ); ?></h2> 449 <p><?php esc_html_e( 'Submit your main sitemap to search engines for better crawling and indexing:', 'easy-xml-sitemap' ); ?></p> 450 <ul style="list-style: disc; margin-left: 20px;"> 451 <li> 452 <strong>Google Search Console:</strong> 453 <a href="https://search.google.com/search-console" target="_blank" rel="noopener noreferrer"> 454 <?php esc_html_e( 'Submit Sitemap', 'easy-xml-sitemap' ); ?> 455 </a> 456 </li> 457 <li> 458 <strong>Bing Webmaster Tools:</strong> 459 <a href="https://www.bing.com/webmasters" target="_blank" rel="noopener noreferrer"> 460 <?php esc_html_e( 'Submit Sitemap', 'easy-xml-sitemap' ); ?> 461 </a> 462 </li> 463 </ul> 306 464 </div> 307 465 <?php … … 313 471 public function render_general_section() { 314 472 ?> 315 <p><?php esc_html_e( ' Configure which content types should be included in XML sitemaps and how caching should behave.', 'easy-xml-sitemap' ); ?></p>473 <p><?php esc_html_e( 'Enable or disable different sitemap types and configure caching behavior.', 'easy-xml-sitemap' ); ?></p> 316 474 <?php 317 475 } … … 334 492 </label> 335 493 <?php 494 } 495 496 /** 497 * Render radio field. 498 * 499 * @param array $args Field arguments. 500 */ 501 public function render_radio_field( $args ) { 502 $options = get_option( self::OPTION_NAME, array() ); 503 $id = isset( $args['label_for'] ) ? $args['label_for'] : ''; 504 $choices = isset( $args['options'] ) ? $args['options'] : array(); 505 $default = isset( $args['default'] ) ? $args['default'] : ''; 506 $value = isset( $options[ $id ] ) ? $options[ $id ] : $default; 507 508 foreach ( $choices as $choice_value => $choice_label ) { 509 ?> 510 <label style="display: block; margin-bottom: 8px;"> 511 <input 512 type="radio" 513 name="<?php echo esc_attr( self::OPTION_NAME . '[' . $id . ']' ); ?>" 514 value="<?php echo esc_attr( $choice_value ); ?>" 515 <?php checked( $value, $choice_value ); ?> 516 /> 517 <?php echo esc_html( $choice_label ); ?> 518 </label> 519 <?php 520 } 521 522 if ( ! empty( $args['description'] ) ) { 523 ?> 524 <p class="description"><?php echo esc_html( $args['description'] ); ?></p> 525 <?php 526 } 336 527 } 337 528 … … 354 545 min="<?php echo esc_attr( $min ); ?>" 355 546 max="<?php echo esc_attr( $max ); ?>" 547 style="width: 150px;" 356 548 /> 357 549 <?php if ( ! empty( $args['description'] ) ) : ?> … … 400 592 ?> 401 593 <div class="notice notice-success is-dismissible"> 402 <p><?php esc_html_e( ' All sitemaps have been regenerated successfully.', 'easy-xml-sitemap' ); ?></p>594 <p><?php esc_html_e( '✓ All sitemaps have been regenerated successfully.', 'easy-xml-sitemap' ); ?></p> 403 595 </div> 404 596 <?php 405 597 } 598 599 if ( isset( $_GET['settings-updated'] ) && 'true' === sanitize_text_field( wp_unslash( $_GET['settings-updated'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended 600 // Check if regeneration happened 601 $regenerated = get_transient( 'easy_xml_sitemap_regenerated' ); 602 if ( $regenerated ) { 603 delete_transient( 'easy_xml_sitemap_regenerated' ); 604 ?> 605 <div class="notice notice-success is-dismissible"> 606 <p><?php esc_html_e( '✓ Settings saved successfully. Cache has been automatically regenerated.', 'easy-xml-sitemap' ); ?></p> 607 </div> 608 <?php 609 } else { 610 ?> 611 <div class="notice notice-success is-dismissible"> 612 <p><?php esc_html_e( '✓ Settings saved successfully.', 'easy-xml-sitemap' ); ?></p> 613 </div> 614 <?php 615 } 616 } 406 617 } 407 618 } -
easy-xml-sitemap/tags/1.2/inc/class-cache.php
r3422280 r3423502 65 65 */ 66 66 public static function clear_all() { 67 $sitemap_types = array( 'posts', 'pages', 'tags', 'categories', 'general', 'news', 'sitemap-index' ); 67 $sitemap_types = array( 68 'posts', 69 'posts-index', 70 'pages', 71 'tags', 72 'categories', 73 'general', 74 'news', 75 'sitemap-index' 76 ); 68 77 69 78 foreach ( $sitemap_types as $type ) { 70 79 self::clear( $type ); 71 80 } 81 82 // Clear dynamic caches (posts-date-*, posts-category-*) 83 global $wpdb; 84 85 $pattern_date = $wpdb->esc_like( '_transient_' . self::CACHE_PREFIX . 'posts-date-' ) . '%'; 86 $pattern_cat = $wpdb->esc_like( '_transient_' . self::CACHE_PREFIX . 'posts-category-' ) . '%'; 87 88 $wpdb->query( 89 $wpdb->prepare( 90 "DELETE FROM {$wpdb->options} 91 WHERE option_name LIKE %s 92 OR option_name LIKE %s", 93 $pattern_date, 94 $pattern_cat 95 ) 96 ); 97 98 // Clear timeout transients too 99 $pattern_date_timeout = $wpdb->esc_like( '_transient_timeout_' . self::CACHE_PREFIX . 'posts-date-' ) . '%'; 100 $pattern_cat_timeout = $wpdb->esc_like( '_transient_timeout_' . self::CACHE_PREFIX . 'posts-category-' ) . '%'; 101 102 $wpdb->query( 103 $wpdb->prepare( 104 "DELETE FROM {$wpdb->options} 105 WHERE option_name LIKE %s 106 OR option_name LIKE %s", 107 $pattern_date_timeout, 108 $pattern_cat_timeout 109 ) 110 ); 72 111 } 73 112 … … 139 178 if ( 'post' === $post_type ) { 140 179 self::clear( 'posts' ); 180 self::clear( 'posts-index' ); 141 181 self::clear( 'tags' ); 142 182 self::clear( 'categories' ); 143 self::clear( 'news' ); // Posts may appear in news sitemap 183 self::clear( 'news' ); 184 185 // Clear date-specific cache 186 $post = get_post( $post_id ); 187 if ( $post ) { 188 $year = gmdate( 'Y', strtotime( $post->post_date ) ); 189 $month = gmdate( 'm', strtotime( $post->post_date ) ); 190 self::clear( 'posts-date-' . $year . '-' . $month ); 191 } 192 193 // Clear category-specific caches 194 $categories = get_the_category( $post_id ); 195 if ( ! empty( $categories ) ) { 196 foreach ( $categories as $category ) { 197 self::clear( 'posts-category-' . $category->slug ); 198 } 199 } 200 144 201 } elseif ( 'page' === $post_type ) { 145 202 self::clear( 'pages' ); … … 161 218 if ( 'category' === $taxonomy ) { 162 219 self::clear( 'categories' ); 220 self::clear( 'posts-index' ); 221 222 // Clear category-specific posts cache 223 $term = get_term( $term_id, $taxonomy ); 224 if ( $term && ! is_wp_error( $term ) ) { 225 self::clear( 'posts-category-' . $term->slug ); 226 } 227 163 228 } elseif ( 'post_tag' === $taxonomy ) { 164 229 self::clear( 'tags' ); -
easy-xml-sitemap/tags/1.2/inc/class-sitemap-controller.php
r3422280 r3423502 35 35 * @var array 36 36 */ 37 private $valid_types = array( 'posts', 'pages', 'tags', 'categories', 'general', 'news', 'sitemap-index' ); 37 private $valid_types = array( 38 'posts', 39 'posts-index', 40 'posts-date', 41 'posts-category', 42 'pages', 43 'tags', 44 'categories', 45 'general', 46 'news', 47 'sitemap-index' 48 ); 38 49 39 50 /** … … 61 72 * Register rewrite rules for sitemap URLs 62 73 */ 63 public staticfunction register_rewrite_rules() {74 public function register_rewrite_rules() { 64 75 $slug = self::SITEMAP_SLUG; 65 76 66 // Pattern: /easy-sitemap/posts.xml or /easy-sitemap/sitemap-index.xml 77 // Main sitemap: /easy-sitemap/sitemap.xml 78 add_rewrite_rule( 79 '^' . $slug . '/sitemap\.xml$', 80 'index.php?easy_sitemap_type=sitemap-index', 81 'top' 82 ); 83 84 // Posts index: /easy-sitemap/posts-index.xml 85 add_rewrite_rule( 86 '^' . $slug . '/posts-index\.xml$', 87 'index.php?easy_sitemap_type=posts-index', 88 'top' 89 ); 90 91 // Posts by date: /easy-sitemap/posts-2024-12.xml 92 add_rewrite_rule( 93 '^' . $slug . '/posts-([0-9]{4})-([0-9]{2})\.xml$', 94 'index.php?easy_sitemap_type=posts-date&easy_sitemap_year=$matches[1]&easy_sitemap_month=$matches[2]', 95 'top' 96 ); 97 98 // Posts by category: /easy-sitemap/posts-{category-slug}.xml 99 add_rewrite_rule( 100 '^' . $slug . '/posts-([a-z0-9-]+)\.xml$', 101 'index.php?easy_sitemap_type=posts-category&easy_sitemap_cat=$matches[1]', 102 'top' 103 ); 104 105 // Other sitemaps: /easy-sitemap/{type}.xml 67 106 add_rewrite_rule( 68 107 '^' . $slug . '/([a-z-]+)\.xml$', … … 80 119 public function add_query_vars( $vars ) { 81 120 $vars[] = 'easy_sitemap_type'; 121 $vars[] = 'easy_sitemap_year'; 122 $vars[] = 'easy_sitemap_month'; 123 $vars[] = 'easy_sitemap_cat'; 82 124 return $vars; 83 125 } … … 100 142 } 101 143 102 // Check if this sitemap is enabled (except for sitemap-index which is always available if enabled) 103 if ( 'sitemap-index' !== $sitemap_type && ! $this->is_sitemap_enabled( $sitemap_type ) ) { 104 $this->send_404(); 105 return; 106 } 107 108 // Check if sitemap index is enabled 109 if ( 'sitemap-index' === $sitemap_type ) { 110 $settings = get_option( 'easy_xml_sitemap_settings', array() ); 111 $index_enabled = isset( $settings['enable_index'] ) ? $settings['enable_index'] : true; 112 113 if ( ! $index_enabled ) { 144 // Special handling for posts-date 145 if ( 'posts-date' === $sitemap_type ) { 146 $year = get_query_var( 'easy_sitemap_year', false ); 147 $month = get_query_var( 'easy_sitemap_month', false ); 148 149 if ( ! $year || ! $month ) { 150 $this->send_404(); 151 return; 152 } 153 154 // Validate year and month 155 if ( ! is_numeric( $year ) || ! is_numeric( $month ) ) { 156 $this->send_404(); 157 return; 158 } 159 160 $year = intval( $year ); 161 $month = intval( $month ); 162 163 if ( $year < 1970 || $year > 2100 || $month < 1 || $month > 12 ) { 164 $this->send_404(); 165 return; 166 } 167 } 168 169 // Special handling for posts-category 170 if ( 'posts-category' === $sitemap_type ) { 171 $cat_slug = get_query_var( 'easy_sitemap_cat', false ); 172 173 if ( ! $cat_slug ) { 174 $this->send_404(); 175 return; 176 } 177 178 // Validate that category exists 179 $category = get_category_by_slug( $cat_slug ); 180 if ( ! $category ) { 181 $this->send_404(); 182 return; 183 } 184 } 185 186 // Check if sitemap is enabled (except for index and dynamic types) 187 $always_available = array( 'sitemap-index', 'posts-index', 'posts-date', 'posts-category' ); 188 if ( ! in_array( $sitemap_type, $always_available, true ) ) { 189 if ( ! $this->is_sitemap_enabled( $sitemap_type ) ) { 114 190 $this->send_404(); 115 191 return; … … 127 203 */ 128 204 private function serve_sitemap( $sitemap_type ) { 205 // Build cache key 206 $cache_key = $sitemap_type; 207 208 // Add dynamic parameters to cache key 209 if ( 'posts-date' === $sitemap_type ) { 210 $year = get_query_var( 'easy_sitemap_year' ); 211 $month = get_query_var( 'easy_sitemap_month' ); 212 $cache_key .= '-' . $year . '-' . $month; 213 } elseif ( 'posts-category' === $sitemap_type ) { 214 $cat_slug = get_query_var( 'easy_sitemap_cat' ); 215 $cache_key .= '-' . $cat_slug; 216 } 217 129 218 // Try to get from cache 130 $xml = Cache::get( $ sitemap_type);219 $xml = Cache::get( $cache_key ); 131 220 132 221 // If not cached, generate fresh … … 136 225 // Store in cache 137 226 if ( ! empty( $xml ) ) { 138 Cache::set( $ sitemap_type, $xml );227 Cache::set( $cache_key, $xml ); 139 228 } 140 229 } … … 158 247 159 248 case 'posts': 160 return XML_Renderer::generate_posts_sitemap(); 249 // Legacy: redirect to posts-index logic 250 return XML_Renderer::generate_posts_index(); 251 252 case 'posts-index': 253 return XML_Renderer::generate_posts_index(); 254 255 case 'posts-date': 256 $year = get_query_var( 'easy_sitemap_year' ); 257 $month = get_query_var( 'easy_sitemap_month' ); 258 return XML_Renderer::generate_posts_by_date( $year, $month ); 259 260 case 'posts-category': 261 $cat_slug = get_query_var( 'easy_sitemap_cat' ); 262 return XML_Renderer::generate_posts_by_category( $cat_slug ); 161 263 162 264 case 'pages': … … 198 300 */ 199 301 private function send_xml_headers() { 200 // Prevent caching by some plugins/servers201 302 if ( ! headers_sent() ) { 202 303 status_header( 200 ); … … 204 305 header( 'X-Robots-Tag: noindex, follow', true ); 205 306 206 // Optional: Add cache control headers307 // Cache control headers 207 308 $cache_duration = $this->get_cache_duration(); 208 309 header( 'Cache-Control: max-age=' . $cache_duration ); … … 236 337 * Get sitemap URL for a specific type 237 338 * 238 * @param string $type Sitemap type (posts, pages, sitemap-index, etc.)339 * @param string $type Sitemap type 239 340 * @return string Full URL to the sitemap 240 341 */ … … 253 354 $urls = array(); 254 355 255 // Add sitemap index first if enabled 256 $index_enabled = isset( $settings['enable_index'] ) ? $settings['enable_index'] : true; 257 if ( $index_enabled ) { 258 $urls['sitemap-index'] = self::get_sitemap_url( 'sitemap-index' ); 259 } 356 // Add sitemap index first 357 $urls['sitemap-index'] = self::get_sitemap_url( 'sitemap-index' ); 260 358 261 359 // Add other sitemaps 262 foreach ( $controller->valid_types as $type ) { 263 // Skip sitemap-index as we already added it 264 if ( 'sitemap-index' === $type ) { 265 continue; 266 } 267 268 $key = 'enable_' . $type; 269 if ( isset( $settings[ $key ] ) && $settings[ $key ] ) { 360 $sitemap_map = array( 361 'posts-index' => 'enable_posts', 362 'pages' => 'enable_pages', 363 'tags' => 'enable_tags', 364 'categories' => 'enable_categories', 365 'general' => 'enable_general', 366 'news' => 'enable_news', 367 ); 368 369 foreach ( $sitemap_map as $type => $setting_key ) { 370 if ( isset( $settings[ $setting_key ] ) && $settings[ $setting_key ] ) { 270 371 $urls[ $type ] = self::get_sitemap_url( $type ); 271 372 } … … 277 378 /** 278 379 * Regenerate all sitemaps (clear cache) 279 * Used by admin settings page280 380 * 281 381 * @return bool True on success 282 382 */ 283 383 public static function regenerate_all_sitemaps() { 284 // Check capability285 384 if ( ! current_user_can( 'manage_options' ) ) { 286 385 return false; … … 291 390 return true; 292 391 } 293 294 /**295 * Regenerate a specific sitemap (clear its cache)296 *297 * @param string $sitemap_type Type of sitemap298 * @return bool True on success299 */300 public static function regenerate_sitemap( $sitemap_type ) {301 // Check capability302 if ( ! current_user_can( 'manage_options' ) ) {303 return false;304 }305 306 $controller = self::get_instance();307 308 if ( ! in_array( $sitemap_type, $controller->valid_types, true ) ) {309 return false;310 }311 312 Cache::clear( $sitemap_type );313 314 return true;315 }316 392 } -
easy-xml-sitemap/tags/1.2/inc/class-xml-renderer.php
r3422280 r3423502 26 26 public static function get_xml_header( $type = 'standard' ) { 27 27 $xsl_url = plugins_url( 'sitemap.xsl', EASY_XML_SITEMAP_FILE ); 28 $generator_url = 'http ://wordpress.andremoura.com';28 $generator_url = 'https://wordpress.andremoura.com'; 29 29 $version = EASY_XML_SITEMAP_VERSION; 30 30 $generated_on = current_time( 'mysql' ); … … 114 114 * Render a Google News URL entry 115 115 * 116 * @param string $url The URL117 * @param array $news_data News-specific data116 * @param string $url The URL 117 * @param array $news_data News-specific data 118 118 * @return string XML for single news URL entry 119 119 */ … … 172 172 // Add enabled sitemaps to index 173 173 $sitemap_types = array( 174 'posts '=> 'enable_posts',175 'pages' => 'enable_pages',176 'tags' => 'enable_tags',177 'categories' => 'enable_categories',178 'general' => 'enable_general',179 'news' => 'enable_news',174 'posts-index' => 'enable_posts', 175 'pages' => 'enable_pages', 176 'tags' => 'enable_tags', 177 'categories' => 'enable_categories', 178 'general' => 'enable_general', 179 'news' => 'enable_news', 180 180 ); 181 181 … … 195 195 196 196 /** 197 * Generate posts sitemap 197 * Generate posts sitemap index (organized by date or category) 198 * 199 * @return string Complete XML sitemap for posts 200 */ 201 public static function generate_posts_index() { 202 $settings = get_option( 'easy_xml_sitemap_settings', array() ); 203 $organization = isset( $settings['posts_organization'] ) ? $settings['posts_organization'] : 'single'; 204 205 // If single organization, return simple sitemap 206 if ( 'single' === $organization ) { 207 return self::generate_posts_sitemap(); 208 } 209 210 // Otherwise, generate an index 211 $xml = self::get_xml_header( 'index' ); 212 213 if ( 'date' === $organization ) { 214 // Get all months/years with posts 215 global $wpdb; 216 217 $dates = $wpdb->get_results( 218 "SELECT DISTINCT YEAR(post_date) as year, MONTH(post_date) as month, MAX(post_modified) as lastmod 219 FROM {$wpdb->posts} 220 WHERE post_type = 'post' 221 AND post_status = 'publish' 222 GROUP BY YEAR(post_date), MONTH(post_date) 223 ORDER BY year DESC, month DESC" 224 ); 225 226 foreach ( $dates as $date ) { 227 $year = str_pad( $date->year, 4, '0', STR_PAD_LEFT ); 228 $month = str_pad( $date->month, 2, '0', STR_PAD_LEFT ); 229 230 $url = home_url( '/easy-sitemap/posts-' . $year . '-' . $month . '.xml' ); 231 $lastmod = self::format_lastmod( $date->lastmod ); 232 233 $xml .= self::render_sitemap_entry( $url, $lastmod ); 234 } 235 236 } elseif ( 'category' === $organization ) { 237 // Get all categories with posts 238 $categories = get_categories( array( 239 'hide_empty' => true, 240 'orderby' => 'name', 241 'order' => 'ASC', 242 ) ); 243 244 foreach ( $categories as $category ) { 245 // Use category slug directly (WordPress ensures it's URL-safe) 246 $category_slug = $category->slug; 247 248 // Build URL: /easy-sitemap/posts-{slug}.xml (not posts-category-{slug}) 249 $url = home_url( '/easy-sitemap/posts-' . $category_slug . '.xml' ); 250 $lastmod = gmdate( 'c' ); 251 252 $xml .= self::render_sitemap_entry( $url, $lastmod ); 253 } 254 } 255 256 $xml .= self::get_xml_footer( 'index' ); 257 258 return $xml; 259 } 260 261 /** 262 * Generate posts sitemap (single file, all posts) 198 263 * 199 264 * @return string Complete XML sitemap for posts … … 208 273 'orderby' => 'modified', 209 274 'order' => 'DESC', 275 'meta_query' => array( 276 'relation' => 'OR', 277 array( 278 'key' => '_easy_xml_sitemap_exclude', 279 'compare' => 'NOT EXISTS', 280 ), 281 array( 282 'key' => '_easy_xml_sitemap_exclude', 283 'value' => '1', 284 'compare' => '!=', 285 ), 286 ), 287 ); 288 289 $posts = get_posts( $args ); 290 291 foreach ( $posts as $post ) { 292 $url = get_permalink( $post->ID ); 293 $lastmod = self::format_lastmod( $post->post_modified_gmt ); 294 $priority = '0.6'; 295 296 $xml .= self::render_url( $url, $lastmod, $priority ); 297 } 298 299 $xml .= self::get_xml_footer(); 300 301 return $xml; 302 } 303 304 /** 305 * Generate posts sitemap for a specific month/year 306 * 307 * @param string $year Year (YYYY) 308 * @param string $month Month (MM) 309 * @return string Complete XML sitemap for posts in that date 310 */ 311 public static function generate_posts_by_date( $year, $month ) { 312 $xml = self::get_xml_header(); 313 314 $args = array( 315 'post_type' => 'post', 316 'post_status' => 'publish', 317 'posts_per_page' => -1, 318 'orderby' => 'modified', 319 'order' => 'DESC', 320 'date_query' => array( 321 array( 322 'year' => intval( $year ), 323 'month' => intval( $month ), 324 ), 325 ), 326 'meta_query' => array( 327 'relation' => 'OR', 328 array( 329 'key' => '_easy_xml_sitemap_exclude', 330 'compare' => 'NOT EXISTS', 331 ), 332 array( 333 'key' => '_easy_xml_sitemap_exclude', 334 'value' => '1', 335 'compare' => '!=', 336 ), 337 ), 338 ); 339 340 $posts = get_posts( $args ); 341 342 foreach ( $posts as $post ) { 343 $url = get_permalink( $post->ID ); 344 $lastmod = self::format_lastmod( $post->post_modified_gmt ); 345 $priority = '0.6'; 346 347 $xml .= self::render_url( $url, $lastmod, $priority ); 348 } 349 350 $xml .= self::get_xml_footer(); 351 352 return $xml; 353 } 354 355 /** 356 * Generate posts sitemap for a specific category 357 * 358 * @param string $cat_slug Category slug 359 * @return string Complete XML sitemap for posts in that category 360 */ 361 public static function generate_posts_by_category( $cat_slug ) { 362 $xml = self::get_xml_header(); 363 364 // Get category by slug 365 $category = get_category_by_slug( $cat_slug ); 366 367 if ( ! $category ) { 368 return $xml . self::get_xml_footer(); 369 } 370 371 $args = array( 372 'post_type' => 'post', 373 'post_status' => 'publish', 374 'posts_per_page' => -1, 375 'orderby' => 'modified', 376 'order' => 'DESC', 377 'cat' => $category->term_id, 210 378 'meta_query' => array( 211 379 'relation' => 'OR', -
easy-xml-sitemap/tags/1.2/readme.txt
r3422350 r3423502 5 5 Tested up to: 6.9 6 6 Requires PHP: 7.2 7 Stable tag: 1. 1.37 Stable tag: 1.2.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html 10 10 11 Lightweight XML sitemap generator for posts, pages, taxonomies and Google News, with sitemap indexand robots.txt integration.11 Lightweight XML sitemap generator with posts organization options, sitemap index, and robots.txt integration. 12 12 13 13 == Description == 14 14 15 Easy XML Sitemap is a lightweight and efficient plugin that generates XML sitemaps for your WordPress site. It focuses on performance, modularity, and compatibility with custom setups.15 Easy XML Sitemap is a lightweight and efficient plugin that generates XML sitemaps for your WordPress site. It focuses on performance, modularity, and scalability for sites of all sizes. 16 16 17 17 **Key Features** 18 18 19 * Automatically generates XML sitemaps for: 20 + Posts 19 * **Flexible Posts Organization**: Choose how to organize your posts sitemaps 20 + Single sitemap (all posts in one file) 21 + Organize by date (one sitemap per month/year) - ideal for news sites 22 + Organize by category (one sitemap per category) - great for multi-topic blogs 23 * **Sitemap Index**: Automatically generates a sitemap index (`sitemap.xml`) 24 * **Multiple Sitemap Types**: 25 + Posts (with organization options) 21 26 + Pages 22 + Custom post types 23 + Taxonomies 24 + Google News (optional) 25 * Sitemap index file (`sitemap-index.xml`) for improved scalability 26 * Support for large sites with pagination and resource-friendly queries 27 * Simple per-post exclusion option 28 * Robots.txt integration (optional) 29 * Works alongside popular SEO plugins (Yoast, Rank Math, All in One SEO, etc.) 30 * Filters and actions for developers to extend and customize behavior 31 * No front-end bloat – all output is XML 32 33 **Performance-focused** 34 35 This plugin was built to use efficient database queries and optional caching to keep your site fast, even with large content libraries. 36 37 **Developer-friendly** 27 + Categories 28 + Tags 29 + General (comprehensive all-in-one) 30 + Google News (optional, last 2 days) 31 * **robots.txt Integration**: Automatic sitemap URL addition to virtual robots.txt 32 * **Per-Post/Page Exclusion**: Simple checkbox to exclude individual content 33 * **Smart Caching System**: Configurable cache duration with automatic invalidation 34 * **Performance-Optimized**: Efficient queries designed for large content libraries 35 * **Developer-Friendly**: Filters, actions, and clean code structure 36 37 **Perfect For** 38 39 * Blogs with 10,000+ posts (use date or category organization) 40 * News sites publishing frequently 41 * Multi-category content sites 42 * Small to enterprise-level WordPress sites 43 * Developers who need extensibility 44 45 **Works Alongside Popular SEO Plugins** 46 47 Compatible with Yoast SEO, Rank Math, All in One SEO, and others. Simply disable their sitemap feature and use Easy XML Sitemap for better performance. 48 49 **Developer-Friendly** 38 50 39 51 All core components are structured in classes and namespaced, with hooks provided throughout: … … 44 56 * `easy_xml_sitemap_after_clear_cache` 45 57 * `easy_xml_sitemap_meta_box_post_types` 58 * `easy_xml_sitemap_cache_duration` 46 59 * and more… 47 60 48 61 == Installation == 49 62 50 1. Upload the `easy-xml-sitemap` folder to the `/wp-content/plugins/` directory, or install the plugin from the WordPress.org plugin repository. 51 2. Activate the plugin through the **Plugins** menu in WordPress. 52 3. Go to **Settings → Reading** or the plugin settings page (if available) to configure any additional options. 53 4. Visit `https://your-site.com/sitemap-index.xml` to view your sitemap index. 63 **Automatic Installation** 64 65 1. Go to WordPress admin → Plugins → Add New 66 2. Search for "Easy XML Sitemap" 67 3. Click "Install Now" → "Activate" 68 4. Go to Settings → Easy Sitemap to configure 69 70 **Manual Installation** 71 72 1. Download the plugin ZIP file 73 2. Go to Plugins → Add New → Upload Plugin 74 3. Choose the ZIP file and click "Install Now" 75 4. Activate the plugin 76 5. Configure at Settings → Easy Sitemap 77 78 **After Installation** 79 80 1. Visit Settings → Easy Sitemap 81 2. Choose your posts organization method: 82 - **Single**: All posts in one file (best for <5,000 posts) 83 - **Date**: One sitemap per month (best for news/time-based sites) 84 - **Category**: One sitemap per category (best for topic-based sites) 85 3. Enable/disable other sitemap types as needed 86 4. Configure cache duration (default: 1 hour) 87 5. Save settings 88 6. Go to Settings → Permalinks and click "Save Changes" (flush rewrite rules) 89 7. Visit `https://your-site.com/easy-sitemap/sitemap.xml` to verify 90 8. Submit your sitemap to Google Search Console and Bing Webmaster Tools 54 91 55 92 == Frequently Asked Questions == … … 57 94 = Where is my sitemap located? = 58 95 59 By default, the sitemap index is available at: 60 61 `https://your-site.com/sitemap-index.xml` 62 63 Individual sitemaps for content types will be available under URLs like: 64 65 `https://your-site.com/sitemap-post-1.xml` 66 `https://your-site.com/sitemap-page-1.xml` 67 68 The exact URLs may vary depending on your permalink structure. 69 70 = Does this plugin conflict with SEO plugins like Yoast SEO or Rank Math? = 71 72 The plugin is designed to be compatible with common SEO plugins. If another plugin already provides XML sitemaps, you may choose to disable that feature in the SEO plugin settings, or configure Easy XML Sitemap to avoid overlapping functionality. 73 74 = Does it support custom post types and taxonomies? = 75 76 Yes. Custom post types and taxonomies that are set to be public can be included in the sitemap. The plugin uses WordPress APIs to detect and handle them. 77 78 = How can I exclude specific posts or pages from the sitemap? = 79 80 You can exclude individual posts or pages directly from the edit screen by using the **XML Sitemap Options** meta box and checking the option to exclude the content from sitemaps. 81 82 = Can I customize which post types or taxonomies are included? = 83 84 Yes. The plugin provides filters that allow developers to customize which post types and taxonomies are included in the sitemap. Please refer to the developer documentation or source code comments for examples. 85 86 = Does this plugin submit the sitemap to Google or other search engines? = 87 88 No. This plugin generates and serves the XML sitemap files. You can manually submit your sitemap URL in Google Search Console, Bing Webmaster Tools, and similar services, or rely on search engines to discover it via `robots.txt`. 96 The main sitemap index is at: 97 `https://your-site.com/easy-sitemap/sitemap.xml` 98 99 Individual sitemaps are automatically generated based on your organization settings. 100 101 = Does this plugin conflict with SEO plugins? = 102 103 No conflicts. This plugin works alongside popular SEO plugins. If your SEO plugin has sitemap functionality, you can disable it and use Easy XML Sitemap instead for better performance on large sites. 104 105 = Which posts organization method should I choose? = 106 107 * **Single** (default): Best for sites with <5,000 posts. All posts in one file. 108 * **Date**: Best for news sites, blogs with frequent updates, or sites with 10,000+ posts. Creates one sitemap per month/year. 109 * **Category**: Best for multi-topic sites with well-organized categories. Creates one sitemap per category. 110 111 You can change this anytime in Settings → Easy Sitemap. 112 113 = Does it support custom post types? = 114 115 Currently, the plugin supports posts and pages. Custom post type support is planned for a future release. 116 117 = How do I exclude specific posts from the sitemap? = 118 119 1. Edit the post or page 120 2. Look for "XML Sitemap Options" in the sidebar (Gutenberg) or below the editor (Classic) 121 3. Check "Exclude from XML sitemaps" 122 4. Update/save the post 123 124 = Does this plugin submit the sitemap to search engines? = 125 126 No, you need to manually submit your sitemap URL to: 127 * [Google Search Console](https://search.google.com/search-console) 128 * [Bing Webmaster Tools](https://www.bing.com/webmasters) 129 130 Enter: `easy-sitemap/sitemap.xml` in the sitemap submission field. 131 132 = Why isn't my sitemap showing in robots.txt? = 133 134 The automatic robots.txt integration only works with WordPress's **virtual** robots.txt. If you have a physical `robots.txt` file in your site root, the plugin can't modify it. Either: 135 1. Delete the physical file (after backing it up), or 136 2. Manually add this line: `Sitemap: https://your-site.com/easy-sitemap/sitemap.xml` 137 138 Check Settings → Easy Sitemap for detection and instructions. 139 140 = How do I clear the sitemap cache? = 141 142 Go to Settings → Easy Sitemap and click "Regenerate All Sitemaps". The cache also clears automatically when you: 143 * Publish, update, or delete posts/pages 144 * Change categories or tags 145 * Modify sitemap settings 146 147 = My sitemaps return 404 errors = 148 149 1. Go to Settings → Permalinks 150 2. Click "Save Changes" (this flushes rewrite rules) 151 3. Test your sitemap URL again 152 153 = How do I check which organization method is active? = 154 155 Go to Settings → Easy Sitemap and look at the "Posts Organization" setting. You'll see three options: 156 * Single sitemap 157 * Organize by date 158 * Organize by category 159 160 The selected option shows which structure is currently active. 89 161 90 162 == Screenshots == 91 163 92 1. Example of XML sitemap index output in the browser. 93 2. Example of an individual sitemap for posts. 94 3. Meta box for excluding individual posts from the sitemap. 164 1. **Admin Settings Page** - Configure sitemap types and posts organization 165 2. **Posts Organization Options** - Choose between single, date, or category organization 166 3. **Sitemap URLs Table** - View all your sitemap URLs with status 167 4. **robots.txt Integration** - Automatic sitemap detection and warnings 168 5. **Per-Post Exclusion** - Simple checkbox in the post editor 169 6. **Sitemap Index Output** - Styled XML view in browser 170 7. **Individual Sitemap Output** - Clean, valid XML for search engines 95 171 96 172 == Changelog == 97 173 98 = 1.1.3 = 99 * Add plugin icons. 174 = 1.2.0 - 2024-12-19 = 175 176 **Added** 177 * Posts organization options: single, by date, or by category 178 * Dynamic posts index that adapts to organization method 179 * Posts by date sitemaps: `/easy-sitemap/posts-YYYY-MM.xml` 180 * Posts by category sitemaps: `/easy-sitemap/posts-{category-slug}.xml` 181 * Radio button UI for organization selection 182 * Automatic cache regeneration when settings change 183 184 **Changed** 185 * Posts sitemap now serves as index when date/category organization enabled 186 * Enhanced admin settings page with better help text 187 * Improved cache invalidation for dynamic sitemap types 188 * Updated rewrite rules to support dynamic URL patterns 189 190 **Fixed** 191 * Critical: Fatal error in sitemap controller causing activation failure 192 * Critical: Malformed rewrite rules and duplicate function declarations 193 * Performance: Optimized database queries for organized sitemaps 194 * Cache key generation for dynamic sitemap types 195 196 = 1.1.3 - 2024-12-15 = 197 * Added plugin icons for WordPress.org directory 198 * Enhanced visual branding 199 200 = 1.1.0 - 2024-12-05 = 201 * Added sitemap index file (`sitemap.xml`) 202 * Added robots.txt integration with automatic detection 203 * Changed base path from `/easy-xml-sitemap/` to `/easy-sitemap/` 204 * Updated settings menu name to "Easy Sitemap" 205 * Enhanced admin interface with better organization 206 * Added robots.txt status detection and warnings 207 * Improved cache management 208 209 = 1.0.0 - 2024-12-05 = 210 * Initial release 211 * Multiple sitemap types (posts, pages, tags, categories, general, news) 212 * Per-post/page exclusion controls 213 * Smart caching system 214 * Admin settings page 215 * Classic and block editor support 216 * Multisite compatible 217 218 == Upgrade Notice == 219 220 = 1.2.0 = 221 Major update with posts organization options. Recommended for sites with 10,000+ posts. Backup before upgrading. After upgrade: 1) Go to Settings → Easy Sitemap and choose organization method, 2) Go to Settings → Permalinks and save, 3) Clear all caches, 4) Resubmit sitemap to search engines. 100 222 101 223 = 1.1.0 = 102 * Added support for per-post exclusion via meta box. 103 * Introduced caching layer for sitemap queries to improve performance. 104 * Improved compatibility with custom post types and taxonomies. 105 * Enhanced robots.txt integration for sitemap index. 106 * Refactored internal classes to be more modular and extensible. 107 108 = 1.0.1 = 109 * Fixed minor issue with sitemap index URLs in certain permalink configurations. 110 * Improved handling of empty or non-public post types. 224 URL structure changed. Main sitemap moved to `/easy-sitemap/sitemap.xml`. After updating, flush permalinks (Settings → Permalinks → Save) and resubmit to Google Search Console. 111 225 112 226 = 1.0.0 = 113 * Initial release of Easy XML Sitemap. 114 * XML sitemap index for posts, pages, and taxonomies. 115 * Basic support for large sites with pagination. 116 117 == Upgrade Notice == 118 119 = 1.1.0 = 120 This release introduces per-post exclusion and an internal caching layer for improved performance. It is recommended to clear any external caches (page cache, object cache) after upgrading. 227 Initial release of Easy XML Sitemap. 228 229 == Performance == 230 231 This plugin is designed for performance: 232 233 * **Efficient Database Queries**: Optimized for large databases 234 * **Smart Caching**: Transient-based with configurable duration 235 * **Conditional Loading**: Admin resources only load when needed 236 * **No Front-End Impact**: Pure XML output, no styling or JavaScript 237 * **Scalable Organization**: Date/category methods handle 100,000+ posts 238 239 **Benchmarks** (average generation time on standard hosting): 240 * 1,000 posts: <0.5 seconds 241 * 10,000 posts (single): ~2 seconds 242 * 10,000 posts (by date): <0.5 seconds per month 243 * 50,000 posts (by date): <0.5 seconds per month 244 245 == Support == 246 247 Need help? We're here for you: 248 249 * [Support Forum](https://wordpress.org/support/plugin/easy-xml-sitemap/) 250 * [GitHub Issues](https://github.com/andremoura/easy-xml-sitemap/issues) 251 * [Documentation](https://wordpress.andremoura.com) 252 * Email: [email protected] 253 254 == Contributing == 255 256 Contributions are welcome! Visit our [GitHub repository](https://github.com/andremoura/easy-xml-sitemap) to: 257 * Report bugs 258 * Suggest features 259 * Submit pull requests 260 * Review code 261 262 See [CONTRIBUTING.md](https://github.com/andremoura/easy-xml-sitemap/blob/main/CONTRIBUTING.md) for guidelines. 263 264 == Privacy == 265 266 This plugin: 267 * Does NOT collect any user data 268 * Does NOT make external API calls 269 * Does NOT use cookies 270 * Does NOT track users 271 * Only generates XML files based on your public WordPress content 272 273 == Credits == 274 275 **Developer**: André Moura 276 **Website**: [wordpress.andremoura.com](https://wordpress.andremoura.com) 277 **License**: GPL v2 or later 278 279 == Links == 280 281 * [Plugin Homepage](https://wordpress.andremoura.com) 282 * [GitHub Repository](https://github.com/andremoura/easy-xml-sitemap) 283 * [Support Forum](https://wordpress.org/support/plugin/easy-xml-sitemap/) 284 * [Sitemaps Protocol](https://www.sitemaps.org/protocol.html) 285 * [Google Sitemap Guidelines](https://developers.google.com/search/docs/advanced/sitemaps/overview) -
easy-xml-sitemap/trunk/changelog.md
r3422280 r3423502 9 9 10 10 ### Planned Features 11 - Custom post type support 12 - Sitemap index file generation 11 - Custom post type support with UI controls 13 12 - Image sitemap support 14 13 - Video sitemap support … … 17 16 - REST API endpoints for programmatic access 18 17 - Sitemap statistics and analytics 19 - Integration with popular SEO plugins 18 - Integration with popular SEO plugins (Yoast, Rank Math) 19 - Automatic ping to search engines on content update 20 21 --- 22 23 ## [1.2.0] - 2024-12-19 24 25 ### Added 26 - **Posts Organization Options**: Choose how to organize posts in sitemaps 27 - Single sitemap (all posts in one file) 28 - Organize by date (one sitemap per month/year) 29 - Organize by category (one sitemap per category) 30 - **Dynamic Posts Index**: Automatically generates index based on organization method 31 - **Posts by Date Sitemaps**: `/easy-sitemap/posts-YYYY-MM.xml` for date-based organization 32 - **Posts by Category Sitemaps**: `/easy-sitemap/posts-{category-slug}.xml` for category-based organization 33 - **Radio Button UI**: New settings interface for posts organization selection 34 - **Automatic Cache Regeneration**: Cache clears automatically when organization settings change 35 36 ### Changed 37 - **Posts Sitemap Endpoint**: Now serves as an index when date/category organization is enabled 38 - **Admin Settings Page**: Enhanced UI with organization options and better help text 39 - **Cache Invalidation**: Improved logic to handle dynamic sitemap types 40 - **Rewrite Rules**: Added support for dynamic URL patterns (date and category slugs) 41 42 ### Fixed 43 - **Critical**: Fixed fatal error in `class-sitemap-controller.php` caused by malformed rewrite rules 44 - **Critical**: Resolved duplicate function declarations and incomplete code blocks 45 - **Performance**: Optimized database queries for date-based and category-based sitemaps 46 - **Cache Keys**: Fixed cache key generation for dynamic sitemap types 47 48 ### Technical Details 49 - Added validation for year/month parameters in date-based sitemaps 50 - Added category existence validation for category-based sitemaps 51 - Enhanced `XML_Renderer` with new methods: 52 - `generate_posts_by_date($year, $month)` 53 - `generate_posts_by_category($cat_slug)` 54 - Updated cache clearing to handle dynamic cache keys with wildcards 55 - Improved SQL query performance with direct database cleanup for dynamic caches 56 57 --- 58 59 ## [1.1.3] - 2024-12-15 60 61 ### Added 62 - Plugin icons for WordPress.org directory 63 - Enhanced visual branding 64 65 --- 66 67 ## [1.1.0] - 2024-12-05 68 69 ### Added 70 - **Sitemap Index**: Main sitemap index file at `/easy-sitemap/sitemap.xml` 71 - **robots.txt Integration**: Automatic sitemap URL addition to virtual robots.txt 72 - **Sitemap Index Priority**: Index now appears first in the admin URLs table with visual highlighting 73 - **Physical robots.txt Detection**: Warning in admin when physical robots.txt file exists 74 - **robots.txt Viewer**: Direct link to view current robots.txt from settings page 75 - **Enhanced Admin Interface**: Improved settings page with better organization and help text 76 77 ### Changed 78 - **Sitemap Base Path**: Changed from `/easy-xml-sitemap/` to `/easy-sitemap/` for cleaner URLs 79 - **Settings Menu Name**: Changed from "XML Sitemap" to "Easy Sitemap" 80 - **Main Sitemap URL**: Now `/easy-sitemap/sitemap.xml` (sitemap index) 81 - **Individual Sitemaps**: All other sitemaps listed in the index 82 - **Admin Table Layout**: Sitemap index highlighted with blue background 83 - **robots.txt Implementation**: Uses WordPress filter instead of manual file manipulation 84 85 ### Updated 86 - All rewrite rules updated for new base path 87 - Cache system updated to handle sitemap index 88 - Documentation updated with new URLs and features 89 - Install.md with comprehensive robots.txt troubleshooting 90 91 ### Fixed 92 - robots.txt integration now uses proper WordPress filters 93 - Duplicate sitemap entries prevention in robots.txt 94 - Permalink flushing on activation and settings changes 20 95 21 96 --- … … 46 121 - Manual regeneration button 47 122 - Clean, SEO-friendly URLs 48 - Pattern: `/easy- xml-sitemap/{type}.xml`123 - Pattern: `/easy-sitemap/{type}.xml` 49 124 - Custom rewrite rules 50 125 - Proper XML content-type headers … … 76 151 - Comprehensive `readme.txt` for WordPress.org 77 152 - Detailed `Install.md` with installation and troubleshooting 78 - This`CHANGELOG.md` file153 - `CHANGELOG.md` file 79 154 - `CONTRIBUTING.md` guidelines 80 155 - Proper plugin lifecycle … … 89 164 - Pretty permalinks recommended 90 165 - **File Structure**: 91 - Main plugin file: `easy-xml-sitemap.php` (< 200 lines)166 - Main plugin file: `easy-xml-sitemap.php` 92 167 - Core classes in `inc/` directory 93 168 - Modular architecture for easy maintenance … … 121 196 ### Known Limitations 122 197 - No custom post type support (planned for future release) 123 - No sitemap index file (uses individual sitemaps) 124 - No image or video sitemap support 125 - No automatic ping to search engines 126 - Post priority is static (not calculated based on factors) 198 - No image or video sitemap support (planned) 199 - No automatic ping to search engines (planned) 127 200 128 201 --- … … 148 221 ## Migration Guide 149 222 150 ### From Version X.X to 1.0 151 152 Not applicable for initial release. 153 154 ### Future Upgrades 155 156 When upgrading to future versions: 157 1. Backup your database before upgrading 158 2. Check the changelog for breaking changes 159 3. Test on a staging site first 160 4. Clear all caches after upgrade 161 5. Regenerate sitemaps from Settings > XML Sitemap 162 6. Resubmit sitemaps to search engines if structure changes 223 ### From Version 1.1.x to 1.2.0 224 225 1. **Backup your database** before upgrading 226 2. The plugin will automatically migrate to the new structure 227 3. **Default behavior**: Posts organization defaults to "single" (all posts in one file) 228 4. If you have a large site (10,000+ posts), consider: 229 - Changing to "date" organization for better performance 230 - Changing to "category" organization if content is well-categorized 231 5. **Clear all caches** after upgrade: 232 - Plugin cache (automatic) 233 - WordPress object cache 234 - Page cache (WP Super Cache, W3 Total Cache, etc.) 235 - CDN cache (Cloudflare, etc.) 236 6. **Flush permalinks**: Go to Settings > Permalinks and click "Save Changes" 237 7. **Regenerate sitemaps**: Go to Settings > Easy Sitemap and click "Regenerate All Sitemaps" 238 8. **Resubmit to search engines**: Update your sitemap in Google Search Console and Bing Webmaster Tools 239 9. The main sitemap URL remains the same: `/easy-sitemap/sitemap.xml` 240 241 ### From Version 1.0.x to 1.1.0 242 243 1. **Main sitemap URL changed**: Update from `/easy-xml-sitemap/sitemap-index.xml` to `/easy-sitemap/sitemap.xml` 244 2. **Resubmit to search engines**: Update sitemap URL in Google Search Console and Bing 245 3. All individual sitemap URLs changed from `/easy-xml-sitemap/` to `/easy-sitemap/` 246 4. robots.txt integration now automatic (if no physical robots.txt exists) 247 5. Check Settings > Easy Sitemap for robots.txt status 163 248 164 249 --- … … 168 253 ### Reporting Issues 169 254 If you encounter bugs or have feature requests: 170 1. Check existing issues on the plugin repository171 2. Search the WordPress.org support forum255 1. Check existing issues on the [GitHub repository](https://github.com/andremoura/easy-xml-sitemap/issues) 256 2. Search the [WordPress.org support forum](https://wordpress.org/support/plugin/easy-xml-sitemap/) 172 257 3. Create a new issue with detailed information: 173 258 - Plugin version … … 183 268 2. Describe the use case and benefit 184 269 3. Provide examples or mockups if applicable 270 4. Submit via GitHub Issues or WordPress.org support forum 185 271 186 272 --- -
easy-xml-sitemap/trunk/easy-xml-sitemap.php
r3422350 r3423502 3 3 * Plugin Name: Easy XML Sitemap 4 4 * Description: Lightweight, modular XML sitemap generator for posts, pages, taxonomies, and Google News. 5 * Version: 1. 1.35 * Version: 1.2.0 6 6 * Author: André Moura 7 7 * Author URI: https://www.andremoura.com … … 38 38 * @var string 39 39 */ 40 const VERSION = '1. 1.0';40 const VERSION = '1.2.0'; 41 41 42 42 /** … … 109 109 */ 110 110 private function init_hooks() { 111 // Disable WordPress native sitemap 112 add_filter( 'wp_sitemaps_enabled', '__return_false', 9999 ); 113 114 // Redirect native WP sitemap to our plugin 115 add_action( 'template_redirect', array( $this, 'redirect_native_sitemap' ), 0 ); 116 111 117 // Initialize core components 112 118 add_action( 'init', array( $this, 'init_components' ) ); … … 116 122 register_deactivation_hook( EASY_XML_SITEMAP_FILE, array( $this, 'deactivate' ) ); 117 123 118 // Add sitemap to robots.txt if enabled 119 add_ action( 'do_robots', array( $this, 'add_robots_sitemap' ), 0);124 // Add sitemap to robots.txt if enabled (using filter for proper formatting) 125 add_filter( 'robots_txt', array( $this, 'add_robots_sitemap' ), 10, 2 ); 120 126 } 121 127 … … 130 136 131 137 /** 138 * Redirect native WordPress sitemap to our plugin sitemap 139 */ 140 public function redirect_native_sitemap() { 141 if ( ! isset( $_SERVER['REQUEST_URI'] ) ) { 142 return; 143 } 144 145 $request_uri = sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ); 146 147 // Redirect /wp-sitemap.xml to our sitemap 148 if ( false !== strpos( $request_uri, 'wp-sitemap.xml' ) ) { 149 wp_redirect( home_url( '/easy-sitemap/sitemap.xml' ), 301 ); 150 exit; 151 } 152 } 153 154 /** 132 155 * Plugin activation 133 156 */ … … 139 162 flush_rewrite_rules(); 140 163 141 // Optionally, initialize default settings164 // Initialize default settings 142 165 $defaults = array( 143 'enable_posts' => true, 144 'enable_pages' => true, 145 'enable_categories' => true, 146 'enable_tags' => true, 147 'enable_news' => false, 148 'add_to_robots' => true, 149 'cache_duration' => 3600, 166 'enable_posts' => true, 167 'posts_organization' => 'single', 168 'enable_pages' => true, 169 'enable_categories' => true, 170 'enable_tags' => true, 171 'enable_news' => false, 172 'enable_general' => true, 173 'add_to_robots' => true, 174 'cache_duration' => 3600, 150 175 ); 151 176 … … 171 196 172 197 /** 173 * Add sitemap index to robots.txt 174 */ 175 public function add_robots_sitemap() { 198 * Add sitemap to robots.txt (via filter for proper formatting) 199 * 200 * @param string $output The robots.txt output 201 * @param bool $public Whether the site is public 202 * @return string Modified robots.txt output 203 */ 204 public function add_robots_sitemap( $output, $public ) { 176 205 $settings = get_option( 'easy_xml_sitemap_settings', array() ); 177 206 178 207 // Check if option is enabled 179 208 if ( empty( $settings['add_to_robots'] ) ) { 180 return; 181 } 182 183 // Get sitemap index URL 184 $sitemap_url = Sitemap_Controller::get_sitemap_url( 'sitemap-index' ); 185 186 echo "Sitemap: " . esc_url( $sitemap_url ) . "\n"; 209 return $output; 210 } 211 212 // Get sitemap URL 213 $sitemap_url = home_url( '/easy-sitemap/sitemap.xml' ); 214 215 // Check if sitemap line already exists to avoid duplication 216 if ( false !== strpos( $output, $sitemap_url ) ) { 217 return $output; 218 } 219 220 // Add sitemap at the end with proper formatting 221 $output .= "\nSitemap: " . esc_url( $sitemap_url ) . "\n"; 222 223 return $output; 187 224 } 188 225 } -
easy-xml-sitemap/trunk/inc/class-admin-settings.php
r3422280 r3423502 72 72 public function add_settings_page() { 73 73 add_options_page( 74 __( 'Easy XMLSitemap', 'easy-xml-sitemap' ),75 __( 'Easy XMLSitemap', 'easy-xml-sitemap' ),74 __( 'Easy Sitemap', 'easy-xml-sitemap' ), 75 __( 'Easy Sitemap', 'easy-xml-sitemap' ), 76 76 'manage_options', 77 77 self::PAGE_SLUG, … … 92 92 add_settings_section( 93 93 'easy_xml_sitemap_general_section', 94 __( ' General Settings', 'easy-xml-sitemap' ),94 __( 'Sitemap Configuration', 'easy-xml-sitemap' ), 95 95 array( $this, 'render_general_section' ), 96 96 self::PAGE_SLUG … … 110 110 111 111 add_settings_field( 112 'posts_organization', 113 __( 'Posts Organization', 'easy-xml-sitemap' ), 114 array( $this, 'render_radio_field' ), 115 self::PAGE_SLUG, 116 'easy_xml_sitemap_general_section', 117 array( 118 'label_for' => 'posts_organization', 119 'options' => array( 120 'single' => __( 'Single sitemap (all posts in one file)', 'easy-xml-sitemap' ), 121 'date' => __( 'Organize by date (one sitemap per month/year)', 'easy-xml-sitemap' ), 122 'category' => __( 'Organize by category (one sitemap per category)', 'easy-xml-sitemap' ), 123 ), 124 'default' => 'single', 125 'description' => __( 'How to organize posts in the sitemap. For large sites, organizing by date or category improves performance.', 'easy-xml-sitemap' ), 126 ) 127 ); 128 129 add_settings_field( 112 130 'enable_pages', 113 131 __( 'Pages Sitemap', 'easy-xml-sitemap' ), … … 146 164 147 165 add_settings_field( 166 'enable_general', 167 __( 'General Sitemap', 'easy-xml-sitemap' ), 168 array( $this, 'render_checkbox_field' ), 169 self::PAGE_SLUG, 170 'easy_xml_sitemap_general_section', 171 array( 172 'label_for' => 'enable_general', 173 'description' => __( 'Generate a comprehensive sitemap with all URLs (homepage, posts, pages, categories, tags)', 'easy-xml-sitemap' ), 174 ) 175 ); 176 177 add_settings_field( 148 178 'enable_news', 149 179 __( 'Google News Sitemap', 'easy-xml-sitemap' ), … … 153 183 array( 154 184 'label_for' => 'enable_news', 155 'description' => __( 'Enable Google News compatible sitemap for recent posts ', 'easy-xml-sitemap' ),185 'description' => __( 'Enable Google News compatible sitemap for recent posts (last 2 days)', 'easy-xml-sitemap' ), 156 186 ) 157 187 ); … … 165 195 array( 166 196 'label_for' => 'add_to_robots', 167 'description' => __( 'Automatically add sitemap index URL torobots.txt', 'easy-xml-sitemap' ),197 'description' => __( 'Automatically add sitemap URL to virtual robots.txt', 'easy-xml-sitemap' ), 168 198 ) 169 199 ); … … 177 207 array( 178 208 'label_for' => 'cache_duration', 179 'description' => __( 'How long to cache sitemap output ( default: 3600 seconds)', 'easy-xml-sitemap' ),209 'description' => __( 'How long to cache sitemap output (60 seconds to 1 week, default: 3600)', 'easy-xml-sitemap' ), 180 210 'min' => 60, 181 211 'max' => 604800, … … 192 222 public function sanitize_settings( $input ) { 193 223 $output = array(); 194 195 $output['enable_posts'] = isset( $input['enable_posts'] ) ? (bool) $input['enable_posts'] : false; 196 $output['enable_pages'] = isset( $input['enable_pages'] ) ? (bool) $input['enable_pages'] : false; 197 $output['enable_categories'] = isset( $input['enable_categories'] ) ? (bool) $input['enable_categories'] : false; 198 $output['enable_tags'] = isset( $input['enable_tags'] ) ? (bool) $input['enable_tags'] : false; 199 $output['enable_news'] = isset( $input['enable_news'] ) ? (bool) $input['enable_news'] : false; 200 $output['add_to_robots'] = isset( $input['add_to_robots'] ) ? (bool) $input['add_to_robots'] : false; 224 225 // Store old values to check if posts organization changed 226 $old_settings = get_option( self::OPTION_NAME, array() ); 227 $old_organization = isset( $old_settings['posts_organization'] ) ? $old_settings['posts_organization'] : 'single'; 228 229 $output['enable_posts'] = isset( $input['enable_posts'] ) ? (bool) $input['enable_posts'] : false; 230 $output['posts_organization'] = isset( $input['posts_organization'] ) ? sanitize_key( $input['posts_organization'] ) : 'single'; 231 $output['enable_pages'] = isset( $input['enable_pages'] ) ? (bool) $input['enable_pages'] : false; 232 $output['enable_categories'] = isset( $input['enable_categories'] ) ? (bool) $input['enable_categories'] : false; 233 $output['enable_tags'] = isset( $input['enable_tags'] ) ? (bool) $input['enable_tags'] : false; 234 $output['enable_general'] = isset( $input['enable_general'] ) ? (bool) $input['enable_general'] : false; 235 $output['enable_news'] = isset( $input['enable_news'] ) ? (bool) $input['enable_news'] : false; 236 $output['add_to_robots'] = isset( $input['add_to_robots'] ) ? (bool) $input['add_to_robots'] : false; 201 237 202 238 $output['cache_duration'] = isset( $input['cache_duration'] ) ? absint( $input['cache_duration'] ) : 3600; … … 208 244 if ( $output['cache_duration'] > 604800 ) { 209 245 $output['cache_duration'] = 604800; 246 } 247 248 // Validate posts_organization 249 if ( ! in_array( $output['posts_organization'], array( 'single', 'date', 'category' ), true ) ) { 250 $output['posts_organization'] = 'single'; 251 } 252 253 // Automatically regenerate cache if settings changed 254 $should_regenerate = false; 255 256 // Check if any sitemap enable/disable changed 257 $enable_keys = array( 'enable_posts', 'enable_pages', 'enable_categories', 'enable_tags', 'enable_general', 'enable_news' ); 258 foreach ( $enable_keys as $key ) { 259 $old_value = isset( $old_settings[ $key ] ) ? $old_settings[ $key ] : true; 260 $new_value = $output[ $key ]; 261 if ( $old_value !== $new_value ) { 262 $should_regenerate = true; 263 break; 264 } 265 } 266 267 // Check if posts organization changed 268 if ( $old_organization !== $output['posts_organization'] ) { 269 $should_regenerate = true; 270 } 271 272 // Check if cache duration changed significantly 273 $old_duration = isset( $old_settings['cache_duration'] ) ? $old_settings['cache_duration'] : 3600; 274 if ( $old_duration !== $output['cache_duration'] ) { 275 $should_regenerate = true; 276 } 277 278 // Regenerate cache if needed 279 if ( $should_regenerate ) { 280 // Clear all caches 281 Cache::clear_all(); 282 283 // Also clear any WordPress object cache 284 if ( function_exists( 'wp_cache_flush' ) ) { 285 wp_cache_flush(); 286 } 287 288 // Set a transient to show regeneration happened 289 set_transient( 'easy_xml_sitemap_regenerated', '1', 30 ); 210 290 } 211 291 … … 224 304 ?> 225 305 <div class="wrap"> 226 <h1><?php esc_html_e( 'Easy XML Sitemap ', 'easy-xml-sitemap' ); ?></h1>227 <p><?php esc_html_e( 'Configure the XML sitemap settings for yoursite.', 'easy-xml-sitemap' ); ?></p>228 229 <form method="post" action="options.php" >306 <h1><?php esc_html_e( 'Easy XML Sitemap Settings', 'easy-xml-sitemap' ); ?></h1> 307 <p><?php esc_html_e( 'Configure XML sitemap generation for your WordPress site.', 'easy-xml-sitemap' ); ?></p> 308 309 <form method="post" action="options.php" id="easy-xml-sitemap-settings-form"> 230 310 <?php 231 311 settings_fields( 'easy_xml_sitemap_settings' ); … … 237 317 <hr /> 238 318 239 <h2><?php esc_html_e( 'Sitemap Tools', 'easy-xml-sitemap' ); ?></h2> 240 <p><?php esc_html_e( 'Use the tools below to manually regenerate XML sitemaps.', 'easy-xml-sitemap' ); ?></p> 319 <h2><?php esc_html_e( 'robots.txt Configuration', 'easy-xml-sitemap' ); ?></h2> 320 321 <?php 322 // Check if physical robots.txt exists 323 $robots_file = ABSPATH . 'robots.txt'; 324 if ( file_exists( $robots_file ) ) : 325 ?> 326 <div class="notice notice-warning inline"> 327 <p> 328 <strong><?php esc_html_e( '⚠️ Physical robots.txt detected', 'easy-xml-sitemap' ); ?></strong><br /> 329 <?php esc_html_e( 'A physical robots.txt file exists in your site root. The "Add to robots.txt" option will not work automatically.', 'easy-xml-sitemap' ); ?> 330 </p> 331 <p> 332 <?php esc_html_e( 'To use automatic robots.txt integration, please delete or rename the physical robots.txt file.', 'easy-xml-sitemap' ); ?><br /> 333 <?php esc_html_e( 'Alternatively, manually add this line to your robots.txt:', 'easy-xml-sitemap' ); ?> 334 </p> 335 <p> 336 <code>Sitemap: <?php echo esc_html( home_url( '/easy-sitemap/sitemap.xml' ) ); ?></code> 337 </p> 338 </div> 339 <?php else : ?> 340 <div class="notice notice-success inline"> 341 <p> 342 <strong><?php esc_html_e( '✓ No physical robots.txt found', 'easy-xml-sitemap' ); ?></strong><br /> 343 <?php esc_html_e( 'Virtual robots.txt integration will work correctly if enabled above.', 'easy-xml-sitemap' ); ?> 344 </p> 345 <p> 346 <a href="<?php echo esc_url( home_url( '/robots.txt' ) ); ?>" target="_blank" rel="noopener noreferrer"> 347 <?php esc_html_e( 'View your robots.txt', 'easy-xml-sitemap' ); ?> 348 </a> 349 </p> 350 </div> 351 <?php endif; ?> 352 353 <hr /> 354 355 <h2><?php esc_html_e( 'Cache Management', 'easy-xml-sitemap' ); ?></h2> 356 <p><?php esc_html_e( 'Manually regenerate all sitemap files to clear the cache and rebuild from current content.', 'easy-xml-sitemap' ); ?></p> 241 357 242 358 <form method="post" action="<?php echo esc_url( admin_url( 'admin-post.php' ) ); ?>"> 243 359 <?php wp_nonce_field( self::NONCE_ACTION, self::NONCE_FIELD ); ?> 244 360 <input type="hidden" name="action" value="easy_xml_sitemap_regenerate" /> 245 <?php submit_button( __( 'Regenerate Sitemaps', 'easy-xml-sitemap' ), 'secondary' ); ?>361 <?php submit_button( __( 'Regenerate All Sitemaps', 'easy-xml-sitemap' ), 'secondary' ); ?> 246 362 </form> 247 363 364 <hr /> 365 248 366 <h2><?php esc_html_e( 'Sitemap URLs', 'easy-xml-sitemap' ); ?></h2> 249 <p><?php esc_html_e( ' Below are the main sitemap URLs generated by this plugin.', 'easy-xml-sitemap' ); ?></p>367 <p><?php esc_html_e( 'These are the sitemap URLs generated by this plugin. Submit the main sitemap to search engines.', 'easy-xml-sitemap' ); ?></p> 250 368 251 369 <?php 252 370 $sitemap_types = array( 253 'sitemap-index' => __( 'Sitemap Index', 'easy-xml-sitemap' ), 254 'posts' => __( 'Posts Sitemap', 'easy-xml-sitemap' ), 255 'pages' => __( 'Pages Sitemap', 'easy-xml-sitemap' ), 256 'categories' => __( 'Categories Sitemap', 'easy-xml-sitemap' ), 257 'tags' => __( 'Tags Sitemap', 'easy-xml-sitemap' ), 258 'news' => __( 'Google News Sitemap', 'easy-xml-sitemap' ), 371 'sitemap' => array( 372 'label' => __( 'Main Sitemap', 'easy-xml-sitemap' ), 373 'enabled' => true, 374 'url' => home_url( '/easy-sitemap/sitemap.xml' ), 375 ), 376 'posts-index' => array( 377 'label' => __( 'Posts Sitemap', 'easy-xml-sitemap' ), 378 'enabled' => ! empty( $options['enable_posts'] ), 379 'url' => Sitemap_Controller::get_sitemap_url( 'posts-index' ), 380 ), 381 'pages' => array( 382 'label' => __( 'Pages Sitemap', 'easy-xml-sitemap' ), 383 'enabled' => ! empty( $options['enable_pages'] ), 384 'url' => Sitemap_Controller::get_sitemap_url( 'pages' ), 385 ), 386 'categories' => array( 387 'label' => __( 'Categories Sitemap', 'easy-xml-sitemap' ), 388 'enabled' => ! empty( $options['enable_categories'] ), 389 'url' => Sitemap_Controller::get_sitemap_url( 'categories' ), 390 ), 391 'tags' => array( 392 'label' => __( 'Tags Sitemap', 'easy-xml-sitemap' ), 393 'enabled' => ! empty( $options['enable_tags'] ), 394 'url' => Sitemap_Controller::get_sitemap_url( 'tags' ), 395 ), 396 'general' => array( 397 'label' => __( 'General Sitemap', 'easy-xml-sitemap' ), 398 'enabled' => ! empty( $options['enable_general'] ), 399 'url' => Sitemap_Controller::get_sitemap_url( 'general' ), 400 ), 401 'news' => array( 402 'label' => __( 'Google News Sitemap', 'easy-xml-sitemap' ), 403 'enabled' => ! empty( $options['enable_news'] ), 404 'url' => Sitemap_Controller::get_sitemap_url( 'news' ), 405 ), 259 406 ); 260 407 ?> … … 263 410 <thead> 264 411 <tr> 265 <th ><?php esc_html_e( 'Sitemap Type', 'easy-xml-sitemap' ); ?></th>412 <th style="width: 40%;"><?php esc_html_e( 'Sitemap Type', 'easy-xml-sitemap' ); ?></th> 266 413 <th><?php esc_html_e( 'URL', 'easy-xml-sitemap' ); ?></th> 267 414 </tr> 268 415 </thead> 269 416 <tbody> 270 <?php foreach ( $sitemap_types as $type => $ label) : ?>417 <?php foreach ( $sitemap_types as $type => $data ) : ?> 271 418 <?php 272 $enabled = true; 273 if ( 'sitemap-index' !== $type ) { 274 if ( 'news' === $type ) { 275 $enabled = ! empty( $options['enable_news'] ); 276 } elseif ( 'posts' === $type ) { 277 $enabled = ! empty( $options['enable_posts'] ); 278 } elseif ( 'pages' === $type ) { 279 $enabled = ! empty( $options['enable_pages'] ); 280 } elseif ( 'categories' === $type ) { 281 $enabled = ! empty( $options['enable_categories'] ); 282 } elseif ( 'tags' === $type ) { 283 $enabled = ! empty( $options['enable_tags'] ); 284 } 285 } 286 287 $url = Sitemap_Controller::get_sitemap_url( $type ); 288 289 $highlight_style = ( 'sitemap-index' === $type ) ? 'background-color: #f0f6fc;' : ''; 419 $url = $data['url']; 420 $highlight_style = ( 'sitemap' === $type ) ? 'background-color: #f0f6fc; font-weight: 600;' : ''; 290 421 ?> 291 422 <tr<?php if ( $highlight_style ) : ?> style="<?php echo esc_attr( $highlight_style ); ?>"<?php endif; ?>> 292 <td><strong><?php echo esc_html( $label ); ?></strong></td>293 423 <td> 294 <?php if ( $enabled ) : ?> 424 <?php echo esc_html( $data['label'] ); ?> 425 <?php if ( 'sitemap' === $type ) : ?> 426 <br /><small style="color: #2271b1;"><?php esc_html_e( '← Submit this URL to Google Search Console and Bing Webmaster Tools', 'easy-xml-sitemap' ); ?></small> 427 <?php endif; ?> 428 </td> 429 <td> 430 <?php if ( $data['enabled'] ) : ?> 295 431 <a href="<?php echo esc_url( $url ); ?>" target="_blank" rel="noopener noreferrer"> 296 432 <?php echo esc_html( $url ); ?> 297 433 </a> 298 434 <?php else : ?> 299 <em><?php esc_html_e( 'Disabled', 'easy-xml-sitemap' ); ?></em> 435 <span style="color: #999;"> 436 <?php esc_html_e( 'Disabled', 'easy-xml-sitemap' ); ?> 437 <small>(<?php echo esc_html( $url ); ?>)</small> 438 </span> 300 439 <?php endif; ?> 301 440 </td> … … 304 443 </tbody> 305 444 </table> 445 446 <hr /> 447 448 <h2><?php esc_html_e( 'Search Engine Submission', 'easy-xml-sitemap' ); ?></h2> 449 <p><?php esc_html_e( 'Submit your main sitemap to search engines for better crawling and indexing:', 'easy-xml-sitemap' ); ?></p> 450 <ul style="list-style: disc; margin-left: 20px;"> 451 <li> 452 <strong>Google Search Console:</strong> 453 <a href="https://search.google.com/search-console" target="_blank" rel="noopener noreferrer"> 454 <?php esc_html_e( 'Submit Sitemap', 'easy-xml-sitemap' ); ?> 455 </a> 456 </li> 457 <li> 458 <strong>Bing Webmaster Tools:</strong> 459 <a href="https://www.bing.com/webmasters" target="_blank" rel="noopener noreferrer"> 460 <?php esc_html_e( 'Submit Sitemap', 'easy-xml-sitemap' ); ?> 461 </a> 462 </li> 463 </ul> 306 464 </div> 307 465 <?php … … 313 471 public function render_general_section() { 314 472 ?> 315 <p><?php esc_html_e( ' Configure which content types should be included in XML sitemaps and how caching should behave.', 'easy-xml-sitemap' ); ?></p>473 <p><?php esc_html_e( 'Enable or disable different sitemap types and configure caching behavior.', 'easy-xml-sitemap' ); ?></p> 316 474 <?php 317 475 } … … 334 492 </label> 335 493 <?php 494 } 495 496 /** 497 * Render radio field. 498 * 499 * @param array $args Field arguments. 500 */ 501 public function render_radio_field( $args ) { 502 $options = get_option( self::OPTION_NAME, array() ); 503 $id = isset( $args['label_for'] ) ? $args['label_for'] : ''; 504 $choices = isset( $args['options'] ) ? $args['options'] : array(); 505 $default = isset( $args['default'] ) ? $args['default'] : ''; 506 $value = isset( $options[ $id ] ) ? $options[ $id ] : $default; 507 508 foreach ( $choices as $choice_value => $choice_label ) { 509 ?> 510 <label style="display: block; margin-bottom: 8px;"> 511 <input 512 type="radio" 513 name="<?php echo esc_attr( self::OPTION_NAME . '[' . $id . ']' ); ?>" 514 value="<?php echo esc_attr( $choice_value ); ?>" 515 <?php checked( $value, $choice_value ); ?> 516 /> 517 <?php echo esc_html( $choice_label ); ?> 518 </label> 519 <?php 520 } 521 522 if ( ! empty( $args['description'] ) ) { 523 ?> 524 <p class="description"><?php echo esc_html( $args['description'] ); ?></p> 525 <?php 526 } 336 527 } 337 528 … … 354 545 min="<?php echo esc_attr( $min ); ?>" 355 546 max="<?php echo esc_attr( $max ); ?>" 547 style="width: 150px;" 356 548 /> 357 549 <?php if ( ! empty( $args['description'] ) ) : ?> … … 400 592 ?> 401 593 <div class="notice notice-success is-dismissible"> 402 <p><?php esc_html_e( ' All sitemaps have been regenerated successfully.', 'easy-xml-sitemap' ); ?></p>594 <p><?php esc_html_e( '✓ All sitemaps have been regenerated successfully.', 'easy-xml-sitemap' ); ?></p> 403 595 </div> 404 596 <?php 405 597 } 598 599 if ( isset( $_GET['settings-updated'] ) && 'true' === sanitize_text_field( wp_unslash( $_GET['settings-updated'] ) ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended 600 // Check if regeneration happened 601 $regenerated = get_transient( 'easy_xml_sitemap_regenerated' ); 602 if ( $regenerated ) { 603 delete_transient( 'easy_xml_sitemap_regenerated' ); 604 ?> 605 <div class="notice notice-success is-dismissible"> 606 <p><?php esc_html_e( '✓ Settings saved successfully. Cache has been automatically regenerated.', 'easy-xml-sitemap' ); ?></p> 607 </div> 608 <?php 609 } else { 610 ?> 611 <div class="notice notice-success is-dismissible"> 612 <p><?php esc_html_e( '✓ Settings saved successfully.', 'easy-xml-sitemap' ); ?></p> 613 </div> 614 <?php 615 } 616 } 406 617 } 407 618 } -
easy-xml-sitemap/trunk/inc/class-cache.php
r3422280 r3423502 65 65 */ 66 66 public static function clear_all() { 67 $sitemap_types = array( 'posts', 'pages', 'tags', 'categories', 'general', 'news', 'sitemap-index' ); 67 $sitemap_types = array( 68 'posts', 69 'posts-index', 70 'pages', 71 'tags', 72 'categories', 73 'general', 74 'news', 75 'sitemap-index' 76 ); 68 77 69 78 foreach ( $sitemap_types as $type ) { 70 79 self::clear( $type ); 71 80 } 81 82 // Clear dynamic caches (posts-date-*, posts-category-*) 83 global $wpdb; 84 85 $pattern_date = $wpdb->esc_like( '_transient_' . self::CACHE_PREFIX . 'posts-date-' ) . '%'; 86 $pattern_cat = $wpdb->esc_like( '_transient_' . self::CACHE_PREFIX . 'posts-category-' ) . '%'; 87 88 $wpdb->query( 89 $wpdb->prepare( 90 "DELETE FROM {$wpdb->options} 91 WHERE option_name LIKE %s 92 OR option_name LIKE %s", 93 $pattern_date, 94 $pattern_cat 95 ) 96 ); 97 98 // Clear timeout transients too 99 $pattern_date_timeout = $wpdb->esc_like( '_transient_timeout_' . self::CACHE_PREFIX . 'posts-date-' ) . '%'; 100 $pattern_cat_timeout = $wpdb->esc_like( '_transient_timeout_' . self::CACHE_PREFIX . 'posts-category-' ) . '%'; 101 102 $wpdb->query( 103 $wpdb->prepare( 104 "DELETE FROM {$wpdb->options} 105 WHERE option_name LIKE %s 106 OR option_name LIKE %s", 107 $pattern_date_timeout, 108 $pattern_cat_timeout 109 ) 110 ); 72 111 } 73 112 … … 139 178 if ( 'post' === $post_type ) { 140 179 self::clear( 'posts' ); 180 self::clear( 'posts-index' ); 141 181 self::clear( 'tags' ); 142 182 self::clear( 'categories' ); 143 self::clear( 'news' ); // Posts may appear in news sitemap 183 self::clear( 'news' ); 184 185 // Clear date-specific cache 186 $post = get_post( $post_id ); 187 if ( $post ) { 188 $year = gmdate( 'Y', strtotime( $post->post_date ) ); 189 $month = gmdate( 'm', strtotime( $post->post_date ) ); 190 self::clear( 'posts-date-' . $year . '-' . $month ); 191 } 192 193 // Clear category-specific caches 194 $categories = get_the_category( $post_id ); 195 if ( ! empty( $categories ) ) { 196 foreach ( $categories as $category ) { 197 self::clear( 'posts-category-' . $category->slug ); 198 } 199 } 200 144 201 } elseif ( 'page' === $post_type ) { 145 202 self::clear( 'pages' ); … … 161 218 if ( 'category' === $taxonomy ) { 162 219 self::clear( 'categories' ); 220 self::clear( 'posts-index' ); 221 222 // Clear category-specific posts cache 223 $term = get_term( $term_id, $taxonomy ); 224 if ( $term && ! is_wp_error( $term ) ) { 225 self::clear( 'posts-category-' . $term->slug ); 226 } 227 163 228 } elseif ( 'post_tag' === $taxonomy ) { 164 229 self::clear( 'tags' ); -
easy-xml-sitemap/trunk/inc/class-sitemap-controller.php
r3422280 r3423502 35 35 * @var array 36 36 */ 37 private $valid_types = array( 'posts', 'pages', 'tags', 'categories', 'general', 'news', 'sitemap-index' ); 37 private $valid_types = array( 38 'posts', 39 'posts-index', 40 'posts-date', 41 'posts-category', 42 'pages', 43 'tags', 44 'categories', 45 'general', 46 'news', 47 'sitemap-index' 48 ); 38 49 39 50 /** … … 61 72 * Register rewrite rules for sitemap URLs 62 73 */ 63 public staticfunction register_rewrite_rules() {74 public function register_rewrite_rules() { 64 75 $slug = self::SITEMAP_SLUG; 65 76 66 // Pattern: /easy-sitemap/posts.xml or /easy-sitemap/sitemap-index.xml 77 // Main sitemap: /easy-sitemap/sitemap.xml 78 add_rewrite_rule( 79 '^' . $slug . '/sitemap\.xml$', 80 'index.php?easy_sitemap_type=sitemap-index', 81 'top' 82 ); 83 84 // Posts index: /easy-sitemap/posts-index.xml 85 add_rewrite_rule( 86 '^' . $slug . '/posts-index\.xml$', 87 'index.php?easy_sitemap_type=posts-index', 88 'top' 89 ); 90 91 // Posts by date: /easy-sitemap/posts-2024-12.xml 92 add_rewrite_rule( 93 '^' . $slug . '/posts-([0-9]{4})-([0-9]{2})\.xml$', 94 'index.php?easy_sitemap_type=posts-date&easy_sitemap_year=$matches[1]&easy_sitemap_month=$matches[2]', 95 'top' 96 ); 97 98 // Posts by category: /easy-sitemap/posts-{category-slug}.xml 99 add_rewrite_rule( 100 '^' . $slug . '/posts-([a-z0-9-]+)\.xml$', 101 'index.php?easy_sitemap_type=posts-category&easy_sitemap_cat=$matches[1]', 102 'top' 103 ); 104 105 // Other sitemaps: /easy-sitemap/{type}.xml 67 106 add_rewrite_rule( 68 107 '^' . $slug . '/([a-z-]+)\.xml$', … … 80 119 public function add_query_vars( $vars ) { 81 120 $vars[] = 'easy_sitemap_type'; 121 $vars[] = 'easy_sitemap_year'; 122 $vars[] = 'easy_sitemap_month'; 123 $vars[] = 'easy_sitemap_cat'; 82 124 return $vars; 83 125 } … … 100 142 } 101 143 102 // Check if this sitemap is enabled (except for sitemap-index which is always available if enabled) 103 if ( 'sitemap-index' !== $sitemap_type && ! $this->is_sitemap_enabled( $sitemap_type ) ) { 104 $this->send_404(); 105 return; 106 } 107 108 // Check if sitemap index is enabled 109 if ( 'sitemap-index' === $sitemap_type ) { 110 $settings = get_option( 'easy_xml_sitemap_settings', array() ); 111 $index_enabled = isset( $settings['enable_index'] ) ? $settings['enable_index'] : true; 112 113 if ( ! $index_enabled ) { 144 // Special handling for posts-date 145 if ( 'posts-date' === $sitemap_type ) { 146 $year = get_query_var( 'easy_sitemap_year', false ); 147 $month = get_query_var( 'easy_sitemap_month', false ); 148 149 if ( ! $year || ! $month ) { 150 $this->send_404(); 151 return; 152 } 153 154 // Validate year and month 155 if ( ! is_numeric( $year ) || ! is_numeric( $month ) ) { 156 $this->send_404(); 157 return; 158 } 159 160 $year = intval( $year ); 161 $month = intval( $month ); 162 163 if ( $year < 1970 || $year > 2100 || $month < 1 || $month > 12 ) { 164 $this->send_404(); 165 return; 166 } 167 } 168 169 // Special handling for posts-category 170 if ( 'posts-category' === $sitemap_type ) { 171 $cat_slug = get_query_var( 'easy_sitemap_cat', false ); 172 173 if ( ! $cat_slug ) { 174 $this->send_404(); 175 return; 176 } 177 178 // Validate that category exists 179 $category = get_category_by_slug( $cat_slug ); 180 if ( ! $category ) { 181 $this->send_404(); 182 return; 183 } 184 } 185 186 // Check if sitemap is enabled (except for index and dynamic types) 187 $always_available = array( 'sitemap-index', 'posts-index', 'posts-date', 'posts-category' ); 188 if ( ! in_array( $sitemap_type, $always_available, true ) ) { 189 if ( ! $this->is_sitemap_enabled( $sitemap_type ) ) { 114 190 $this->send_404(); 115 191 return; … … 127 203 */ 128 204 private function serve_sitemap( $sitemap_type ) { 205 // Build cache key 206 $cache_key = $sitemap_type; 207 208 // Add dynamic parameters to cache key 209 if ( 'posts-date' === $sitemap_type ) { 210 $year = get_query_var( 'easy_sitemap_year' ); 211 $month = get_query_var( 'easy_sitemap_month' ); 212 $cache_key .= '-' . $year . '-' . $month; 213 } elseif ( 'posts-category' === $sitemap_type ) { 214 $cat_slug = get_query_var( 'easy_sitemap_cat' ); 215 $cache_key .= '-' . $cat_slug; 216 } 217 129 218 // Try to get from cache 130 $xml = Cache::get( $ sitemap_type);219 $xml = Cache::get( $cache_key ); 131 220 132 221 // If not cached, generate fresh … … 136 225 // Store in cache 137 226 if ( ! empty( $xml ) ) { 138 Cache::set( $ sitemap_type, $xml );227 Cache::set( $cache_key, $xml ); 139 228 } 140 229 } … … 158 247 159 248 case 'posts': 160 return XML_Renderer::generate_posts_sitemap(); 249 // Legacy: redirect to posts-index logic 250 return XML_Renderer::generate_posts_index(); 251 252 case 'posts-index': 253 return XML_Renderer::generate_posts_index(); 254 255 case 'posts-date': 256 $year = get_query_var( 'easy_sitemap_year' ); 257 $month = get_query_var( 'easy_sitemap_month' ); 258 return XML_Renderer::generate_posts_by_date( $year, $month ); 259 260 case 'posts-category': 261 $cat_slug = get_query_var( 'easy_sitemap_cat' ); 262 return XML_Renderer::generate_posts_by_category( $cat_slug ); 161 263 162 264 case 'pages': … … 198 300 */ 199 301 private function send_xml_headers() { 200 // Prevent caching by some plugins/servers201 302 if ( ! headers_sent() ) { 202 303 status_header( 200 ); … … 204 305 header( 'X-Robots-Tag: noindex, follow', true ); 205 306 206 // Optional: Add cache control headers307 // Cache control headers 207 308 $cache_duration = $this->get_cache_duration(); 208 309 header( 'Cache-Control: max-age=' . $cache_duration ); … … 236 337 * Get sitemap URL for a specific type 237 338 * 238 * @param string $type Sitemap type (posts, pages, sitemap-index, etc.)339 * @param string $type Sitemap type 239 340 * @return string Full URL to the sitemap 240 341 */ … … 253 354 $urls = array(); 254 355 255 // Add sitemap index first if enabled 256 $index_enabled = isset( $settings['enable_index'] ) ? $settings['enable_index'] : true; 257 if ( $index_enabled ) { 258 $urls['sitemap-index'] = self::get_sitemap_url( 'sitemap-index' ); 259 } 356 // Add sitemap index first 357 $urls['sitemap-index'] = self::get_sitemap_url( 'sitemap-index' ); 260 358 261 359 // Add other sitemaps 262 foreach ( $controller->valid_types as $type ) { 263 // Skip sitemap-index as we already added it 264 if ( 'sitemap-index' === $type ) { 265 continue; 266 } 267 268 $key = 'enable_' . $type; 269 if ( isset( $settings[ $key ] ) && $settings[ $key ] ) { 360 $sitemap_map = array( 361 'posts-index' => 'enable_posts', 362 'pages' => 'enable_pages', 363 'tags' => 'enable_tags', 364 'categories' => 'enable_categories', 365 'general' => 'enable_general', 366 'news' => 'enable_news', 367 ); 368 369 foreach ( $sitemap_map as $type => $setting_key ) { 370 if ( isset( $settings[ $setting_key ] ) && $settings[ $setting_key ] ) { 270 371 $urls[ $type ] = self::get_sitemap_url( $type ); 271 372 } … … 277 378 /** 278 379 * Regenerate all sitemaps (clear cache) 279 * Used by admin settings page280 380 * 281 381 * @return bool True on success 282 382 */ 283 383 public static function regenerate_all_sitemaps() { 284 // Check capability285 384 if ( ! current_user_can( 'manage_options' ) ) { 286 385 return false; … … 291 390 return true; 292 391 } 293 294 /**295 * Regenerate a specific sitemap (clear its cache)296 *297 * @param string $sitemap_type Type of sitemap298 * @return bool True on success299 */300 public static function regenerate_sitemap( $sitemap_type ) {301 // Check capability302 if ( ! current_user_can( 'manage_options' ) ) {303 return false;304 }305 306 $controller = self::get_instance();307 308 if ( ! in_array( $sitemap_type, $controller->valid_types, true ) ) {309 return false;310 }311 312 Cache::clear( $sitemap_type );313 314 return true;315 }316 392 } -
easy-xml-sitemap/trunk/inc/class-xml-renderer.php
r3422280 r3423502 26 26 public static function get_xml_header( $type = 'standard' ) { 27 27 $xsl_url = plugins_url( 'sitemap.xsl', EASY_XML_SITEMAP_FILE ); 28 $generator_url = 'http ://wordpress.andremoura.com';28 $generator_url = 'https://wordpress.andremoura.com'; 29 29 $version = EASY_XML_SITEMAP_VERSION; 30 30 $generated_on = current_time( 'mysql' ); … … 114 114 * Render a Google News URL entry 115 115 * 116 * @param string $url The URL117 * @param array $news_data News-specific data116 * @param string $url The URL 117 * @param array $news_data News-specific data 118 118 * @return string XML for single news URL entry 119 119 */ … … 172 172 // Add enabled sitemaps to index 173 173 $sitemap_types = array( 174 'posts '=> 'enable_posts',175 'pages' => 'enable_pages',176 'tags' => 'enable_tags',177 'categories' => 'enable_categories',178 'general' => 'enable_general',179 'news' => 'enable_news',174 'posts-index' => 'enable_posts', 175 'pages' => 'enable_pages', 176 'tags' => 'enable_tags', 177 'categories' => 'enable_categories', 178 'general' => 'enable_general', 179 'news' => 'enable_news', 180 180 ); 181 181 … … 195 195 196 196 /** 197 * Generate posts sitemap 197 * Generate posts sitemap index (organized by date or category) 198 * 199 * @return string Complete XML sitemap for posts 200 */ 201 public static function generate_posts_index() { 202 $settings = get_option( 'easy_xml_sitemap_settings', array() ); 203 $organization = isset( $settings['posts_organization'] ) ? $settings['posts_organization'] : 'single'; 204 205 // If single organization, return simple sitemap 206 if ( 'single' === $organization ) { 207 return self::generate_posts_sitemap(); 208 } 209 210 // Otherwise, generate an index 211 $xml = self::get_xml_header( 'index' ); 212 213 if ( 'date' === $organization ) { 214 // Get all months/years with posts 215 global $wpdb; 216 217 $dates = $wpdb->get_results( 218 "SELECT DISTINCT YEAR(post_date) as year, MONTH(post_date) as month, MAX(post_modified) as lastmod 219 FROM {$wpdb->posts} 220 WHERE post_type = 'post' 221 AND post_status = 'publish' 222 GROUP BY YEAR(post_date), MONTH(post_date) 223 ORDER BY year DESC, month DESC" 224 ); 225 226 foreach ( $dates as $date ) { 227 $year = str_pad( $date->year, 4, '0', STR_PAD_LEFT ); 228 $month = str_pad( $date->month, 2, '0', STR_PAD_LEFT ); 229 230 $url = home_url( '/easy-sitemap/posts-' . $year . '-' . $month . '.xml' ); 231 $lastmod = self::format_lastmod( $date->lastmod ); 232 233 $xml .= self::render_sitemap_entry( $url, $lastmod ); 234 } 235 236 } elseif ( 'category' === $organization ) { 237 // Get all categories with posts 238 $categories = get_categories( array( 239 'hide_empty' => true, 240 'orderby' => 'name', 241 'order' => 'ASC', 242 ) ); 243 244 foreach ( $categories as $category ) { 245 // Use category slug directly (WordPress ensures it's URL-safe) 246 $category_slug = $category->slug; 247 248 // Build URL: /easy-sitemap/posts-{slug}.xml (not posts-category-{slug}) 249 $url = home_url( '/easy-sitemap/posts-' . $category_slug . '.xml' ); 250 $lastmod = gmdate( 'c' ); 251 252 $xml .= self::render_sitemap_entry( $url, $lastmod ); 253 } 254 } 255 256 $xml .= self::get_xml_footer( 'index' ); 257 258 return $xml; 259 } 260 261 /** 262 * Generate posts sitemap (single file, all posts) 198 263 * 199 264 * @return string Complete XML sitemap for posts … … 208 273 'orderby' => 'modified', 209 274 'order' => 'DESC', 275 'meta_query' => array( 276 'relation' => 'OR', 277 array( 278 'key' => '_easy_xml_sitemap_exclude', 279 'compare' => 'NOT EXISTS', 280 ), 281 array( 282 'key' => '_easy_xml_sitemap_exclude', 283 'value' => '1', 284 'compare' => '!=', 285 ), 286 ), 287 ); 288 289 $posts = get_posts( $args ); 290 291 foreach ( $posts as $post ) { 292 $url = get_permalink( $post->ID ); 293 $lastmod = self::format_lastmod( $post->post_modified_gmt ); 294 $priority = '0.6'; 295 296 $xml .= self::render_url( $url, $lastmod, $priority ); 297 } 298 299 $xml .= self::get_xml_footer(); 300 301 return $xml; 302 } 303 304 /** 305 * Generate posts sitemap for a specific month/year 306 * 307 * @param string $year Year (YYYY) 308 * @param string $month Month (MM) 309 * @return string Complete XML sitemap for posts in that date 310 */ 311 public static function generate_posts_by_date( $year, $month ) { 312 $xml = self::get_xml_header(); 313 314 $args = array( 315 'post_type' => 'post', 316 'post_status' => 'publish', 317 'posts_per_page' => -1, 318 'orderby' => 'modified', 319 'order' => 'DESC', 320 'date_query' => array( 321 array( 322 'year' => intval( $year ), 323 'month' => intval( $month ), 324 ), 325 ), 326 'meta_query' => array( 327 'relation' => 'OR', 328 array( 329 'key' => '_easy_xml_sitemap_exclude', 330 'compare' => 'NOT EXISTS', 331 ), 332 array( 333 'key' => '_easy_xml_sitemap_exclude', 334 'value' => '1', 335 'compare' => '!=', 336 ), 337 ), 338 ); 339 340 $posts = get_posts( $args ); 341 342 foreach ( $posts as $post ) { 343 $url = get_permalink( $post->ID ); 344 $lastmod = self::format_lastmod( $post->post_modified_gmt ); 345 $priority = '0.6'; 346 347 $xml .= self::render_url( $url, $lastmod, $priority ); 348 } 349 350 $xml .= self::get_xml_footer(); 351 352 return $xml; 353 } 354 355 /** 356 * Generate posts sitemap for a specific category 357 * 358 * @param string $cat_slug Category slug 359 * @return string Complete XML sitemap for posts in that category 360 */ 361 public static function generate_posts_by_category( $cat_slug ) { 362 $xml = self::get_xml_header(); 363 364 // Get category by slug 365 $category = get_category_by_slug( $cat_slug ); 366 367 if ( ! $category ) { 368 return $xml . self::get_xml_footer(); 369 } 370 371 $args = array( 372 'post_type' => 'post', 373 'post_status' => 'publish', 374 'posts_per_page' => -1, 375 'orderby' => 'modified', 376 'order' => 'DESC', 377 'cat' => $category->term_id, 210 378 'meta_query' => array( 211 379 'relation' => 'OR', -
easy-xml-sitemap/trunk/readme.txt
r3422350 r3423502 5 5 Tested up to: 6.9 6 6 Requires PHP: 7.2 7 Stable tag: 1. 1.37 Stable tag: 1.2.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html 10 10 11 Lightweight XML sitemap generator for posts, pages, taxonomies and Google News, with sitemap indexand robots.txt integration.11 Lightweight XML sitemap generator with posts organization options, sitemap index, and robots.txt integration. 12 12 13 13 == Description == 14 14 15 Easy XML Sitemap is a lightweight and efficient plugin that generates XML sitemaps for your WordPress site. It focuses on performance, modularity, and compatibility with custom setups.15 Easy XML Sitemap is a lightweight and efficient plugin that generates XML sitemaps for your WordPress site. It focuses on performance, modularity, and scalability for sites of all sizes. 16 16 17 17 **Key Features** 18 18 19 * Automatically generates XML sitemaps for: 20 + Posts 19 * **Flexible Posts Organization**: Choose how to organize your posts sitemaps 20 + Single sitemap (all posts in one file) 21 + Organize by date (one sitemap per month/year) - ideal for news sites 22 + Organize by category (one sitemap per category) - great for multi-topic blogs 23 * **Sitemap Index**: Automatically generates a sitemap index (`sitemap.xml`) 24 * **Multiple Sitemap Types**: 25 + Posts (with organization options) 21 26 + Pages 22 + Custom post types 23 + Taxonomies 24 + Google News (optional) 25 * Sitemap index file (`sitemap-index.xml`) for improved scalability 26 * Support for large sites with pagination and resource-friendly queries 27 * Simple per-post exclusion option 28 * Robots.txt integration (optional) 29 * Works alongside popular SEO plugins (Yoast, Rank Math, All in One SEO, etc.) 30 * Filters and actions for developers to extend and customize behavior 31 * No front-end bloat – all output is XML 32 33 **Performance-focused** 34 35 This plugin was built to use efficient database queries and optional caching to keep your site fast, even with large content libraries. 36 37 **Developer-friendly** 27 + Categories 28 + Tags 29 + General (comprehensive all-in-one) 30 + Google News (optional, last 2 days) 31 * **robots.txt Integration**: Automatic sitemap URL addition to virtual robots.txt 32 * **Per-Post/Page Exclusion**: Simple checkbox to exclude individual content 33 * **Smart Caching System**: Configurable cache duration with automatic invalidation 34 * **Performance-Optimized**: Efficient queries designed for large content libraries 35 * **Developer-Friendly**: Filters, actions, and clean code structure 36 37 **Perfect For** 38 39 * Blogs with 10,000+ posts (use date or category organization) 40 * News sites publishing frequently 41 * Multi-category content sites 42 * Small to enterprise-level WordPress sites 43 * Developers who need extensibility 44 45 **Works Alongside Popular SEO Plugins** 46 47 Compatible with Yoast SEO, Rank Math, All in One SEO, and others. Simply disable their sitemap feature and use Easy XML Sitemap for better performance. 48 49 **Developer-Friendly** 38 50 39 51 All core components are structured in classes and namespaced, with hooks provided throughout: … … 44 56 * `easy_xml_sitemap_after_clear_cache` 45 57 * `easy_xml_sitemap_meta_box_post_types` 58 * `easy_xml_sitemap_cache_duration` 46 59 * and more… 47 60 48 61 == Installation == 49 62 50 1. Upload the `easy-xml-sitemap` folder to the `/wp-content/plugins/` directory, or install the plugin from the WordPress.org plugin repository. 51 2. Activate the plugin through the **Plugins** menu in WordPress. 52 3. Go to **Settings → Reading** or the plugin settings page (if available) to configure any additional options. 53 4. Visit `https://your-site.com/sitemap-index.xml` to view your sitemap index. 63 **Automatic Installation** 64 65 1. Go to WordPress admin → Plugins → Add New 66 2. Search for "Easy XML Sitemap" 67 3. Click "Install Now" → "Activate" 68 4. Go to Settings → Easy Sitemap to configure 69 70 **Manual Installation** 71 72 1. Download the plugin ZIP file 73 2. Go to Plugins → Add New → Upload Plugin 74 3. Choose the ZIP file and click "Install Now" 75 4. Activate the plugin 76 5. Configure at Settings → Easy Sitemap 77 78 **After Installation** 79 80 1. Visit Settings → Easy Sitemap 81 2. Choose your posts organization method: 82 - **Single**: All posts in one file (best for <5,000 posts) 83 - **Date**: One sitemap per month (best for news/time-based sites) 84 - **Category**: One sitemap per category (best for topic-based sites) 85 3. Enable/disable other sitemap types as needed 86 4. Configure cache duration (default: 1 hour) 87 5. Save settings 88 6. Go to Settings → Permalinks and click "Save Changes" (flush rewrite rules) 89 7. Visit `https://your-site.com/easy-sitemap/sitemap.xml` to verify 90 8. Submit your sitemap to Google Search Console and Bing Webmaster Tools 54 91 55 92 == Frequently Asked Questions == … … 57 94 = Where is my sitemap located? = 58 95 59 By default, the sitemap index is available at: 60 61 `https://your-site.com/sitemap-index.xml` 62 63 Individual sitemaps for content types will be available under URLs like: 64 65 `https://your-site.com/sitemap-post-1.xml` 66 `https://your-site.com/sitemap-page-1.xml` 67 68 The exact URLs may vary depending on your permalink structure. 69 70 = Does this plugin conflict with SEO plugins like Yoast SEO or Rank Math? = 71 72 The plugin is designed to be compatible with common SEO plugins. If another plugin already provides XML sitemaps, you may choose to disable that feature in the SEO plugin settings, or configure Easy XML Sitemap to avoid overlapping functionality. 73 74 = Does it support custom post types and taxonomies? = 75 76 Yes. Custom post types and taxonomies that are set to be public can be included in the sitemap. The plugin uses WordPress APIs to detect and handle them. 77 78 = How can I exclude specific posts or pages from the sitemap? = 79 80 You can exclude individual posts or pages directly from the edit screen by using the **XML Sitemap Options** meta box and checking the option to exclude the content from sitemaps. 81 82 = Can I customize which post types or taxonomies are included? = 83 84 Yes. The plugin provides filters that allow developers to customize which post types and taxonomies are included in the sitemap. Please refer to the developer documentation or source code comments for examples. 85 86 = Does this plugin submit the sitemap to Google or other search engines? = 87 88 No. This plugin generates and serves the XML sitemap files. You can manually submit your sitemap URL in Google Search Console, Bing Webmaster Tools, and similar services, or rely on search engines to discover it via `robots.txt`. 96 The main sitemap index is at: 97 `https://your-site.com/easy-sitemap/sitemap.xml` 98 99 Individual sitemaps are automatically generated based on your organization settings. 100 101 = Does this plugin conflict with SEO plugins? = 102 103 No conflicts. This plugin works alongside popular SEO plugins. If your SEO plugin has sitemap functionality, you can disable it and use Easy XML Sitemap instead for better performance on large sites. 104 105 = Which posts organization method should I choose? = 106 107 * **Single** (default): Best for sites with <5,000 posts. All posts in one file. 108 * **Date**: Best for news sites, blogs with frequent updates, or sites with 10,000+ posts. Creates one sitemap per month/year. 109 * **Category**: Best for multi-topic sites with well-organized categories. Creates one sitemap per category. 110 111 You can change this anytime in Settings → Easy Sitemap. 112 113 = Does it support custom post types? = 114 115 Currently, the plugin supports posts and pages. Custom post type support is planned for a future release. 116 117 = How do I exclude specific posts from the sitemap? = 118 119 1. Edit the post or page 120 2. Look for "XML Sitemap Options" in the sidebar (Gutenberg) or below the editor (Classic) 121 3. Check "Exclude from XML sitemaps" 122 4. Update/save the post 123 124 = Does this plugin submit the sitemap to search engines? = 125 126 No, you need to manually submit your sitemap URL to: 127 * [Google Search Console](https://search.google.com/search-console) 128 * [Bing Webmaster Tools](https://www.bing.com/webmasters) 129 130 Enter: `easy-sitemap/sitemap.xml` in the sitemap submission field. 131 132 = Why isn't my sitemap showing in robots.txt? = 133 134 The automatic robots.txt integration only works with WordPress's **virtual** robots.txt. If you have a physical `robots.txt` file in your site root, the plugin can't modify it. Either: 135 1. Delete the physical file (after backing it up), or 136 2. Manually add this line: `Sitemap: https://your-site.com/easy-sitemap/sitemap.xml` 137 138 Check Settings → Easy Sitemap for detection and instructions. 139 140 = How do I clear the sitemap cache? = 141 142 Go to Settings → Easy Sitemap and click "Regenerate All Sitemaps". The cache also clears automatically when you: 143 * Publish, update, or delete posts/pages 144 * Change categories or tags 145 * Modify sitemap settings 146 147 = My sitemaps return 404 errors = 148 149 1. Go to Settings → Permalinks 150 2. Click "Save Changes" (this flushes rewrite rules) 151 3. Test your sitemap URL again 152 153 = How do I check which organization method is active? = 154 155 Go to Settings → Easy Sitemap and look at the "Posts Organization" setting. You'll see three options: 156 * Single sitemap 157 * Organize by date 158 * Organize by category 159 160 The selected option shows which structure is currently active. 89 161 90 162 == Screenshots == 91 163 92 1. Example of XML sitemap index output in the browser. 93 2. Example of an individual sitemap for posts. 94 3. Meta box for excluding individual posts from the sitemap. 164 1. **Admin Settings Page** - Configure sitemap types and posts organization 165 2. **Posts Organization Options** - Choose between single, date, or category organization 166 3. **Sitemap URLs Table** - View all your sitemap URLs with status 167 4. **robots.txt Integration** - Automatic sitemap detection and warnings 168 5. **Per-Post Exclusion** - Simple checkbox in the post editor 169 6. **Sitemap Index Output** - Styled XML view in browser 170 7. **Individual Sitemap Output** - Clean, valid XML for search engines 95 171 96 172 == Changelog == 97 173 98 = 1.1.3 = 99 * Add plugin icons. 174 = 1.2.0 - 2024-12-19 = 175 176 **Added** 177 * Posts organization options: single, by date, or by category 178 * Dynamic posts index that adapts to organization method 179 * Posts by date sitemaps: `/easy-sitemap/posts-YYYY-MM.xml` 180 * Posts by category sitemaps: `/easy-sitemap/posts-{category-slug}.xml` 181 * Radio button UI for organization selection 182 * Automatic cache regeneration when settings change 183 184 **Changed** 185 * Posts sitemap now serves as index when date/category organization enabled 186 * Enhanced admin settings page with better help text 187 * Improved cache invalidation for dynamic sitemap types 188 * Updated rewrite rules to support dynamic URL patterns 189 190 **Fixed** 191 * Critical: Fatal error in sitemap controller causing activation failure 192 * Critical: Malformed rewrite rules and duplicate function declarations 193 * Performance: Optimized database queries for organized sitemaps 194 * Cache key generation for dynamic sitemap types 195 196 = 1.1.3 - 2024-12-15 = 197 * Added plugin icons for WordPress.org directory 198 * Enhanced visual branding 199 200 = 1.1.0 - 2024-12-05 = 201 * Added sitemap index file (`sitemap.xml`) 202 * Added robots.txt integration with automatic detection 203 * Changed base path from `/easy-xml-sitemap/` to `/easy-sitemap/` 204 * Updated settings menu name to "Easy Sitemap" 205 * Enhanced admin interface with better organization 206 * Added robots.txt status detection and warnings 207 * Improved cache management 208 209 = 1.0.0 - 2024-12-05 = 210 * Initial release 211 * Multiple sitemap types (posts, pages, tags, categories, general, news) 212 * Per-post/page exclusion controls 213 * Smart caching system 214 * Admin settings page 215 * Classic and block editor support 216 * Multisite compatible 217 218 == Upgrade Notice == 219 220 = 1.2.0 = 221 Major update with posts organization options. Recommended for sites with 10,000+ posts. Backup before upgrading. After upgrade: 1) Go to Settings → Easy Sitemap and choose organization method, 2) Go to Settings → Permalinks and save, 3) Clear all caches, 4) Resubmit sitemap to search engines. 100 222 101 223 = 1.1.0 = 102 * Added support for per-post exclusion via meta box. 103 * Introduced caching layer for sitemap queries to improve performance. 104 * Improved compatibility with custom post types and taxonomies. 105 * Enhanced robots.txt integration for sitemap index. 106 * Refactored internal classes to be more modular and extensible. 107 108 = 1.0.1 = 109 * Fixed minor issue with sitemap index URLs in certain permalink configurations. 110 * Improved handling of empty or non-public post types. 224 URL structure changed. Main sitemap moved to `/easy-sitemap/sitemap.xml`. After updating, flush permalinks (Settings → Permalinks → Save) and resubmit to Google Search Console. 111 225 112 226 = 1.0.0 = 113 * Initial release of Easy XML Sitemap. 114 * XML sitemap index for posts, pages, and taxonomies. 115 * Basic support for large sites with pagination. 116 117 == Upgrade Notice == 118 119 = 1.1.0 = 120 This release introduces per-post exclusion and an internal caching layer for improved performance. It is recommended to clear any external caches (page cache, object cache) after upgrading. 227 Initial release of Easy XML Sitemap. 228 229 == Performance == 230 231 This plugin is designed for performance: 232 233 * **Efficient Database Queries**: Optimized for large databases 234 * **Smart Caching**: Transient-based with configurable duration 235 * **Conditional Loading**: Admin resources only load when needed 236 * **No Front-End Impact**: Pure XML output, no styling or JavaScript 237 * **Scalable Organization**: Date/category methods handle 100,000+ posts 238 239 **Benchmarks** (average generation time on standard hosting): 240 * 1,000 posts: <0.5 seconds 241 * 10,000 posts (single): ~2 seconds 242 * 10,000 posts (by date): <0.5 seconds per month 243 * 50,000 posts (by date): <0.5 seconds per month 244 245 == Support == 246 247 Need help? We're here for you: 248 249 * [Support Forum](https://wordpress.org/support/plugin/easy-xml-sitemap/) 250 * [GitHub Issues](https://github.com/andremoura/easy-xml-sitemap/issues) 251 * [Documentation](https://wordpress.andremoura.com) 252 * Email: [email protected] 253 254 == Contributing == 255 256 Contributions are welcome! Visit our [GitHub repository](https://github.com/andremoura/easy-xml-sitemap) to: 257 * Report bugs 258 * Suggest features 259 * Submit pull requests 260 * Review code 261 262 See [CONTRIBUTING.md](https://github.com/andremoura/easy-xml-sitemap/blob/main/CONTRIBUTING.md) for guidelines. 263 264 == Privacy == 265 266 This plugin: 267 * Does NOT collect any user data 268 * Does NOT make external API calls 269 * Does NOT use cookies 270 * Does NOT track users 271 * Only generates XML files based on your public WordPress content 272 273 == Credits == 274 275 **Developer**: André Moura 276 **Website**: [wordpress.andremoura.com](https://wordpress.andremoura.com) 277 **License**: GPL v2 or later 278 279 == Links == 280 281 * [Plugin Homepage](https://wordpress.andremoura.com) 282 * [GitHub Repository](https://github.com/andremoura/easy-xml-sitemap) 283 * [Support Forum](https://wordpress.org/support/plugin/easy-xml-sitemap/) 284 * [Sitemaps Protocol](https://www.sitemaps.org/protocol.html) 285 * [Google Sitemap Guidelines](https://developers.google.com/search/docs/advanced/sitemaps/overview)
Note: See TracChangeset
for help on using the changeset viewer.