=== RankGrep Connector ===
Contributors: rankgrep
Tags: seo, meta description, seo title, rankgrep
Requires at least: 6.0
Tested up to: 6.8
Requires PHP: 7.4
Stable tag: 0.9.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Gives RankGrep real, REST-writable SEO title, meta description, H1 and opening-paragraph fields on this site, and renders them.

== Description ==

WordPress core has no SEO title or meta description field of its own. `post_title` is the page's *name*, and themes routinely decorate it when building the rendered `<title>` — appending a brand suffix, applying a per-template pattern, and so on. That makes `post_title` unusable for setting an exact title: whatever you write, the theme may render something else.

This plugin gives RankGrep two dedicated fields it can read and write over the WordPress REST API, and renders them verbatim:

* **SEO title** — output through `pre_get_document_title` at a late priority, so an explicitly set title wins over a theme's own construction instead of being decorated by it.
* **Meta description** — output in `<head>` on singular views.

Pages you haven't set a value for are left completely untouched, so installing this never changes existing titles or descriptions on its own.

Every change still goes through RankGrep's own review and approval flow — this plugin only provides the fields, it never edits anything by itself.

= Editing the fields yourself =

Every field appears in a **RankGrep SEO** panel on the page or post editor, so you can read exactly what your site is serving and change it by hand, whether RankGrep wrote it or you did. Character counters show the lengths search engines actually use.

Clearing a field removes the override entirely and hands the page back to your theme — it never leaves an empty title or description behind.

= Optional: H1 and opening-paragraph repairs =

Headings are different. WordPress renders the `<title>` through a filter any plugin can win, but the H1 and the opening paragraph are printed by your theme's template, and there's no core hook to override them. When your heading is typed into the editor, RankGrep edits it in the page content directly and no setup is needed. When your theme prints the heading itself, RankGrep can only write it if the theme asks for it.

Add this to your theme's `functions.php`:

`add_theme_support( 'rankgrep-headings' );`

Then, wherever the template prints its heading and opening paragraph, pass the existing markup as the fallback:

`<h1><?php echo rankgrep_h1( 'Electrician <span>Irene</span>' ); ?></h1>`

`<p><?php echo rankgrep_intro( esc_html( $default_intro ) ); ?></p>`

The fallback is returned untouched whenever RankGrep hasn't set a value, so the page renders exactly as it does today until a repair is actually approved and applied. A stored value is plain text and is escaped for you, so the result is always safe to echo. `rankgrep_h1_text()` and `rankgrep_intro_text()` return the raw stored value (or an empty string) for templates that need to branch on whether an override exists.

Wrap the calls in a small theme helper so the site still renders if this plugin is ever deactivated — an undefined function would otherwise be a fatal error on every page:

`function my_theme_h1( $fallback_html ) { return function_exists( 'rankgrep_h1' ) ? rankgrep_h1( $fallback_html ) : $fallback_html; }`

Themes that don't opt in are unaffected: RankGrep sees that the capability is missing and keeps proposing heading changes as guidance to apply by hand, rather than writing something that would never reach the page.

== Installation ==

1. Deactivate any other SEO plugin first. A site should not run two plugins writing competing meta tags.
2. Upload the plugin zip through **Plugins → Add Plugin → Upload Plugin**, then activate it.
3. In RankGrep, open **Connections** and connect this website. RankGrep detects the plugin automatically.

== Frequently Asked Questions ==

= Does this change my titles as soon as I activate it? =

No. It only renders a title or description for pages where a value has actually been set. Everything else keeps rendering exactly as it does now.

= Can I run this alongside Yoast or Rank Math? =

No. Two SEO plugins writing meta tags on the same page conflict with each other. The plugin shows an admin warning if it detects one, but never deactivates anything for you.

= Where do I see what RankGrep changed? =

Open the page in WordPress and look at the **RankGrep SEO** panel below the editor. It shows the exact SEO title, description, heading and opening paragraph currently set for that page, and you can edit or clear any of them.

== Changelog ==

= 0.9.0 =
* The blog index (Settings → Reading → "Posts page") now renders its SEO title and meta description. It is a real page, but WordPress reports it as non-singular, so values written there were saved and never shown — which made a repair on /blog/ apply, fail its own live check, and roll itself back.
* Themes can now call rankgrep_renders_description() to decide whether to print their own description tag, instead of listing the contexts themselves.

= 0.8.1 =
* Update checks now refresh hourly instead of every six hours, so a newly published version shows up without waiting most of a day.

= 0.8.0 =
* Category and tag archives now have their own SEO title and meta description fields, so blog archive pages — which WordPress gives no description at all — can be fixed like any other page.

= 0.7.1 =
* The status endpoint now reports whether an Application Password actually reached WordPress, and where in the chain it was lost — presence only, never a credential.

= 0.7.0 =
* Application Passwords now work on hosts running CGI, FastCGI, PHP-FPM or LiteSpeed, where PHP does not hand WordPress the Authorization header and every REST request arrives anonymous — the cause of a "rejected password" that no amount of re-issuing could fix.

= 0.6.2 =
* Heading fields are now only read where the page genuinely is a post: on a category or author archive the lookup could previously match an unrelated post and print its heading.

= 0.6.1 =
* Fixed update checking, which never actually offered an update: the version was sent under the wrong key, so WordPress discarded every response. This is the last version that has to be installed by uploading a zip.

= 0.6.0 =
* Added a RankGrep SEO panel to the page and post editor, so the title, description, heading and opening paragraph are visible and editable in WordPress instead of only through RankGrep.

= 0.5.1 =
* "Check again" on the Updates screen now really re-checks: a forced check no longer gets answered from this plugin's own cached copy of the update manifest.

= 0.5.0 =
* Added REST-writable H1 and opening-paragraph fields, plus `rankgrep_h1()` / `rankgrep_intro()` template helpers, so heading repairs can apply on themes that print their own headings.
* The status endpoint now reports whether the active theme opts in, so RankGrep only offers an automatic heading fix when one would actually reach the page.

= 0.4.0 =
* Added self-hosted update checking, so future versions can be installed from the Plugins screen instead of by uploading a zip.
* Added the plugin and author links, and the View details listing.

= 0.3.0 =
* Added a dedicated SEO title field, rendered verbatim so themes cannot decorate what RankGrep writes.

= 0.2.0 =
* Restructured into one class per concern for maintainability.

= 0.1.0 =
* First release: REST-writable meta description field, plugin detection endpoint, and conflicting-plugin warning.
