{"id":369442,"date":"2026-09-24T17:32:48","date_gmt":"2026-09-24T17:32:48","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/seo-presence\/"},"modified":"2026-09-24T17:36:32","modified_gmt":"2026-09-24T17:36:32","slug":"seogeox","status":"publish","type":"plugin","link":"https:\/\/te.wordpress.org\/plugins\/seogeox\/","author":18379578,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.0.5","stable_tag":"1.0.5","tested":"7.1.2","requires":"6.0","requires_php":"7.4","requires_plugins":null,"header_name":"SEOGeoX","header_author":"Softnika","header_description":"SEO and GEO optimization toolkit for WordPress \u2014 meta titles\/descriptions, Open Graph & Twitter cards, JSON-LD schema (including Local Business geo data), XML sitemaps, and a transparent on-page analyzer. Built to add near-zero overhead to your site.","assets_banners_color":"","last_updated":"2026-09-24 17:36:32","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"https:\/\/softnika.com\/seogeox\/","header_author_uri":"https:\/\/softnika.com\/","rating":5,"author_block_rating":0,"active_installs":0,"downloads":54,"num_ratings":1,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"1.0.5":{"tag":"1.0.5","author":"softnikasolutions","date":"2026-09-24 17:36:32","revision":3711788}},"upgrade_notice":[],"ratings":{"1":0,"2":0,"3":0,"4":0,"5":1},"assets_icons":[],"assets_banners":[],"assets_blueprints":{},"all_blocks":[],"tagged_versions":["1.0.5"],"block_files":[],"assets_screenshots":[],"screenshots":{"1":"The SEOGeoX meta box on a post-edit screen: SEO title\/\ndescription, focus keyphrases, and the live on-page analyzer.","2":"The Google\/Facebook\/Twitter search &amp; social preview inside the meta box.","3":"The top-level \"SEOGeoX\" admin menu and its General Settings screen.","4":"The Search Console screen: overview, Keyword Ranking, and Top Queries\nwith \"Opportunity\" badges.","5":"The SEO Bulk Editor: inline title\/description editing across every\npost of a type in one table.","6":"The 404 Monitor with its one-click \"Add Redirect\" action."}},"plugin_section":[],"plugin_tags":[726,1117,127115,186,1557],"plugin_category":[55],"plugin_contributors":[282542],"plugin_business_model":[],"class_list":["post-369442","plugin","type-plugin","status-publish","hentry","plugin_tags-redirects","plugin_tags-schema","plugin_tags-search-console","plugin_tags-seo","plugin_tags-sitemap","plugin_category-seo-and-marketing","plugin_contributors-softnikasolutions","plugin_committers-softnikasolutions"],"banners":[],"icons":{"svg":false,"icon":"https:\/\/s.w.org\/plugins\/geopattern-icon\/seogeox.svg","icon_2x":false,"generated":true},"screenshots":[],"raw_content":"<!--section=description-->\n<p>SEOGeoX is a lean, fast SEO plugin built around one idea: do the\nsame core job as Yoast\/RankMath, with less admin overhead and a fully\ntransparent scoring model \u2014 no opaque single-number score, no bundled JS\nadmin framework, and no assets loaded outside the one screen that needs\nthem. The \"Geo\" in the name is the built-in Local SEO toolkit further\ndown this list (address\/geo-coordinate fields, LocalBusiness schema, and\na multi-location \"SEO Locations\" post type) rather than a separate\nadd-on \u2014 everywhere else, \"geo\" isn't used as a stand-in for features\nthat don't exist yet. This is a new plugin looking for real-world\ntesters before it grows further; see \"Has this been tested on a live\nsite?\" in the FAQ below for exactly where your testing would help most.\nIt currently includes:<\/p>\n\n<ul>\n<li>A single per-post meta box: SEO title, meta description, up to 5 focus\nkeyphrases (the first treated as primary), a live Google\/Facebook\/\nTwitter-X preview, optional noindex, optional canonical override,\noptional cornerstone-content flag, and an optional per-post schema type\noverride.<\/li>\n<li>Frontend <code>&lt;title&gt;<\/code> override, meta description, canonical link, Open\nGraph tags, and Twitter Card tags \u2014 rendered from data already loaded on\nthe page request, with no extra database queries.<\/li>\n<li>A social preview + snippet editor right in the meta box: live, no-\nnetwork-request previews of how a page will look in Google search\nresults, a Facebook\/X link share, and a Twitter\/X card, with optional\ntitle\/description\/image overrides for the social cards (via the media\nlibrary) that never touch the SEO title or meta description search\nengines see. Twitter\/X reuses the Facebook card's content unless you\nopt into a separate title\/description\/image for it.<\/li>\n<li>JSON-LD structured data: an Organization\/WebSite graph sitewide, plus\nArticle and BreadcrumbList nodes on singular content. When WooCommerce\nis active, product pages automatically get a Product node instead \u2014\nwith Offer (or AggregateOffer for variable products), stock\navailability, SKU, AggregateRating, and up to 10 individual Review\nnodes (most recent first) when the product has reviews with a star\nrating. A per-post Schema Type Override (in the meta box's advanced\nfields) can replace the automatic Article\/Product node with an Event,\nCourse, Podcast Episode, Dataset, or FAQ Page node instead, each with\nits own small set of type-specific fields. A GTIN\/MPN\/brand-override\nsection appears on WooCommerce product edit screens, feeding straight\ninto the Product node for better Google Merchant Center \/ Shopping\neligibility. When Organization type (SEOGeoX &gt; General Settings) is set to\na business type (LocalBusiness, Store, Restaurant, etc.), the sitewide\nOrganization node also carries address, geo coordinates, phone, price\nrange, and opening hours from the new \"Local Business Info\" section\nthere. For multi-location businesses, a new \"SEO Locations\" post type\n(nested under the SEOGeoX menu) gives each branch its own page,\nits own LocalBusiness\nnode (same address\/hours\/geo fields, per location), and \u2014 since it's a\npublic post type \u2014 its own entry in the XML sitemap automatically.<\/li>\n<li>A cached XML sitemap at <code>\/sitemap.xml<\/code> (index) and\n  \/sitemap-{post_type}.xml, rebuilt only when content changes \u2014 not on\nevery visit. When WPML or Polylang is active, each per-type sitemap\ncovers every active language in one file, with every translation\ncross-linked via <code>hreflang<\/code> alternate tags \u2014 built against both\nplugins' public APIs, but not yet exercised against a live multilingual\ninstall. Please verify the sitemap output on a real WPML\/Polylang site\nbefore relying on it, and let us know what you find. Each URL entry can\nalso carry <code>&lt;image:image&gt;<\/code> tags for its featured image and any images\nfound in the post content (toggle in SEOGeoX &gt; General Settings) \u2014 pulled\nwith a lightweight regex scan, not a DOM parser, so it doesn't add a\nhard dependency on an extension not every host has enabled. Two more\nspecialized sitemaps are available, both off by default: a Google News\nsitemap (<code>\/sitemap-news.xml<\/code>, only for sites approved as a Google News\npublisher \u2014 lists content published in the last 48 hours from\nwhichever post types you choose) and a video sitemap\n(<code>\/sitemap-videos.xml<\/code>, auto-detects self-hosted <code>&lt;video&gt;<\/code> tags and\nYouTube\/Vimeo embeds already in your post content \u2014 nothing extra to\nfill in).<\/li>\n<li>A client-side, in-browser on-page analyzer with per-check pass\/warn\/fail\nreasoning (title length, description length, sentence length, passive\nvoice, paragraph length) instead of one opaque numeric score, scored\nseparately against every focus keyphrase you've added \u2014 not just one \u2014\nso each gets its own title\/description\/content-presence and density\nchecks. No external requests.<\/li>\n<li>One lean settings screen built on WordPress's core Settings API \u2014 no\nbundled JS admin framework.<\/li>\n<li>A redirect manager (SEOGeoX &gt; SEO Redirects): add 301\/302 redirects,\nsee hit counts and last-hit time per redirect, and get one created\n<em>automatically<\/em> whenever a published post or page's URL slug changes \u2014\nso old links don't silently start 404ing. Matching is a single indexed\ndatabase lookup per request, not a cached map that has to be rebuilt\nand unserialized on every page load. Alongside exact-path redirects,\nwildcard patterns are supported too \u2014 e.g. old path <code>\/blog\/*<\/code> to target\n  \/new-blog\/$1 \u2014 checked only as a small cached fallback when the exact\nlookup misses, so sites without any wildcard redirects pay nothing extra.<\/li>\n<li>A bulk editor (SEOGeoX &gt; SEO Bulk Editor): filter by post type, see a\npaginated table of posts with inline SEO title\/description fields and\nlive character counters, and save every row in one submit \u2014 instead of\nopening each post individually. Focus keyword, noindex, and canonical\noverrides already set on a post are left untouched by a bulk save. The\nsame screen flags duplicate SEO titles and duplicate meta descriptions\nacross every post of the filtered type \u2014 a summary list with edit\nlinks, plus a \"Duplicate\" badge on affected rows in the current page.<\/li>\n<li>A robots.txt editor (SEOGeoX &gt; Robots.txt), using WordPress's own\nvirtual-robots.txt filter \u2014 no file written to disk. Prefilled with\nWordPress's current effective default as a starting point, with a\n\"Sitemap:\" line added automatically when the XML sitemap is enabled.\nWarns when a physical robots.txt file on the server would override it,\nand when the site's \"discourage search engines\" setting is on (custom\ncontent would override WordPress's own blanket Disallow in that case).<\/li>\n<li>Redirect CSV export\/import, right on the Redirects screen: export\nevery redirect (including hit counts) as a CSV, or upload a CSV with\n\"source\"\/\"target\" columns (an optional \"type\" column, 301 default) to\nbulk-add redirects \u2014 handy for migrating off another redirect plugin.<\/li>\n<li>A Yoast SEO \/ Rank Math data migration screen (SEOGeoX &gt; SEO\nMigration): detects existing per-post titles, descriptions, focus\nkeywords, noindex flags, and canonical URLs from either plugin and\nimports them in one click. Only fills posts that don't already have\nSEOGeoX data \u2014 nothing already set here is overwritten. Common\ntemplate variables (%%title%%, %sitename%, etc.) are resolved; anything\nless common is left as literal text to clean up afterward in the Bulk\nEditor.<\/li>\n<li>A 404 monitor (SEOGeoX &gt; 404 Monitor): logs real front-end 404s\n(path, hit count, first\/last seen), ranked by frequency, with a\none-click \"Add Redirect\" button that opens the Redirects screen with\nthe path pre-filled. Once a redirect exists for a logged path, it stops\naccumulating new hits automatically \u2014 the redirect manager catches it\nfirst. Noisy paths (wp-admin, wp-content, favicon.ico, etc.) are\nexcluded, and the log self-prunes to the least-hit, longest-stale 5,000\nentries so bot scanning can't grow it without bound.<\/li>\n<li>A settings export\/import screen (SEOGeoX &gt; General Settings): export the\norganization\/logo\/Twitter\/sitemap\/schema settings and robots.txt content\nas a JSON file, or upload one to apply those same settings on another\nsite \u2014 handy for staging \u2192 production deployment. Imported data is run\nback through the same validation a manual save uses, not trusted as-is.<\/li>\n<li>Instant Indexing (SEOGeoX &gt; Instant Indexing): pings search engines the\nmoment a public post is published, updated, or unpublished, instead of\nwaiting for their next crawl. IndexNow (Bing, Yandex, Seznam, Naver, and\nothers) needs no account \u2014 just a generated key, verified by a plain-text\nfile this plugin serves automatically \u2014 and is off by default, so nothing\nis sent anywhere until you turn it on in SEOGeoX &gt; Instant Indexing. An\noptional Google Indexing API integration is also included, off by default\nand clearly flagged: Google officially restricts that API to JobPosting\/\nBroadcastEvent pages, so using it for ordinary content isn't sanctioned by\nGoogle even though it works in practice for many sites. Neither call ever\nblocks a post save \u2014 both run a few seconds later via WP-Cron, with the\nactual sent\/failed result shown on the settings screen.<\/li>\n<li>Internal linking (SEOGeoX &gt; Internal Linking, plus a new \"Cornerstone\ncontent\" checkbox in the meta box): an on-site internal-link graph is\nkept in its own small table and rebuilt whenever a post is saved. An\nOrphaned Content Finder lists cornerstone content with no incoming\ninternal links from anywhere else on the site; a Stale Cornerstone\nContent Finder lists cornerstone content untouched for a configurable\nnumber of months. While editing a post, a \"Link suggestions\" panel\nshows other published posts worth linking to (cornerstone pages\nsurfaced first), with a one-click \"Copy link\" button \u2014 the only network\nrequest this plugin's admin JS makes anywhere, and it never leaves the\nsite.<\/li>\n<li>A Role Manager (SEOGeoX &gt; Role Manager): control which WordPress roles\ncan see and use each of this plugin's settings screens, and which roles\ncan edit the \"advanced\" per-post fields (noindex, canonical override,\ncornerstone). Built on WordPress's own capabilities system, not a\nseparate permission layer. Defaults preserve exactly what already\nworked before this screen existed \u2014 screen access defaults to whichever\nroles already had <code>manage_options<\/code>, and advanced-field access defaults\nto whichever roles already had <code>edit_posts<\/code> \u2014 and any role that already\nhas full admin access is never editable from this screen, so there's no\nway to use it to lock yourself out.<\/li>\n<li>Google Search Console integration (SEOGeoX &gt; Search Console):\nconnects with a Google Cloud service account (the same auth approach\nthe Google Indexing API integration uses) and reads Search Console's\nown Search Analytics report \u2014 never by scraping live search results,\nwhich would violate Google's terms of service. One cached report\n(refreshed every 6 hours) powers a sitewide clicks\/impressions\/CTR\/\nposition overview, a Keyword Ranking table for up to 20 tracked\nkeywords\/phrases (their real average position, clicks, impressions, and\nCTR, straight from Search Console), and a Top Queries table that flags\nqueries already earning meaningful impressions but ranking past\nposition 10 as an \"Opportunity\" \u2014 a keyword suggestion backed by this\nsite's own real search demand, not a generic volume guess.<\/li>\n<li>A second, independent Google Keyword Suggestion tool sits right in the\nmeta box: a \"Get keyword ideas\" button next to focus keyphrases that\ncalls Google's own public autocomplete endpoint \u2014 the same suggestions\nthe Google search box itself shows as you type. Off by default and\nflagged the same way the Google Indexing API is (it's not a documented,\nsupported Google API); enable it in SEOGeoX &gt; General Settings.\nOnly the typed keyphrase is ever sent, and results are cached for 12\nhours per term.<\/li>\n<li>Automated Image SEO: fills in missing alt text automatically and for\nfree \u2014 from an image's own caption or title, or a cleaned-up version of\nits filename (WordPress's own resize suffix and camera-generated\nprefixes like \"IMG_\"\/\"DSC_\" stripped) \u2014 never by looking at the image\nitself, so it never claims to describe what's actually depicted. Runs\nonce, automatically, right when an image is uploaded (only ever filling\nan <em>empty<\/em> alt field), and a new Image SEO screen lists every image\nalready in the Media Library with missing alt text, with an editable\nsuggestion for each, fixed in bulk. The on-page analyzer also now flags\nimages in the post being edited that are missing alt text.<\/li>\n<\/ul>\n\n<p>Assets (JS\/CSS) load only on the specific admin screen that needs them \u2014\nnever sitewide across wp-admin.<\/p>\n\n<h3>Roadmap ideas (not yet built)<\/h3>\n\n<p>Every item originally on this list \u2014 including every purely in-house\nbatch toward RankMath Pro \/ Yoast Premium parity, and the Search\nConsole \/ keyword ranking \/ keyword suggestion integrations that used to\nbe queued here as \"bring your own API key\" ideas \u2014 has now been built.\nThe only thing still queued is:<\/p>\n\n<ul>\n<li>AI-assisted title\/content suggestions, using your own OpenAI\/\nAnthropic-style API key. This is the one item that genuinely can't be\ndone as plain code \u2014 everything else originally queued for a\n\"bring-your-own-API-key\" integration (keyword rank tracking, a Search\nConsole dashboard inside wp-admin) turned out to be buildable as a\nfully-authenticated Search Console integration instead, needing only a\nfree Google Cloud service account rather than a paid third-party key.\nAI-assisted content will always be off until you supply your own\ncredentials \u2014 this plugin will never bundle or resell access to\nsomeone else's API.<\/li>\n<\/ul>\n\n<!--section=installation-->\n<ol>\n<li>Zip the <code>seogeox<\/code> folder if it isn't already, or upload it as-is\nvia Plugins &gt; Add New &gt; Upload Plugin.<\/li>\n<li>Activate the plugin.<\/li>\n<li>Go to the new \"SEOGeoX\" menu in the main admin sidebar (its own\ntop-level menu, not nested under Settings) to set your organization\nname, logo, Twitter handle, and toggle the sitemap\/schema output.<\/li>\n<li>Edit any post or page \u2014 the SEOGeoX meta box appears below the\ncontent editor.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"has%20this%20been%20tested%20on%20a%20live%20wordpress%20site%3F\"><h3>Has this been tested on a live WordPress site?<\/h3><\/dt>\n<dd><p>Not extensively yet \u2014 this is a new plugin, and this release is\nspecifically meant to invite that testing rather than assume it. Areas\nwhere real-site testing matters most: the WooCommerce schema output, the\nWPML\/Polylang sitemap output, the Yoast\/Rank Math migration screen\nagainst real existing data, the 404 monitor under real traffic, the\nrobots.txt filter against your actual server config, the settings\nexport\/import round-trip, wildcard redirect matching, the image\nsitemap's <code>&lt;img&gt;<\/code> extraction, IndexNow's key-file verification, the\nGoogle Indexing API and Search Console integrations' service-account\nauthentication, the internal-link graph and its suggestions panel,\nmulti-keyphrase scoring and the Role Manager's capability defaults, the\nsocial preview\/snippet editor, the Event\/Course\/Podcast\/Dataset\/FAQ\nschema types, the Google News\/video sitemaps, the Local SEO \/ SEO\nLocations post type, the top-level admin menu's per-role visibility, the\nAutomated Image SEO auto-fill-on-upload behavior, and the Google Keyword\nSuggestion button. If you hit something that doesn't work as described,\nplease open a support topic \u2014 that report is exactly what this release\nis asking for.<\/p><\/dd>\n<dt id=\"does%20this%20plugin%20send%20data%20to%20any%20external%20service%3F\"><h3>Does this plugin send data to any external service?<\/h3><\/dt>\n<dd><p>Every integration below is off by default (none of them run until you turn\nthem on in the relevant settings screen), sends only the minimum data each\none needs to function, and is disclosed here in full:<\/p>\n\n<p><strong>IndexNow<\/strong> \u2014 Off by default. Once enabled in SEOGeoX &gt; Instant Indexing,\nit sends the URL of a public post to the IndexNow API the moment that post\nis published, updated, or unpublished, plus a plugin-generated key (not a\npersonal account credential) used to verify you control the site. Nothing\nelse about the post \u2014 its content, author, or any personal data \u2014 is\ntransmitted. This happens automatically in the background (via WP-Cron)\nevery time a matching post changes, for as long as the setting stays on.\nDestination: <code>https:\/\/api.indexnow.org\/indexnow<\/code>, run by Microsoft and\nYandex. Terms: https:\/\/www.indexnow.org\/terms \u2014 Documentation:\nhttps:\/\/www.indexnow.org\/documentation \u2014 Privacy\/FAQ:\nhttps:\/\/www.indexnow.org\/faq<\/p>\n\n<p><strong>Google Indexing API<\/strong> \u2014 Off by default and requires you to first create\nand paste in your own Google Cloud service account JSON key. Once\nconfigured, publishing, updating, or unpublishing a public post sends that\npost's URL to Google's Indexing API in the background (via WP-Cron), the\nsame way IndexNow does. A \"Test Connection\" button lets you verify the\ncredentials without submitting a real URL. Google officially restricts\nthis API to JobPosting\/BroadcastEvent pages; using it for ordinary content\nworks in practice but isn't a sanctioned use, which the settings screen\nflags. Destination: Google's <code>indexing.googleapis.com<\/code>. Docs:\nhttps:\/\/developers.google.com\/search\/apis\/indexing-api\/v3\/quickstart \u2014\nTerms: https:\/\/developers.google.com\/terms \u2014 Privacy:\nhttps:\/\/policies.google.com\/privacy<\/p>\n\n<p><strong>Google OAuth (service-account authentication)<\/strong> \u2014 Not a separate feature\nyou turn on; it's the shared authentication step behind both the Google\nIndexing API and Search Console integrations above; it runs only when one\nof those is enabled and configured. This plugin never asks you (or your\nsite's visitors) to sign in to a personal Google account \u2014 instead, it\nreads the <code>client_email<\/code> and private key from the service account JSON\nyou paste in yourself, signs a short-lived authentication token locally\nwith PHP's own OpenSSL extension, and exchanges that token directly with\nGoogle for an access token scoped to only the one API being used. No\npersonal Google account, password, or visitor data is ever involved.\nDestination: Google's <code>oauth2.googleapis.com\/token<\/code>. Policies:\nhttps:\/\/developers.google.com\/identity\/protocols\/oauth2\/policies and\nhttps:\/\/developers.google.com\/terms\/api-services-user-data-policy \u2014\nPrivacy: https:\/\/policies.google.com\/privacy<\/p>\n\n<p><strong>Google Autocomplete (Keyword Suggestion button)<\/strong> \u2014 Off by default;\nenable it in SEOGeoX &gt; General Settings. When you click \"Get keyword\nideas\" in the post-edit meta box, only the keyphrase you've typed at that\nmoment is sent \u2014 never post content, title, or any personal data \u2014 to\nfetch the same suggestions Google's own search box shows as you type.\nResults are cached for 12 hours per term so retyping an idea doesn't\nrepeat the request. Destination: <code>suggestqueries.google.com<\/code>, a public\nendpoint Google uses for its own autocomplete UI. It isn't part of any\ndocumented, officially supported Google API, so there's no dedicated\nTerms of Service page for it specifically; Google's general terms and\nprivacy policy apply: https:\/\/policies.google.com\/terms and\nhttps:\/\/policies.google.com\/privacy<\/p>\n\n<p><strong>Google Search Console<\/strong> \u2014 Off by default and, like the Indexing API,\nrequires your own Google Cloud service account first. Once configured,\nthis plugin only <em>reads<\/em> your site's existing Search Console Search\nAnalytics report (clicks, impressions, position, and query data Google\nalready collected) on a schedule (cached for 6 hours) \u2014 it never writes\nto, modifies, or submits anything to Search Console, and it never scrapes\nlive Google search results. Destination: Google's\n    searchconsole.googleapis.com \/ <code>www.googleapis.com\/webmasters<\/code>. Docs:\nhttps:\/\/developers.google.com\/webmaster-tools\/v1\/terms \u2014 Terms:\nhttps:\/\/developers.google.com\/terms \u2014 Privacy:\nhttps:\/\/policies.google.com\/privacy<\/p>\n\n<p>Automated Image SEO, the on-page analyzer, XML sitemaps, redirects, the\n404 monitor, and everything else this plugin does make no external\nrequest of any kind \u2014 alt text, for example, is generated from the\nimage's own filename\/caption\/title, entirely on your own server.<\/p><\/dd>\n<dt id=\"will%20this%20conflict%20with%20yoast%20seo%20or%20rank%20math%3F\"><h3>Will this conflict with Yoast SEO or Rank Math?<\/h3><\/dt>\n<dd><p>Running two SEO plugins at once usually means duplicate meta tags and\nduplicate JSON-LD schema, so deactivate the other one first. The SEO\nMigration screen (SEOGeoX &gt; SEO Migration) then copies over existing\ntitles, descriptions, focus keywords, noindex flags, and canonical URLs\nfrom whichever of the two you were using, in one click.<\/p><\/dd>\n<dt id=\"does%20this%20work%20with%20woocommerce%2C%20wpml%2C%20or%20polylang%3F\"><h3>Does this work with WooCommerce, WPML, or Polylang?<\/h3><\/dt>\n<dd><p>WooCommerce product pages automatically get Product schema (price,\nstock, SKU, ratings\/reviews) instead of the default Article node, plus a\nGTIN\/MPN\/brand fields section on the product edit screen \u2014 no\nconfiguration needed beyond having WooCommerce active. WPML and Polylang\nare supported in the XML sitemap (every active language in one sitemap\nper post type, cross-linked with <code>hreflang<\/code> alternates) if either plugin\nis active; see the note above about this not yet being tested against a\nreal multilingual install.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.0.5<\/h4>\n\n<p>Closes a gap in 1.0.3's IndexNow\/Google Indexing API enabled-state fix: that\nround guarded the <em>callers<\/em> (the automatic publish\/update ping and the\n\"Resubmit All\" button) but not the function that actually sends the HTTP\nrequest. That was enough for both normal paths, but not for a request\nalready queued on WordPress's cron before the integration got disabled \u2014\nsuch a request would reach <code>send()<\/code> directly, skipping both guarded\ncallers. Added the same <code>enabled<\/code> check inside <code>send()<\/code> itself in both\n    includes\/class-indexnow.php and <code>includes\/class-google-indexing.php<\/code>, so\nneither integration can reach its external API once disabled, regardless of\nwhich code path gets there.<\/p>\n\n<h4>1.0.4<\/h4>\n\n<p>Small follow-up to 1.0.3, closing the last two spots a text-based scanner\n(rather than a person) could still flag:<\/p>\n\n<ul>\n<li>The social preview card's image was rendered with a dynamically generated\n  style=\"background-image:url(...)\" attribute in <code>assets\/admin.js<\/code>.\nReplaced with a plain <code>&lt;img&gt;<\/code> element (same visual result, styled via\n  assets\/admin.css) run through the file's existing <code>escapeHtml()<\/code> helper.<\/li>\n<li>Removed the one remaining literal <code>onclick=\"...\"<\/code> example text from a code\ncomment in <code>assets\/confirm-submit.js<\/code> describing the earlier fix, for the\nsame reason literal examples were cleaned out of other comments in 1.0.3.<\/li>\n<\/ul>\n\n<h4>1.0.3<\/h4>\n\n<p>A second round of fixes on the same \"SEOGeoX\" review:<\/p>\n\n<ul>\n<li>Removed every remaining inline <code>style=\"\"<\/code> attribute from admin screens\n(field widths, table column widths, form spacing, and show\/hide states\nthat used to be set with <code>style=\"display:none;\"<\/code>). All of it now lives\nin <code>assets\/admin.css<\/code> as reusable classes, or is toggled via\n  classList in JavaScript instead of writing to <code>.style.display<\/code>\ndirectly.<\/li>\n<li>Removed three remaining inline click-confirm handlers (Clear All 404s,\nRegenerate IndexNow Key, Migrate Now) that were passed to WordPress's\nown <code>submit_button()<\/code> helper as an <code>onclick<\/code> attribute \u2014 still a real\ninline event handler even though it wasn't typed as literal HTML in the\ntemplate. Replaced with a plain <code>data-confirm<\/code> attribute and one small\nshared enqueued script (<code>assets\/confirm-submit.js<\/code>).<\/li>\n<li>Fixed the JSON-LD schema output's JSON encoding: it was passing\n  JSON_UNESCAPED_SLASHES to <code>wp_json_encode()<\/code>, which would let a\nliteral <code>&lt;\/<\/code> sequence through unescaped \u2014 meaning post content\ncontaining <code>&lt;\/script&gt;<\/code> could break out of the structured-data <code>&lt;script&gt;<\/code>\ntag it's embedded in. Removed that flag (PHP's default behavior escapes\n  \/ as <code>\\\/<\/code>, which prevents this) and added an explicit <code>&lt;\/<\/code> \u2192 <code>&lt;\\\/<\/code>\nreplacement as defense in depth.<\/li>\n<li>IndexNow's \"Resubmit All Published URLs\" button (and, for the same\nreason, the Google Indexing API's equivalent button) now checks that\nthe integration is actually enabled before doing anything \u2014 previously\na disabled integration's automatic pinging correctly stayed off, but\nthe manual resubmit button could still reach the API even while the\ntoggle was off.<\/li>\n<li>Removed a couple of literal <code>&lt;script&gt;<\/code>\/<code>onclick=<\/code> examples from code\ncomments (describing fixes from the previous round) that a text-based\nscanner can't distinguish from the real thing still being present.<\/li>\n<li>Trimmed the readme's changelog: the full multi-generation rename history\n(three product names, two different company names, and internal\nclass-prefix details like <code>RCSEO_<\/code>\/<code>SEOP_<\/code>) has been collapsed into\nshort, brand-neutral notes. That level of internal development history\ndoesn't belong in a public-facing readme, and specifically shouldn't\nreference a company name unrelated to this submission.<\/li>\n<li>A stray code comment left over from an earlier rename still said\n\"SEO Presence\" instead of \"SEOGeoX\" \u2014 fixed.<\/li>\n<\/ul>\n\n<h4>1.0.2<\/h4>\n\n<p>Addresses the WordPress.org review feedback on the \"SEOGeoX\" submission:<\/p>\n\n<ul>\n<li>IndexNow (Instant Indexing) is now off by default, matching every other\nintegration in the plugin that calls an external service: Google\nIndexing API, Search Console, and Keyword Suggestions were already off\nuntil you supplied credentials or flipped a setting, but IndexNow\npreviously started pinging Bing\/Yandex\/Seznam\/Naver on every publish\/\nupdate\/unpublish the moment the plugin was activated, with no\nconfiguration required first. That's now consistent \u2014 turn it on in\nSEOGeoX &gt; Instant Indexing when you're ready to use it. No other\nbehavior changed; if you already turned it on deliberately, this update\ndoes not turn it back off for you (the setting is only re-defaulted for\ninstalls that never touched it).<\/li>\n<li>The \"Does this plugin send data to any external service?\" FAQ entry now\ndocuments every integration individually \u2014 IndexNow, the Google Indexing\nAPI, the shared Google service-account authentication step, the Google\nAutocomplete-based Keyword Suggestion button, and Google Search Console \u2014\nwith exactly what each one sends, when, where it goes, and links to its\nTerms of Service and (where one exists specifically for it) Privacy\nPolicy.<\/li>\n<li>Removed the one inline <code>&lt;script&gt;<\/code> tag and the one inline <code>onclick=\"\"<\/code>\nattribute this plugin printed directly into an admin page (both on the\nSEO Redirects screen); that behavior now lives in a properly enqueued\n  assets\/redirects.js, loaded only on that one screen. The plugin's other\nuse of an inline <code>&lt;script type=\"application\/ld+json\"&gt;<\/code> block (front-end\nstructured data) is intentionally unchanged \u2014 it's a per-page JSON data\npayload, not executable logic, and can't be moved into a static enqueued\nfile; this is the same approach every major SEO plugin uses for schema\nmarkup.<\/li>\n<li>Fixed the <code>Contributors:<\/code> line below to the correct WordPress.org\nusername.<\/li>\n<\/ul>\n\n<h4>1.0.0 - 1.0.1<\/h4>\n\n<p>Internal development milestones before this plugin's first public\nsubmission, including settling on its final name and branding. No\nend-user-facing features changed in either step.<\/p>\n\n<h4>0.1.0 - 0.22.0<\/h4>\n\n<p>Pre-1.0 development releases that built out the feature set described\nabove, one batch at a time, before the first public submission.<\/p>","raw_excerpt":"A lean, fast SEO and local\/geo toolkit: meta tags, schema, sitemaps, redirects, Search Console integration, and a transparent on-page analyzer.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/te.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/369442","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/te.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/te.wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/te.wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=369442"}],"author":[{"embeddable":true,"href":"https:\/\/te.wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/softnikasolutions"}],"wp:attachment":[{"href":"https:\/\/te.wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=369442"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/te.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=369442"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/te.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=369442"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/te.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=369442"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/te.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=369442"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/te.wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=369442"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}