=== McCrossenSEO ===
Contributors: mccrossenmarketing
Tags: seo, schema, open graph, redirects, sitemap
Requires at least: 6.0
Tested up to: 6.9
Stable tag: 3.0.11
Requires PHP: 8.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

WordPress SEO plugin with meta management, schema markup, XML sitemap, Open Graph, redirect manager, and optional platform-powered optimization.

== Description ==

McCrossenSEO™ handles all standard on-page SEO requirements out of the box — meta titles and descriptions, robots meta, canonical URLs, Open Graph, Twitter/X cards, JSON-LD schema markup, XML sitemap generation, redirect management, and bulk editing.

**Free features (no account required):**

* Meta title and description management per post, page, and custom post type
* Character count indicators (60 for titles, 160 for descriptions)
* Global title pattern with configurable separator
* Robots meta controls (noindex, nofollow, max-snippet, max-image-preview, max-video-preview)
* Canonical URL management with paginated content support
* Open Graph meta (title, description, image) with fallback chain
* Twitter/X Card meta with card type selector
* JSON-LD schema markup (Article, WebPage, BreadcrumbList, Organization, LocalBusiness)
* XML sitemap with configurable post types and automatic noindex exclusion
* Redirect manager (301, 302, 307)
* Bulk editor for titles and descriptions across all content
* Internal link suggestions
* Import from Yoast, RankMath, AIOSEO, and SEOPress (one-click migration)
* Analytics and tracking code injection (GA4, GTM, Facebook Pixel) — only loads when administrator configures IDs
* Site verification codes (Google Search Console, Bing Webmaster)
* robots.txt editor
* Diagnostics panel with rate-limited automatic error reporting

**Optional connected features (requires McCrossen Marketing account):**

* URL Optimization scoring across 59 ranking signals
* AI-powered SEO recommendations
* One-click Plugin Bridge: apply meta titles, descriptions, headings, alt text, and schema changes directly from the platform to your WordPress site
* Platform-pushed llms.txt with audit-score-weighted content

The free version is fully functional as a standalone SEO plugin. No account or external connection is required for free-tier features.

== Installation ==

1. Upload the `mccrossenseo` directory to `/wp-content/plugins/`
2. Activate the plugin through the Plugins menu in WordPress
3. Go to McCrossenSEO™ in the admin sidebar to configure settings

To enable connected features, click "Connect to McCrossen" under McCrossenSEO™ > Connect2McCrossen.

== Frequently Asked Questions ==

= Do I need a McCrossen Marketing account to use this plugin? =

No. All core SEO features work without an account or external connection. A McCrossen Marketing account is only required for URL Optimization scoring, AI recommendations, and the Plugin Bridge.

= Will this conflict with Yoast, RankMath, or other SEO plugins? =

McCrossenSEO™ uses its own meta keys and does not conflict at the database level. However, running two SEO plugins simultaneously will produce duplicate meta tags. Deactivate your previous SEO plugin after importing. McCrossenSEO™ includes a one-click importer for Yoast, RankMath, AIOSEO, and SEOPress.

= Does the free version send any data externally? =

The free version makes no external connections by default. External requests are only made when you explicitly configure them: connecting to McCrossen Marketing, or adding a Google Analytics / Google Tag Manager / Facebook Pixel ID. See the External Services section below for details.

= What post types are supported? =

McCrossenSEO™ works with all public post types including pages, posts, and WooCommerce products. The meta box appears automatically on any public custom post type.

= What PHP version is required? =

PHP 8.0 or higher is required.

== Screenshots ==

1. SEO meta box on the post editor with title and description fields
2. Settings panel with global title pattern, schema, and sitemap configuration
3. Bulk editor for managing titles and descriptions across all content
4. XML sitemap configuration with post type selection
5. Import tool with one-click migration from Yoast, RankMath, AIOSEO, and SEOPress

== Changelog ==

= 3.0.11 =
* Bug fix: the McCrossenSEO™ meta box now renders in its canonical position below the content on the post editor for all post types. On some sites it had been appearing in the sidebar due to leftover state from an earlier preview version — that position is now restored automatically.
* All SEO features and platform-connected features (where enabled) continue to work exactly as before; no settings or workflows changed.
* Removed an unused internal background process that was logging PHP debug warnings on sites running with `WP_DEBUG` enabled. If you saw those warnings in your error log, they will stop after this update.
* Cleanup runs automatically on update: a leftover scheduled task and cached options from the removed layer are cleared with no action required.
* Internal: the `McCrossen_SDK` and `McCrossen_CrossSell` classes were removed (neither was wired to any user-facing code path). The 15-minute heartbeat (`MCCM_SEO_Heartbeat`) is unaffected and remains the sole platform sync channel for connected installs. Connection state is read directly from the stored API key. The `sdk_version` field was dropped from heartbeat and diagnostic payloads (it was always reporting the `'unknown'` fallback). All other payload fields are unchanged.

= 3.0.10 =
* Plugin Check: added missing `Squiz.PHP.DiscouragedFunctions.Discouraged` to the existing `phpcs:ignore` annotation on `class-mccm-seo-admin.php:727` (`@set_time_limit( 60 )` in `ajax_404_purge_noise`). The `WordPress.PHP.*` rules were already covered; the Squiz-flavor rule was missed when alpha.40 originally annotated this line. The sibling call at line 876 (rollback batch) already had the Squiz rule covered, which is why Plugin Check flagged only one of them. Plugin Check now returns zero ERRORs / zero WARNINGs.

= 3.0.9 =
* Hotfix: 3.0.8 contained a parse error in `includes/helpers.php` introduced when adding the new `mccm_seo_custom_head_allowed_html()` and `mccm_seo_sanitize_custom_head()` helpers — the existing `if ( ! function_exists( 'mccm_seo_show_in_dev' ) ) :` opener was inadvertently removed, leaving a stranded `endif;` and a fatal `syntax error, unexpected token "endif"`. Restored the opener. Plugin now activates cleanly on a debug-enabled WordPress install. Verified via `php -l` across all PHP files.

= 3.0.8 =
* Bug fix: button labels in the Elementor preview-draft meta box ("Replacing…", "Replaced ✓", "Discarding…", "Discarded ✓") were displaying their literal escape sequences (e.g. `Replacing\u2026`) because PHP single-quoted strings do not interpret `\u` escapes. Replaced with the actual UTF-8 characters.

= 3.0.7 =
* WordPress.org review round 2 fixes:
* Replaced broken Google Tag Manager terms URL in readme.txt (`marketingplatform.google.com/about/analytics/tag-manager-terms/` → `www.google.com/analytics/terms/tag-manager/`).
* Removed inline `<script>` block from the Connect tab; the manual-setup toggle handler is now in the already-enqueued `mccm-seo-admin.js`.
* Removed inline `<script>` block from the Replace-Original meta box on Elementor preview drafts; logic moved to a new `mccm-seo-preview-draft.js` enqueued via `admin_enqueue_scripts` only on screens that render that meta box.
* Replaced direct `ABSPATH` references in `class-mccm-seo-llms.php` and `class-mccm-seo-robots.php` with `get_home_path()` so site root path is computed via a WordPress function.
* Replaced direct `WP_PLUGIN_DIR` reference in `class-mccm-seo-migrator.php` with a path derived from the plugin's own location (`MCCM_SEO_DIR`).
* Replaced raw `is_writable()` probe in `class-mccm-seo-robots.php` with the `WP_Filesystem` API.
* Custom `<head>` code option (`mccm_seo_custom_head`) is now escape-on-output via `wp_kses` with a head-content allowlist, matching the same allowlist applied at save time. Removes reliance on save-time-only sanitization.
* Removed `JSON_UNESCAPED_SLASHES` flag from JSON-LD output in `class-mccm-seo-schema.php`. `JSON_HEX_TAG` is preserved as the security-relevant flag.

= 3.0.0-alpha.1 =
* **Plugin slug renamed** from `mccrossen-seo` to `mccrossenseo` to match the McCrossenSEO™ trademark form. Existing installs migrate automatically on activation.
* WordPress.org compliance pass: removed dead watchdog references, removed deprecated Google/Bing sitemap pings (deprecated 2023), fixed sanitization gaps in nonce and content readers, hardened REST endpoint permission_callbacks (action-plan, webpage-draft) so authentication runs in the permission_callback rather than the handler, added `JSON_HEX_TAG` to JSON-LD output, removed the dead `MCCM_PLATFORM_VERSION` conditional in the tracking tab.
* Inline `<script>` and `<style>` blocks across admin views converted to enqueued assets per WP.org guidelines. Affected pages: Pending Optimizations, Redirects + 404 Monitor, Import, robots.txt editor.
* Frontend tracking pixel injection (GA4, GTM, Facebook Pixel) now uses `wp_print_script_tag()` and `wp_print_inline_script_tag()` per WordPress 5.7+ guidelines.
* External Services section in this readme rewritten to accurately disclose all conditionally-loaded third-party scripts.
* Cross-sell module's inline JS converted to use `wp_print_inline_script_tag()`.
* Diagnostics file-scope filter updated to recognize the renamed plugin folder.

= 2.1.7 =
* WordPress.org submission release
* License updated to GPL-2.0-or-later
* Added comprehensive readme.txt with external services documentation
* Added sanitize callbacks to all register_setting calls
* Code compliance review for WordPress.org guidelines

= 2.1.6 =
* SDK stale update fix

= 2.1.5 =
* Plugin Bridge one-click apply for meta, headings, alt text, and schema
* Bulk editor with post type filtering

= 2.1.4 =
* LLMs.txt generator
* Internal link suggestions
* Redirect manager

== Upgrade Notice ==

= 3.0.11 =
Bug fix + internal cleanup. Restores the McCrossenSEO™ meta box to its intended position below the content on the post editor (on some sites it had been rendering in the sidebar), and removes an unused background process that was logging debug warnings on `WP_DEBUG` installs. No action required — both run automatically.

= 3.0.0-alpha.1 =
Major release: plugin folder renamed from `mccrossen-seo` to `mccrossenseo`. Your settings, post meta, and history are preserved automatically. After activating, you can safely delete the old `mccrossen-seo` folder from your Plugins page.

== External Services ==

This plugin connects to external services depending on which features the site administrator enables. The plugin makes zero external connections by default. All listed services activate only after explicit administrator configuration or connection.

= McCrossen Marketing Platform =

**When:** Only when the site is connected via the "Connect to McCrossen" button under McCrossenSEO™ > Connect2McCrossen.
**What is sent:** Site URL, WordPress version, PHP version, plugin version, account identifier, and installed McCrossen plugin list. (The plugin also sends sanitized error reports rate-limited to one per error type per hour, even when not connected, to surface plugin-specific issues to the maintainer. This can be disabled by deactivating the plugin.)
**Why:** Authenticated delivery of platform-generated SEO recommendations, audit data, and (where the account permits) llms.txt content.
**Service URL:** https://mccrossenmarketing.com
**Privacy Policy:** https://mccrossenmarketing.com/privacy-policy/
**Terms of Service:** https://mccrossenmarketing.com/terms-of-service/

= Google Analytics 4 =

**When:** Only when an administrator configures a GA4 Measurement ID under McCrossenSEO™ → Tracking & Verification.
**What is loaded:** The `gtag.js` script from `https://www.googletagmanager.com/gtag/js`.
**Where:** Site frontend, in `<head>` for site visitors, only on installs that have configured a GA4 Measurement ID.
**Service:** Google Analytics — https://policies.google.com/privacy and https://www.google.com/analytics/terms/

= Google Tag Manager =

**When:** Only when an administrator configures a GTM Container ID under McCrossenSEO™ → Tracking & Verification.
**What is loaded:** The GTM script from `https://www.googletagmanager.com/gtm.js`, and a `<noscript>` iframe fallback to `https://www.googletagmanager.com/ns.html`.
**Where:** Site frontend, only on installs that have configured a GTM Container ID.
**Service:** Google Tag Manager — https://policies.google.com/privacy and https://www.google.com/analytics/terms/tag-manager/

= Meta (Facebook) Pixel =

**When:** Only when an administrator configures a Facebook Pixel ID under McCrossenSEO™ → Tracking & Verification.
**What is loaded:** The pixel script from `https://connect.facebook.net/en_US/fbevents.js` and a tracking image from `https://www.facebook.com/tr`.
**Where:** Site frontend, only on installs that have configured a Pixel ID.
**Service:** Meta Platforms — https://www.facebook.com/policy.php and https://www.facebook.com/legal/terms/businesstools
