=== Spek Regg SEO ===
Contributors: spekregg
Tags: seo, sitemap, schema, redirects, indexnow
Requires at least: 6.5
Tested up to: 6.7
Requires PHP: 8.0
Stable tag: 1.0.20
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Complete SEO toolkit for WordPress with metadata, Schema, XML sitemaps,
IndexNow, redirects, analysis and diagnostic logging.

== Installation ==

1. Upload the plugin ZIP from Plugins > Add New > Upload Plugin.
2. Activate Spek Regg SEO.
3. Complete the setup assistant.
4. Review Ajustes > Diagnóstico if the site reports a problem.

== Changelog ==

= 1.0.20 =
* [FIX] Meta description: single output guaranteed via improved callback removal + output buffer
* [FIX] Description cleanup: removes broken endings like "lácteos,.", "texto..", trailing commas/conjunctions
* [FIX] Social image: commerce listings now correctly use their own image instead of global logo
* [FIX] GeoDirectory images: resolved via _geodir_featured_image, geodir_get_post_images(), and post thumbnail
* [FIX] Directorist images: resolved via _listing_prv_img and _listing_img arrays
* [FIX] Generic directory image resolution: checks common meta keys for any custom directory type
* [FIX] Schema relationship: WebPage.mainEntity → URL/#LocalBusiness, LocalBusiness.@id → URL/#LocalBusiness
* [FIX] LocalBusiness.mainEntityOfPage → URL/#webpage, WebPage.mainEntityOfPage added
* [FIX] Cleaned empty sameAs, empty review, and all empty schema properties from all graph nodes
* [FIX] Diagnostic logging: added srseo_diagnostic_log() for admin debugging of callbacks, images, schema
* [FIX] Output buffer now always active to ensure exactly one meta description
* [FIX] Better deduplication: identifies callbacks by class/method/plugin origin before removal
* Version bumped to 1.0.20.

= 1.0.13 =
* [FIX] ZIP packaging: internal root folder always exactly `spek-regg-seo/`, never versioned.
* [FIX] Build script: validates ZIP structure (single root, correct name, no backslashes).
* [FIX] Diagnostics: detects duplicate Spek Regg SEO installations (versioned folders).
* [FIX] Stable basename, text domain and slug always `spek-regg-seo`.
* Version bumped to 1.0.13.

= 1.0.12 =
* IndexNow: rewritten with full queue system, exponential backoff retry, and proper add/update/delete detection via transition_post_status.
* IndexNow: key now served from site root via template_redirect (https://dominio.com/CLAVE.txt) instead of wp-uploads.
* IndexNow: noindex detection via blog_public, wp_robots filter, X-Robots-Tag header, and HTML meta robots parsing.
* IndexNow: wildcard excluded paths (/privado/*, /gracias-*) with 20-pattern limit.
* IndexNow: URL deduplication (60s window) and same-domain validation.
* IndexNow: dedicated srseo_indexnow_queue table with retry_count, max_retries, next_attempt, last_error.
* IndexNow: WP-Cron processing every 60 seconds (srseo_indexnow_cron schedule), with process lock.
* IndexNow: 6 action buttons (Comprobar configuración, Enviar URL de prueba, Procesar cola ahora, Reintentar fallidos, Vaciar cola, Exportar registros).
* IndexNow: key preview (last 4 chars only, no full key exposure).
* IndexNow: migration assistant from Microsoft IndexNow plugin (key, excluded paths, logs).
* IndexNow: original plugin conflict detection (prevents dual-engine operation).
* IndexNow: 6 status cards in admin (Estado, Pendientes, Enviadas, Fallidas, Último envío, Próximo cron).
* IndexNow: dedicated queue table and logs table with proper dbDelta() and indexes.
* IndexNow: zero new menus, zero new classes, zero new options (all under existing srseo_settings).
* IndexNow: template_redirect key serving non-admins can access (public verification).
* License: adapted from Microsoft IndexNow plugin (GPL-2.0+), all code rewritten under SpekReggSEO namespace.
* Version bumped to 1.0.12.

= 1.0.11 =
* [FIX] Meta description: added deduplication via wp_head callback removal (GeoDirectory, Directorist) + output buffer fallback.
* [FIX] Description truncation: now ends at complete sentence (., !, ?) rather than mid-word/mid-sentence.
* [FIX] Social image priority: directory listing main image (GeoDirectory, Directorist) now ranks above featured image, gallery, and global image. Priority: manual > listing image > gallery > featured > content > global > logo > icon.
* [FIX] BreadcrumbList @id: now uses trailingslashit(current URL) . '#breadcrumb' — adds trailing slash before fragment.
* [FIX] WebPage schema: directory/commerce listings are always WebPage, never BlogPosting. Added mainEntity linking to external LocalBusiness when detected.
* [FIX] Schema sameAs: empty arrays removed from Organization and all @graph nodes via post-processing.
* [FIX] Audit 0/135 bug: process_batch now uses count(results) as authoritative processed count. Added count_pending_below_cursor() to detect posts the cursor skipped. Never resets cursor infinitely. Properly stabilizes total = processed + failed on completion.
* [FIX] No URL/slug modification: confirmed zero wp_update_post/wp_insert_post calls.
* Version bumped to 1.0.11.

= 1.0.10 =
* Fixed analysis stuck at 0% — replaced meta_query NOT EXISTS with ID-based cursor (WHERE ID > last_processed_id).
* Fixed completed-with-zero corrupt state — auto-detection and repair in get_status() and start().
* Fixed empty tabs in analysis page — all content analysis tabs (Todas/Correctas/Advertencias/Errores) now display real results from srseo_analysis_results.
* Added content type, score badge, status, and "Ver detalles" button per result row.
* Added tab counter badges showing exact counts of pass/warning/error results.
* Fixed meta description duplication — added output buffer callback that strips all but the first <meta name="description">.
* Added ob_start() filter_head_meta() with regex that keeps exactly one meta description.
* Added compatibility mode setting (off by default) for opt-in head inspection.
* Fixed "Corregir problemas seguros" button — now correctly routes to batch analysis start.
* Added srseo_audit_reset AJAX endpoint with reset_stuck() method that clears lock without deleting results.
* Added "Restablecer análisis detenido" button visible during running/failed states.
* Added timeout (30s), retry (3x), and proper error display in analysis JS cycle.
* Added lock age detection (get_lock_age()) for stale lock recovery.
* Added proper WP-Cron continuation — Cron::run_batch_process() calls process_batch().
* Added logged actions: auditoría iniciada, lote procesado, contenido analizado, lock recuperado, completado.
* Separated site audit (SiteAuditManager score) from content analysis (BatchProcessor results) — no longer conflated.
* Updated to v1.0.10 (patch increment).

= 1.0.9 =
* Fixed corrupt "✓ Análisis completado" state showing 0/135 progress when analysis never ran or failed silently.
* Added per-post SEO results table (score, title, URL, status) below the analysis progress panel.
* Saved individual analysis results (srseo_analysis_results option) with scores, errors, and timestamps.
* Added get_results() method to BatchProcessor for frontend access to per-post scores.
* Added repair_state() detection and auto-fix for corrupt "completed with zero" state.
* Results table sorts by score ascending (worst first) for easy prioritization.

= 1.0.8 =
* Fixed "Acción desconocida" error when pressing "Corregir" on "Contenido analizado" row.
* Added `analyze_pending` and `start_content_analysis` cases to `ajax_audit_fix` switch.
* Updated audit fix JS to trigger batch content analysis instead of showing alert.

= 1.0.7 =
* Added dedicated URL settings tab (Spek Regg SEO → Configuración → URLs).
* Default URL mode is "Manual — recomendado" (never modify slugs without permission).
* Added "Permitir optimización automática de URLs" toggle (off by default).
* Added scope selector: borradores, nuevo contenido, selección manual, todo (never auto-selected).
* Added per-post-type checkboxes for URL management (all disabled initially).
* Confirmed via code audit: zero calls to wp_update_post, wp_insert_post, or slug modification.
* Analysis and batch processing never modify URLs — read-only recommendations only.
* Rewrote BatchProcessor with real cursor (last_processed_id) and 6-state machine.
* Added proper Analyzer::analyze() call to store _srseo_score per content.
* Added 6 new AJAX endpoints: srseo_audit_start/process/status/pause/resume/cancel.
* Added batch analysis JavaScript with polling, progress bar, pause/resume/cancel UI.
* Added og:image:alt and twitter:image:alt with alt text priority chain.
* Added corrupt state repair in BatchProcessor.
* Fixed batch processing to only process posts missing _srseo_score.
* Added real-time progress display in analysis admin page.
* Added admin CSS for progress bar and analysis status.

= 1.0.5 =
* Added Organization logo fallback chain (custom_logo → site_icon) with @id and contentUrl.
* Fixed social image priority to include product image, custom logo, and site icon.
* Added OG/Twitter meta output to homepage, front page, and term pages.
* Added publisher reference from WebSite to Organization in Schema.
* Added data: URI and invalid URL protection in social image detection.
* Fixed contentUrl field for Organization logo in JSON-LD.
* Generated OG/Twitter images when only logo or site icon exists.
* Maintained no-image mode (summary card, no og:image output).

= 1.0.4 =
* Fixed duplicate canonical, robots and BreadcrumbList output.
* Front page now omits breadcrumb schema.
* Twitter card adapts to image availability (summary/summary_large_image).
* Improved dashboard data accuracy.

= 1.0.3 =
* Corrected production ZIP paths for Linux and WordPress compatibility.
* Fixed activation, deactivation, administrator capabilities and menu registration.
* Fixed special-class autoloading.
* Removed duplicate administrative asset loading.

= 1.0.2 =
* Fixed autoloading, administration routes, settings persistence and redirect storage.
* Added diagnostics and internal event logs under Settings.
* Added safe database upgrade checks and production package validation.