=== Spek Regg SEO ===
Contributors: spekregg
Tags: seo, sitemap, schema, redirects, indexnow
Requires at least: 6.5
Tested up to: 7.0
Requires PHP: 8.0
Stable tag: 1.0.29
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.29 =
* [FIX] Todos los selectores de imagen del editor, Open Graph, X y Schema abren correctamente la Biblioteca de medios.
* [FIX] La integración de medios se carga una sola vez con las dependencias de WordPress y admite imágenes sin miniatura.
* [FIX] Los botones para quitar imágenes limpian de forma coherente el ID, la URL y la vista previa.
* [FIX] El logotipo de Schema deja de enviar una URL y un ID bajo el mismo nombre de ajuste.
* [FIX] Las imágenes globales elegidas para Open Graph y X se utilizan realmente en las etiquetas sociales.
* [FIX] La aplicación ALT conserva las imágenes que la vista previa ya marcó como correctas.
* [CHANGE] El pie de las páginas administrativas del plugin muestra la versión instalada.

= 1.0.28 =
* [CHANGE] La interfaz de redes sociales muestra X en lugar de Twitter, conservando internamente las etiquetas técnicas compatibles.
* [CHANGE] La sobrescritura ALT procesa hasta 40 contenidos por lote y espera 60 segundos antes del siguiente lote.
* [FIX] El análisis ALT omite imágenes cuyo texto alternativo ya contiene el título completo del contenido, solo o acompañado por palabras clave.

= 1.0.27 =
* [FIX] Título SEO y Descripción SEO muestran su valor efectivo y permanecen en solo lectura visual cuando usan el modo Auto.
* [FIX] El metabox se renderiza completo en PHP 8 y sus pestañas vuelven a funcionar.
* [FIX] La herramienta de ALT omite resultados ya actualizados y refresca progresivamente la vista previa.

= 1.0.26 =
* [NEW] Herramientas includes an explicit preview-and-apply utility that overwrites image ALT values with the post title and unique post-local keywords.
* [NEW] The first eligible image uses the post title; additional images use unique title-and-keyphrase combinations found in the post itself.
* [NEW] Existing and missing ALT values can be processed site-wide in bounded ID-cursor batches, optionally including featured images.
* [SAFETY] Preview scanning is read-only, application requires an explicit confirmation, and each post-content change creates a revision first.
* [SAFETY] Batch application is limited to the exact maximum post ID captured by the completed preview.
* [ACCESSIBILITY] Decorative, presentation, aria-hidden, pixel and explicitly marked decorative images keep an empty ALT and are never overwritten.
* [IMPROVEMENT] Local WordPress attachment ALT metadata is synchronized during the explicit operation, with featured-image context taking precedence.
* [FIX] Public post-type selectors in the image ALT tools now contain the available post types.
* [FIX] Images without explicit width and height are no longer misclassified as 1x1 tracking pixels.
* [TEST] Frontend validation now discovers WordPress 7 REST routes correctly and exits non-zero when discovery fails.
* [FIX] Article-page Schema no longer emits a broken author @id whose Person node is absent from the graph.

= 1.0.25 =
* [NEW] Canonical and focus keywords are generated and persisted automatically when posts and pages are saved.
* [NEW] Herramientas now includes a batched site scanner that completes missing canonical and keyword fields without overwriting manual or locked values.
* [NEW] Canonical and focus keywords are visible in the post/page editor with Auto, Manual and Locked ownership controls.
* [FIX] The editor now loads existing SEO metadata instead of rendering empty fields.
* [FIX] Focus keywords now use the `_srseo_focus_keyword` key consumed by the analyzer.
* [FIX] Comma-separated keywords are analyzed using the first keyword as the primary phrase.
* [FIX] Editing an automatic field switches it to Manual so an editorial value cannot be overwritten on save.
* [FIX] Legacy metadata without ownership modes is preserved as editorial data.
* [FIX] Social title, description and image modes are saved, and the social image uses the key consumed by the frontend resolver.
* [FIX] The metabox analysis button now executes and displays the current SEO score and checks.
* [FIX] Herramientas import, export and cache forms now use matching secure admin-post handlers; configuration downloads send valid headers.
* [FIX] SEO imports now include canonical, robots and focus-keyword ownership without overwriting existing plugin metadata.
* [FIX] Image-alt scanning now includes content and featured-only images, counts featured images correctly and no longer skips posts due to operator precedence.
* [FIX] Image-alt scan batches now retain access to their results table instead of stopping on a JavaScript scope error.
* [FIX] Image-alt scan progress reports the real number of inspected contents instead of adding a fixed batch size.
* [FIX] Explicit image-alt saving processes content with the correct post context; public-output mode performs no database writes.
* [SECURITY] Image-alt scan rows are built with safe DOM APIs and correct authenticated wp-admin edit URLs.
* [SECURITY] Metadata batches require administrator capability and a valid admin AJAX nonce.
* [PERFORMANCE] Bulk metadata completion uses bounded ID-cursor batches suitable for large sites.

= 1.0.24 =
* [SECURITY] REST analysis and preview endpoints now enforce per-post edit permissions.
* [SECURITY] Admin JavaScript renders server and media values without unsafe HTML concatenation.
* [FIX] The individual "Contenido analizado" repair action is now routed correctly.
* [FIX] Safe-fix batches now record, verify and count executed actions correctly.
* [FIX] Partial content-analysis results can no longer be marked as completed.
* [FIX] Failed batch results are no longer counted twice in progress and totals.
* [FIX] Manual reset reliably releases a stuck content-analysis lock while preserving results.
* [FIX] Output buffering only closes the buffer owned by Spek Regg SEO.
* [FIX] Removed callback deletion that could suppress complete SEO output from third-party plugins.
* [FIX] Feature toggles are respected for titles, descriptions, canonical, robots, social and Schema output.
* [FIX] Taxonomy SEO titles are applied before the document title is rendered.
* [FIX] HTTP 410 rules now return a real Gone response instead of calling wp_redirect().
* [FIX] IndexNow queue processing is registered exactly once.
* [IMPROVEMENT] Site-audit locks are always released, including after exceptions.
* [IMPROVEMENT] Empty sites and non-applicable checks no longer receive false errors or inflated scores.
* [IMPROVEMENT] Content quality checks use UTF-8-aware word counting and post-type capabilities.
* [IMPROVEMENT] Published content is resolved once and reused across audit checks.
* [IMPROVEMENT] Site-wide checks use a bounded, disclosed sample on very large sites while the batch analyzer continues to cover every published item.

= 1.0.23 =
* [NEW] Automatic image alt text generation for missing alt attributes.
* [NEW] ImageAltManager: central class (includes/class-image-alt-manager.php) with in-memory cache, decorative image detection, meaningful text validation.
* [NEW] Settings UI: "Texto alternativo automático para imágenes" section in Ajustes generales with master toggle, source selector (Smart/Post title/Caption/Attachment title/Custom template), max length (50-250), scope (featured, content, gallery, public CPTs), and replace-auto option (off by default).
* [NEW] Smart mode priority: manual alt → caption → attachment title → post title → site name. Ignores generic filenames (IMG_1234, DSC_0001, etc.).
* [NEW] Custom template with placeholders: {post_title}, {image_title}, {caption}, {site_name}, {post_type}, {index}.
* [NEW] WP_HTML_Tag_Processor (WP >= 6.2) for safe HTML processing, DOMDocument fallback.
* [NEW] Decorative image detection: role="presentation", aria-hidden="true", data-srseo-decorative, 1×1 pixels, known decorative CSS classes (icon, emoji, avatar, tracking-pixel, wp-smiley).
* [NEW] Bulk scanning tool in Herramientas: "Revisar imágenes sin texto alternativo" with per-post preview of generated alt, select-all, batch apply (public output or save with revision).
* [NEW] AJAX endpoints: srseo_toggle_decorative, srseo_image_alt_scan, srseo_image_alt_apply.
* [NEW] Live preview in settings showing sample alt generation.
* [NEW] Upgrade notice for existing installations (non-intrusive, dismissible).
* [NEW] Upgrades preserve existing settings; new installations auto-enable the feature.
* [FIX] Never overwrites manual alt text. Never modifies decorative images. Never writes to database during frontend visits.
* [FIX] Same image in different posts gets contextual alt per post (no global _wp_attachment_image_alt modification).
* [FIX] External images (no attachment ID) receive contextual alt from post title.
* [FIX] Template result that would be empty falls back to post title.
* Version bumped to 1.0.23.

= 1.0.22 =
* [NEW] SchemaProviderCoordinator: adapter-based system to detect GeoDirectory/Directorist external LocalBusiness
* [NEW] SchemaProviderAdapterInterface + 3 adapters (GeoDirectory, Directorist, Generic) with filter integration
* [NEW] SocialProfileDetector: 7-source detection (settings, theme mods, menus, widgets, Gutenberg, plugins, schema)
* [NEW] social_enabled toggle: gates OG/Twitter/sameAs output; auto-activates when profiles detected
* [NEW] Schema admin view: provider selector (Auto/Spek/External) with live diagnostic display
* [FIX] CRITICAL: SchemaManager no longer fabricates @id for external providers. Only links mainEntity when Spek owns LB or provider is verified-enriched via official hooks. Prevents broken @id references.
* [FIX] remove_empty_properties(): fully rewritten with recursive clean_schema_value() — strips whitespace-only strings, empty arrays, single-@type objects at any depth
* [FIX] LocalBusiness: maximum 1 per entity. Coordinated via resolve_localbusiness() with per-post cache
* [FIX] DescriptionManager.generate() now sanitizes after apply_filters()
* [FIX] ContentGenerator.get_excerpt(): first paragraph normalized via sanitize_text() before truncation
* [FIX] SocialProfileDetector.validate_url(): rewritten with str_starts_with(), wp_http_validate_url(), proper share-link rejection (facebook/sharer, twitter/intent, {url} placeholders)
* [FIX] Bluesky pattern: now supports bsky.app/profile/username format
* [FIX] Tests: uses DOMDocument/XPath for ALL JSON-LD blocks (not just first), //title counting, content discovery via REST API
* [FIX] Removed broken "Procesamiento por Lotes" section from tools page (stub that never worked)
* [DEV] Autoloader updated for SpekReggSEO\Adapters\ namespace
* Version bumped to 1.0.22.

= 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.
