=== Visibility - Native SEO / AEO / GEO ===
Contributors: fernandot, ayudawp
Tags: seo, schema, open graph, noindex, sitemap
Requires at least: 6.1
Tested up to: 7.0
Requires PHP: 7.4
Stable tag: 1.5.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Visible to search. Visible to AI. Lightweight SEO, AEO & GEO on WordPress core: meta tags, schema, Open Graph, robots and native sitemap.

== Description ==

**Visibility** brings together everything a WordPress site needs for technical SEO without installing a heavyweight all-in-one plugin. It is built around WordPress native features (`wp_robots`, `wp_sitemaps_*`, `register_post_meta`, the Block Editor sidebar) and stays out of your way: no custom database tables, no cron jobs, no third-party calls beyond a cached Gravatar lookup.

The clean metadata, Open Graph and JSON-LD structured data it generates don't only help classic search engines (SEO) — they are also what answer engines (AEO) and generative AI assistants and LLMs (GEO) read to understand, summarize and cite your content.

Three modules you toggle independently from the **Overview** tab:

* **Discover** — meta tags, Open Graph, Twitter Cards and JSON-LD schema.
* **Indexing** — robots directives (noindex / nofollow).
* **Sitemaps** — native XML sitemap control.

Visibility replaces a full SEO suite, it does not run alongside one: two SEO plugins fight over the `<head>` and hurt your ranking. If you need keyword research, redirect managers or content scoring, keep your current suite; if you want the essentials, fast and native, this is for you. When it detects Yoast SEO, Rank Math, All in One SEO, SEOPress, The SEO Framework or Slim SEO active, it warns you on the settings page so you run only one.

Switching from one of those, or from the legacy AyudaWP plugins (Native SEO Meta Tags, NoIndexer, Sitemap Customizer)? The **Overview** tab runs a one-click, non-destructive importer that brings your per-post, per-term and per-author values, plus each plugin's site-wide settings, into Visibility. See the migration FAQ below for the field-by-field detail.

The full feature list per module, the editor integration, the migration details and the recommended companion plugins are in the sections below.

== Discover: meta tags and social cards ==

* **Document title and meta description** on home, singulars, taxonomy archives and author archives, auto-generated from the content you already have (post title, excerpt, term description, user biographical info).
* **Open Graph** tags (`og:title`, `og:description`, `og:image`, `og:url`, `og:type`, `og:site_name`).
* **Twitter Card** tags (`summary_large_image` when an image is available, `summary` otherwise).
* **Article + BreadcrumbList JSON-LD** on posts and pages for Google rich results.
* **Person JSON-LD** on author archives with `sameAs` from user profile social URLs (E-E-A-T).
* **Smart image detection** for `og:image`: per-post override → featured image → WooCommerce gallery → first inline image in the content → fallback Open Graph image.
* **Customizable**: max length of auto-generated descriptions, home description override, fallback Open Graph image, site-wide Twitter `@username` and Facebook App ID, publisher logo for Article schema.
* **Title separator** — pick the character placed between the page title and the site name in the automatic document title, applied through the native `document_title_separator` filter. Per-post title overrides are used verbatim.
* **Schema @type per content type** — choose the JSON-LD type emitted on each content type's singulars, from the Article family (Article, BlogPosting, NewsArticle…) or the page types (WebPage, AboutPage, CollectionPage…), with a per-entry override. Limited to types that fill from the post content; rich types (Recipe, Product, Event…) are left to the plugins that own that data.
* **Per-post overrides** in the post sidebar: meta title (also replaces the document title), meta description, canonical URL, social title and social description (override og: / twitter: title and description), Open Graph image, and the schema @type for the entry.
* **Canonical URL override** per post or page: replaces `rel=canonical` and `og:url` for that entry, for syndicated or duplicate content whose ranking signals should point elsewhere.
* **Per-term overrides** on the term edit screen: meta title and meta description for category, tag and custom taxonomy archives, with the term name and term description as fallbacks.
* **Author social URLs**: fields added to the user profile (Twitter/X, LinkedIn, GitHub, Mastodon, Instagram, YouTube) used as `sameAs` in Person schema.

== Indexing: robots directives (noindex / nofollow) ==

* **Bulk noindex / nofollow by post type** — Set entire post types (pages, posts, products, etc.) as noindex, nofollow or both with a single checkbox per directive.
* **Bulk noindex / nofollow by taxonomy** — Set entire taxonomy archives (categories, tags, product attributes, etc.) as noindex, nofollow or both. Works with all public taxonomies, including WooCommerce product categories, tags and attributes.
* **Smart exclusions** — When a post type or taxonomy is set to noindex or nofollow, exclude specific posts or terms that should keep the default behavior. Exclusions work independently per directive.
* **Individual noindex / nofollow** — Set noindex or nofollow on specific posts or taxonomy terms without affecting the entire content type or taxonomy.
* **Instant search** — Find content and terms quickly with a live search field in the settings page. Click to add, click to remove.
* **Link-level nofollow** — A nofollow checkbox in the Classic Editor link popup lets you mark individual links with `rel="nofollow"` without leaving the editor. The Block Editor already exposes a native nofollow toggle in its link popover.
* **Quick Edit** — Toggle noindex and nofollow directly from the post list table without opening the editor.
* **Bulk actions** — Set or remove noindex and nofollow on multiple posts at once from the post list.
* **Robots column** — Two color-coded icons in your content lists show the current index/follow state at a glance.
* **Search results noindex / nofollow** — Prevent internal search result pages from being indexed and/or having their links followed.
* **404 pages noindex / nofollow** — Keep 404 error pages out of search indexes and stop crawlers from following dead-end links.
* **Date archives noindex / nofollow** — Apply either or both directives to daily, monthly and yearly archive pages.
* **Paginated archives noindex / nofollow** — Apply either or both directives to `/page/2/`, `/page/3/` and beyond, including Query Loop block pagination on block themes.
* **Attachment pages noindex / nofollow** — Apply either or both directives to media attachment pages.
* **Author archives noindex / nofollow** — Apply either or both directives to author archive pages.
* **RSS feeds noindex / nofollow** — Granular control to noindex and/or nofollow RSS feeds by type: main feed, taxonomy feeds, author feeds, site comments feed and per-post comments feeds. Works via the `X-Robots-Tag` HTTP header since feeds are XML.
* **Remove the category base** — Optionally strip the `/category/` base (and WooCommerce's `/product-category/`) from category URLs, built on the core rewrite API, with automatic 301 redirects from the old URLs so nothing already indexed breaks. Off by default; deactivating the plugin restores the default URLs.
* **Attachment page redirects** — Optionally 301-redirect attachment pages to their parent post via the core `template_redirect` hook. Only the attachment page is redirected; the media file itself keeps working. Since WordPress 6.4 new sites disable attachment pages and no longer link to them, so this mainly helps older sites that still use them; on a modern site you can simply noindex them instead. Off by default.

**Priority logic** (applies independently to noindex and nofollow):

1. Individual directive always applies.
2. Post type / taxonomy bulk directive applies unless the post or term is specifically excluded.
3. Exclusions override the bulk setting for specific posts or terms.

== Sitemaps: native XML sitemap control ==

* **Exclude post types** from the native `wp-sitemap.xml`.
* **Exclude taxonomies** entirely from the sitemap.
* **User control** — exclude specific user roles, individual users, or disable the entire users sitemap for single-author sites.
* **Flexible content exclusions** — exclude posts and pages by ID or by slug patterns; exclude taxonomy terms by ID or by slug fragments.
* **Lastmod dates** — adds last-modification dates to every URL in the sitemap and to every entry in the sitemap index (Google's most used optional field).
* **Smart redirects** — automatically redirect old sitemap URLs left over by other SEO and sitemap plugins (Yoast, Rank Math, AIOSEO, SEOPress, Google XML Sitemaps, Jetpack and others) to the native WordPress sitemap (301), so you don't lose crawl signals when switching.
* **Static sitemap detector** — spots leftover `sitemap.xml`, `sitemap_index.xml`, `news-sitemap.xml` and similar files in your WordPress root that silently override the native sitemap, and helps you remove them.
* **Performance** — customize the maximum number of URLs per sitemap (1–50,000).
* **Noindex-aware** — content marked as noindex by the Indexing module is automatically excluded from the sitemap, so search engines won't even find it there.

== Editor integration ==

= Block Editor =

A single sidebar panel in the Block Editor exposes everything you need per post:

* Meta title (also replaces the document title).
* Meta description.
* Canonical URL.
* Social title and social description (override og: / twitter: title and description).
* Open Graph image (with media picker and live preview).
* Schema @type for the entry.
* `noindex` toggle.
* `nofollow` toggle.

The panel is implemented as a `PluginDocumentSettingPanel` — fully compatible with WordPress collaborative editing.

= Classic Editor =

When the Block Editor is disabled for a post type, all the per-post controls are exposed as a single Classic Editor meta box with the same fields. Quick Edit and Bulk Actions on the post list provide the noindex/nofollow toggles without opening the editor.

== Perfect companions ==

Visibility is fully independent — these plugins aren't required — but they pair really well with it:

* [VigIA](https://wordpress.org/plugins/vigia/) — AI visibility, crawler analytics, Site Identity JSON-LD on the home page. Visibility coordinates `@id` references with VigIA so both can add schema without duplicating the Organization or WebSite nodes.
* [AI Share & Summarize](https://wordpress.org/plugins/ai-share-summarize/) — share buttons for social networks and AI assistants. Helps your content reach both audiences and language models.
* [AI Content Signals](https://wordpress.org/plugins/ai-content-signals/) — control how AI systems can use your content (training, search, both) through `robots.txt` directives endorsed by Cloudflare.
* [Vigilante](https://wordpress.org/plugins/vigilante/) — all-in-one WordPress security: firewall, login protection, security headers, 2FA, file integrity monitoring.

== Installation ==

1. Upload the `native-aeo-pack` folder to `/wp-content/plugins/` or install through the Plugins screen.
2. Activate the plugin.
3. Open **Visibility** in the admin sidebar to review the active modules and tune the defaults.
4. Done. Meta tags, robots directives and sitemap filters are applied automatically based on your settings.

== External services ==

This plugin connects to **Gravatar** (operated by Automattic Inc.) to check whether the post or page author has a public Gravatar avatar, so it can be exposed in Open Graph tags (`og:image`) and in Person JSON-LD (`image`) for E-E-A-T.

What data is sent and when:

* When a singular view is rendered and the Open Graph module is active, and no other image source exists (per-post `og:image`, featured image, WooCommerce gallery image, inline content image), the plugin sends an MD5 hash of the lowercased and trimmed author email to `https://www.gravatar.com/avatar/{hash}?d=404` via `wp_remote_head()` (HEAD request, no body).
* When an author archive is rendered and the Person schema module is active, the same probe is performed for that author.
* The original email address is **never** sent — only an MD5 hash, which is the standard Gravatar lookup mechanism.
* The result (found / not found) is cached for 24 hours in a transient so subsequent page loads do not hit Gravatar again.

This is the same mechanism WordPress itself uses to display Gravatar avatars in comments. Gravatar's privacy policy: https://automattic.com/privacy/

== Performance and security ==

"Lightweight" should be a measurement, not a slogan. These are the real numbers for this release, refreshed on every update.

= Footprint (1.5.0) =

* **Download size:** about 125 KB zipped.
* **Codebase:** 31 PHP files, roughly 10,700 lines of PHP.
* **Zero custom database tables.** Settings live in a single autoloaded option; per-post, per-term and per-author values use native WordPress meta that is already in the object cache.
* **Zero cron jobs** and no scheduled background processes.
* **Zero external HTTP calls**, with a single exception: a cached Gravatar HEAD probe (24-hour TTL) that exposes the author avatar for E-E-A-T, the same lookup WordPress already performs for comment avatars.
* **Built on WordPress core APIs** (`wp_robots`, `wp_sitemaps_*`, `register_post_meta`, `get_canonical_url`, the Block Editor), not private reimplementations, so most of the work is the platform's and already optimized.

= Security =

* **Every release passes a security audit** of the changed code (sanitization, escaping, nonces, capabilities, SQL) before it ships. It is a release gate, not an afterthought.
* **Minimal attack surface by design:** no custom tables, no unauthenticated endpoints, output escaped where it is emitted, input validated against allowlists, and prepared statements for every database query.
* **No premium tier, no telemetry, no data collection.** Nothing phones home.

A smaller surface is a smaller target.

== Frequently Asked Questions ==

= Does Visibility replace Yoast / Rank Math / AIOSEO? =

For most sites, yes. Visibility covers what 90% of sites actually need: titles, meta descriptions, canonical URLs, Open Graph, schema, robots directives and sitemap control — and it imports your per-post data from those plugins in one click. It does not include keyword analysis, redirect managers or content scoring. If you don't need those, Visibility is a much lighter alternative. If you do, stick with your current SEO plugin — Visibility isn't meant to run alongside.

= How do I migrate from Yoast SEO, Rank Math or All in One SEO? =

Install and activate Visibility. If data from any of those plugins exists in your database (the suite can be active or already deactivated), the **Overview** tab shows an import card with a per-field breakdown: titles, meta descriptions and robots overrides on posts and on taxonomy terms, plus canonical URLs, Open Graph images, social titles and descriptions, the default social image and the per-content-type schema type. One click copies it all into Visibility without touching the source plugin, skipping anything you already set here and any value built from the suite's template variables (`%%title%%`, `%title%`, `#post_title`) — Visibility generates those from your real content. From its free version, All in One SEO stores fewer fields (no Open Graph images and no per-term SEO, which are AIOSEO Pro features); Yoast and Rank Math import the full list, taxonomy terms included. The per-field breakdown always shows exactly what was found before you import, and the import is non-destructive and idempotent: your existing Visibility values win and re-running never duplicates. Review a few entries, then deactivate the suite. Old sitemap URLs (`sitemap.xml`, `post-sitemap.xml`…) keep working through a 301 redirect to the native `/wp-sitemap.xml`.

= I used Native SEO Meta Tags, NoIndexer or Sitemap Customizer. How do I migrate? =

Install and activate Visibility. If it finds data from any of those plugins, the **Overview** tab shows an import card with a per-plugin breakdown and an "Import now" button. The import is non-destructive (it copies, never deletes) and your existing Visibility values win on any conflict, so you can run it safely. Once you've imported and checked everything, deactivate the old plugins.

= Are there any database tables? =

No. Visibility stores its settings in a single WordPress option and uses native post, term and user meta tables for per-content overrides. Deleting the plugin removes the option; per-content overrides remain so reinstalling does not lose your work.

= Can I disable individual modules? =

Yes. The **Overview** tab lets you toggle Discover (meta tags), Indexing (robots) and Sitemaps independently. You can run only what you need.

= Does it work with custom post types and custom taxonomies? =

Yes. All public post types and taxonomies are supported — including WooCommerce product categories, tags and attributes (the latter requires "Enable Archives?" in the WooCommerce attribute settings).

= How do I set noindex on a single post? =

In the Block Editor, open the **Visibility** panel in the post sidebar and tick the noindex toggle. In the Classic Editor, the same toggle is in the Visibility meta box. You can also use Quick Edit or Bulk Actions from the post list.

= Where does Visibility store per-post overrides? =

In standard WordPress post meta (`_native_aeo_pack_*` keys). Term overrides go to term meta, user social URLs to user meta. Nothing leaves the standard WordPress tables.

= How can other plugins or themes detect Visibility's robots state? =

Public helper class methods are available for third-party integrations. Documentation will be expanded as the public API stabilizes.

== Screenshots ==

1. Overview tab — toggle the Discover, Indexing and Sitemaps modules, and run the one-click importers (from Yoast SEO, Rank Math and All in One SEO, or from the previous AyudaWP SEO plugins) with a per-plugin breakdown.
2. Discover tab — meta title, meta description, Open Graph, Twitter Card and JSON-LD schema, with site-wide defaults and per-post overrides.
3. Indexing tab — bulk noindex / nofollow by post type and taxonomy, plus live search to add individual posts or terms, and rules for search, 404, archives, pagination and feeds.
4. Sitemaps tab — exclude post types, taxonomies, user roles and specific posts or terms (by ID or slug), with lastmod control and max URLs per sitemap.
5. Per-post controls — the Visibility panel in the Block Editor and the Robots column with Quick Edit in the post list.

== Changelog ==

= 1.5.0 =
* New: Schema @type selector per content type, with a per-entry override. Pick the JSON-LD type emitted on each post type's singulars from the Article family (Article, BlogPosting, NewsArticle, TechArticle, ScholarlyArticle, Report) or the page types (WebPage, AboutPage, ContactPage, CollectionPage, ProfilePage, ItemPage). Each entry can override its type from its own editor. Rich types that need data the content does not provide (Recipe, Product, Event...) are intentionally left to the plugins that own them.
* New: Per-entry social title and social description, in the Block Editor panel and the Classic Editor meta box. They override og:title / twitter:title and og:description / twitter:description for that entry, for when the shared text should differ from the meta title and description.
* Improved: The one-click importer from Yoast SEO, Rank Math and All in One SEO now also brings over per-entry social titles and descriptions, the per-content-type schema @type (when it matches a supported type), and the default social image. The import card shows the new Social and Schema columns in its per-plugin breakdown.
* Improved: Admin UI consistency — monospace placeholders across the meta boxes and settings fields, and the Block Editor panel labels and headings now follow the same styling as the Classic Editor meta box.
* Fix: The Article, WebPage, BreadcrumbList and Person JSON-LD was printed with HTML-encoded quotes, so structured-data validators (including Google's Rich Results Test) rejected it as malformed and read no schema at all. It is now emitted as a valid inline JSON-LD script and parses correctly. The schema image also resolves through the same chain as og:image (per-post override, featured image, then site fallback) instead of only the featured image.
* Fix: The Open Graph image button in the Classic Editor meta box did nothing when clicked, because the WordPress media library was not loaded on post edit screens. It now opens the media picker as expected; the Block Editor was unaffected.

For older changelog entries, please check the [changelog.txt](https://plugins.svn.wordpress.org/native-aeo-pack/trunk/changelog.txt) file.

== Upgrade Notice ==

= 1.5.0 =
Choose the JSON-LD schema type per content type (with a per-post override) and set per-entry social title and description. The importer now also brings these and the default social image over from Yoast, Rank Math and AIOSEO.

== Support ==

Need private support or custom development?

Do you need one-on-one help, priority troubleshooting, or a custom feature, integration, or tweak built specifically for your site? I offer private support and custom development. Just [contact me](mailto:native-aeo-pack@ayudawp.com) and tell me what you need.

Need help or have suggestions?

* [Official website](https://visibility.quest)
* [WordPress support forum](https://wordpress.org/support/plugin/native-aeo-pack/)
* [YouTube channel](https://www.youtube.com/AyudaWordPressES)
* [Documentation and tutorials](https://ayudawp.com)

Love the plugin? Please leave us a 5-star review and help spread the word!

== About AyudaWP ==

We are specialists in WordPress security, SEO, AI and performance optimization plugins. We create tools that solve real problems for WordPress site owners while maintaining the highest coding standards and accessibility requirements.
