{"id":368297,"date":"2026-09-21T08:17:47","date_gmt":"2026-09-21T08:17:47","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/seovision-publisher\/"},"modified":"2026-09-21T10:38:08","modified_gmt":"2026-09-21T10:38:08","slug":"seovision-publisher","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/seovision-publisher\/","author":23565453,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"0.8.2","stable_tag":"0.8.2","tested":"7.1.2","requires":"6.4","requires_php":"7.4","requires_plugins":null,"header_name":"SeoVision Publisher","header_author":"SeoVision","header_description":"Receives SeoVision article metadata through the WordPress REST API and injects article JSON-LD into the page head.","assets_banners_color":"322d2b","last_updated":"2026-09-21 10:38:08","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"https:\/\/seovision.io\/","rating":0,"author_block_rating":0,"active_installs":0,"downloads":65,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"0.8.0":{"tag":"0.8.0","author":"seovisionio","date":"2026-09-21 08:17:16","revision":3705134},"0.8.1":{"tag":"0.8.1","author":"seovisionio","date":"2026-09-21 08:17:30","revision":3705136},"0.8.2":{"tag":"0.8.2","author":"seovisionio","date":"2026-09-21 10:38:08","revision":3705395}},"upgrade_notice":[],"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3705133,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3705133,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3705133,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3705133,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["0.8.0","0.8.1","0.8.2"],"block_files":[],"assets_screenshots":[],"screenshots":[]},"plugin_section":[],"plugin_tags":[529,2516,7143,186,1121],"plugin_category":[55],"plugin_contributors":[281795],"plugin_business_model":[],"class_list":["post-368297","plugin","type-plugin","status-publish","hentry","plugin_tags-content","plugin_tags-json-ld","plugin_tags-publishing","plugin_tags-seo","plugin_tags-structured-data","plugin_category-seo-and-marketing","plugin_contributors-seovisionio","plugin_committers-seovisionio"],"banners":{"banner":"https:\/\/ps.w.org\/seovision-publisher\/assets\/banner-772x250.png?rev=3705133","banner_2x":"https:\/\/ps.w.org\/seovision-publisher\/assets\/banner-1544x500.png?rev=3705133","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/seovision-publisher\/assets\/icon-128x128.png?rev=3705133","icon_2x":"https:\/\/ps.w.org\/seovision-publisher\/assets\/icon-256x256.png?rev=3705133","generated":false},"screenshots":[],"raw_content":"<!--section=description-->\n<p>SeoVision Publisher connects the SeoVision app to WordPress for controlled article draft creation, scheduled publishing, and live publishing.<\/p>\n\n<p>This plugin is intended to work with the SeoVision service. The service prepares article content, SEO metadata, structured data, and image references, then sends them to WordPress through authenticated REST requests.<\/p>\n\n<p>The plugin currently:<\/p>\n\n<ul>\n<li>Registers REST-visible post meta fields for article identifiers, SEO metadata, JSON-LD, and media maps.<\/li>\n<li>Registers an authenticated REST endpoint for idempotent draft, scheduled, and publish flows.<\/li>\n<li>Stores article JSON-LD as post meta and injects it into the page head on single post pages, pointed at the post's real permalink.<\/li>\n<li>Prints the article's SEO title, meta description and Open Graph \/ Twitter card tags when no SEO plugin is active. With Yoast SEO or Rank Math active, it fills that plugin's SEO title, meta description and focus keyword fields instead, and with another SEO plugin active it prints nothing, so a page never carries two descriptions.<\/li>\n<li>Downloads approved article images into the WordPress Media Library and stores attachment IDs in a media map.<\/li>\n<li>Rewrites article HTML and JSON-LD image URLs to WordPress Media Library URLs.<\/li>\n<li>Attributes each post to an existing WordPress user you choose in SeoVision.<\/li>\n<\/ul>\n\n<p>Every request is authorized by the integration token you paste on the plugin's settings page, and by nothing else. SeoVision can keep articles as drafts, schedule them for the requested WordPress site date\/time, or publish them immediately.<\/p>\n\n<p><strong>Post authors:<\/strong> the plugin never creates users. You choose the WordPress user that SeoVision's posts should be attributed to, in SeoVision under Settings \u2192 Integrations \u2192 WordPress, and the plugin verifies that user exists before using it. If no user is chosen, the plugin looks for a user whose login matches the article's SeoVision author key (so sites set up with an older version keep their existing authors), and otherwise attributes the post to the site administrator and tells SeoVision to ask you to pick an author.<\/p>\n\n<h3>External services<\/h3>\n\n<p>This plugin is the WordPress connector for the SeoVision content platform (https:\/\/seovision.io\/). It does not work standalone \u2014 it exists to receive articles prepared in your SeoVision account.<\/p>\n\n<p>What the plugin receives: SeoVision sends authenticated REST requests (Bearer token you paste on the plugin's settings page) containing the article HTML, SEO metadata, JSON-LD structured data, image URLs, publishing status, and author info.<\/p>\n\n<p>What the plugin sends out: after each publish request the plugin downloads the article images from the URLs supplied in that request (hosted by SeoVision's media storage) into your WordPress Media Library, so your posts use local media instead of hotlinking. Its REST responses back to SeoVision contain the resulting post ID, permalink, status, media map, and author resolution. The <code>\/verify<\/code> endpoint reports plugin presence\/version for the dashboard's \"Test connection\" button, and the <code>\/users<\/code> endpoint lists the display names and roles of users who can edit posts, for the \"Post author\" dropdown in SeoVision. No other data leaves your site, and nothing is sent anywhere except in direct response to these authenticated requests.<\/p>\n\n<p>SeoVision terms of service: https:\/\/seovision.io\/terms\nSeoVision privacy policy: https:\/\/seovision.io\/privacy<\/p>\n\n<!--section=installation-->\n<ol>\n<li>Upload the plugin folder to <code>\/wp-content\/plugins\/seovision-publisher\/<\/code>.<\/li>\n<li>Activate the plugin through the WordPress Plugins screen.<\/li>\n<li>In SeoVision, open Settings \u2192 Integrations \u2192 WordPress and generate an integration token.<\/li>\n<li>Paste that token into Settings \u2192 SeoVision Publisher in WordPress and click Save.<\/li>\n<li>Back in SeoVision, choose the post author and click Test connection.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20this%20publish%20posts%20live%3F\"><h3>Does this publish posts live?<\/h3><\/dt>\n<dd><p>Only when SeoVision sends publish mode. The plugin supports draft, scheduled, and immediate publish requests through the authenticated REST endpoint. Already-published posts stay published if a repeated schedule request is sent.<\/p><\/dd>\n<dt id=\"does%20this%20work%20with%20yoast%20seo%20or%20rank%20math%3F\"><h3>Does this work with Yoast SEO or Rank Math?<\/h3><\/dt>\n<dd><p>Yes. When Yoast SEO or Rank Math is active, each SeoVision article's SEO title, meta description and focus keyword are written into that plugin's own fields, and that plugin prints the tags. Without an SEO plugin, SeoVision Publisher prints them itself. With All in One SEO, SEOPress or The SEO Framework active it stays out of the way and prints nothing. Developers can switch the output off with the <code>seovision_print_seo_tags<\/code> and <code>seovision_print_open_graph<\/code> filters.<\/p><\/dd>\n<dt id=\"does%20this%20track%20visitors%3F\"><h3>Does this track visitors?<\/h3><\/dt>\n<dd><p>No. The plugin does not add analytics, tracking pixels, or visitor profiling.<\/p><\/dd>\n<dt id=\"does%20this%20call%20third-party%20services%20from%20wordpress%3F\"><h3>Does this call third-party services from WordPress?<\/h3><\/dt>\n<dd><p>Yes, but only when an authenticated publish request is sent. The plugin downloads image URLs provided by SeoVision into the WordPress Media Library so the post can use local WordPress media assets. See the \"External services\" section above for the full data flow.<\/p><\/dd>\n<dt id=\"does%20the%20plugin%20create%20wordpress%20users%3F\"><h3>Does the plugin create WordPress users?<\/h3><\/dt>\n<dd><p>No. Version 0.8.0 removed user creation entirely. Posts are attributed to an existing user you select in SeoVision, and the plugin verifies that user exists before using it. If you want a dedicated byline, create that user yourself in Users \u2192 Add New and then select it in SeoVision.<\/p><\/dd>\n<dt id=\"test%20connection%20says%20the%20token%20is%20invalid%2C%20but%20i%20pasted%20it%20correctly\"><h3>Test connection says the token is invalid, but I pasted it correctly<\/h3><\/dt>\n<dd><p>Some hosts (Apache running PHP as CGI\/FastCGI) drop the Authorization header before PHP sees it, so the plugin receives no token at all. Version 0.8.0 recovers the header automatically on most such hosts. If it still fails, add this line to the <code>.htaccess<\/code> in your site root and test again:<\/p>\n\n<pre><code>SetEnvIf Authorization \"(.*)\" HTTP_AUTHORIZATION=$1\n<\/code><\/pre>\n\n<p>Also check that you did not paste a new token on top of an old one \u2014 the token field is intentionally shown empty, and leaving it empty keeps the token already saved.<\/p><\/dd>\n<dt id=\"do%20i%20need%20a%20seovision%20account%3F\"><h3>Do I need a SeoVision account?<\/h3><\/dt>\n<dd><p>Yes. The plugin is the WordPress connector for the SeoVision publishing workflow.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>0.8.2<\/h4>\n\n<ul>\n<li>A picture whose address is a web page (a link the writer formatted as an image) no longer fails the whole publish. The plugin refuses it before download, skips any image it cannot add to the Media Library, keeps the post as written, and reports the skipped addresses under <code>warnings<\/code> in the response.<\/li>\n<li>The article's SeoVision id is stored the moment the post is created, before its images are processed, so a publish that fails in the media step is found by its id on the next attempt instead of being rewritten by slug every hour.<\/li>\n<\/ul>\n\n<h4>0.8.1<\/h4>\n\n<ul>\n<li>SEO tags: the article's SEO title replaces the post title in <code>&lt;title&gt;<\/code>, and the meta description, Open Graph and Twitter card tags are printed on SeoVision articles when no SEO plugin is active. Jetpack's own Open Graph output is respected.<\/li>\n<li>Yoast SEO and Rank Math: the SEO title, meta description and focus keyword are written into their fields on every publish, so they print the tags.<\/li>\n<li>Stores the article's focus keyword (<code>_seovision_focus_keyword<\/code>).<\/li>\n<li>The JSON-LD now uses the post's real permalink instead of the address SeoVision assumed before publishing (<code>\/blog\/&lt;slug&gt;<\/code>), and the \"Blog\" breadcrumb points at the site's posts page, or is left out when posts are shown on the front page. An address set on purpose in SeoVision is kept.<\/li>\n<\/ul>\n\n<h4>0.8.0<\/h4>\n\n<ul>\n<li>Removed WordPress user creation. Posts are now attributed to an existing user selected in SeoVision; the plugin never calls <code>wp_insert_user<\/code>.<\/li>\n<li>Authorization is now the integration token only. The previous fallback that also accepted any logged-in user with <code>edit_posts<\/code> is gone \u2014 it let a Contributor publish immediately and set an arbitrary post author, bypassing their normal capabilities.<\/li>\n<li>Article CSS is delivered through <code>wp_enqueue_style()<\/code> + <code>wp_add_inline_style()<\/code> instead of a <code>&lt;style&gt;<\/code> tag echoed into <code>wp_head<\/code>.<\/li>\n<li>Hosts that move the Authorization header to <code>REDIRECT_HTTP_AUTHORIZATION<\/code> (Apache with PHP as CGI\/FastCGI) now work without an <code>.htaccess<\/code> change. Where the header is still missing, the plugin answers with a distinct error so SeoVision can name the real cause instead of blaming the token.<\/li>\n<li>The settings page no longer prints the saved token into the input. It shows the last characters instead, and an empty field on save keeps the existing token \u2014 pasting a new token on top of the old one used to concatenate them, after which no request could ever authenticate.<\/li>\n<\/ul>\n\n<h4>0.7.7<\/h4>\n\n<ul>\n<li>Removed the <code>Plugin URI<\/code> header. It pointed at seovision.io, the same address as <code>Author URI<\/code>, which the WordPress.org submission form rejects; seovision.io describes the author, not this specific plugin.<\/li>\n<\/ul>\n\n<h4>0.7.6<\/h4>\n\n<ul>\n<li>Fixes the Plugin Check findings from the 0.7.5 review run: prefixed the two global variables in uninstall.php, and annotated the intentional single-row meta lookup that makes publishing idempotent (it never runs on page views).<\/li>\n<li>Brand name consistently written \"SeoVision\" across the plugin UI, readme, and headers. All technical identifiers (text domain, option names, REST namespace, meta prefix) are unchanged, so updates and stored data are unaffected.<\/li>\n<\/ul>\n\n<h4>0.7.5<\/h4>\n\n<ul>\n<li>WordPress.org submission readiness. Tested up to WordPress 7.1.<\/li>\n<li>Hardened the <code>\/users<\/code> endpoint: it now lists only users who can edit posts and no longer returns login names (usernames), closing a username-enumeration aid. The SeoVision dashboard only needs display names for its \"Post author\" dropdown.<\/li>\n<li>Replaced <code>@unlink()<\/code> with <code>wp_delete_file()<\/code> in the image sideloader and sanitized the raw <code>Authorization<\/code> header reads.<\/li>\n<li><code>error_log()<\/code> diagnostics now only run when <code>WP_DEBUG<\/code> is enabled; the same warning still reaches SeoVision in the REST response.<\/li>\n<li>readme: added an \"External services\" disclosure section (data in\/out, terms, privacy policy) and documented exactly when the plugin creates an Author-role WordPress user.<\/li>\n<\/ul>\n\n<h4>0.7.4<\/h4>\n\n<ul>\n<li>Adds uninstall cleanup: deleting the plugin now removes the stored integration token (<code>seovision_integration_token<\/code>) and the activation-redirect flag. Previously these options survived a delete, so reinstalling on the same site showed \"A token is configured\" with the old token pre-filled. Deactivating (without deleting) still keeps the token.<\/li>\n<\/ul>\n\n<h4>0.6.1<\/h4>\n\n<ul>\n<li>Security\/correctness: the JSON-LD printed into the page head now escapes <code>&lt;<\/code>, <code>&gt;<\/code>, and <code>&amp;<\/code> (JSON_HEX_TAG | JSON_HEX_AMP). Previously a value containing the literal <code>&lt;\/script&gt;<\/code> could close the schema script tag early and break the page or inject markup. Slashes stay unescaped so embedded URLs remain readable.<\/li>\n<\/ul>\n\n<h4>0.6.0<\/h4>\n\n<ul>\n<li>Draft, scheduled, and immediate publish modes with media sideloading and JSON-LD head injection (see prior internal releases for details).<\/li>\n<\/ul>\n\n<h4>0.5.2<\/h4>\n\n<ul>\n<li>Keeps already-published posts published when repeated schedule requests are sent.<\/li>\n<li>Prevents accidental unpublishing of indexed WordPress posts during bulk schedule operations.<\/li>\n<li>Restores the safer default after 0.5.1: schedule requests never unpublish existing live posts.<\/li>\n<\/ul>\n\n<h4>0.5.1<\/h4>\n\n<ul>\n<li>Allows a published post to be moved back to scheduled status when the requested schedule date is still in the future.<\/li>\n<li>Keeps already-published posts live when repeated schedule requests refer to dates that have already passed.<\/li>\n<\/ul>\n\n<h4>0.5.0<\/h4>\n\n<ul>\n<li>Added support for draft, scheduled, and immediate publish modes.<\/li>\n<li>Scheduled posts use <code>post.status<\/code> <code>future<\/code> with the supplied WordPress local <code>post.date<\/code>.<\/li>\n<li>Idempotent lookup now includes published posts to avoid duplicate articles on repeat requests.<\/li>\n<\/ul>\n\n<h4>0.4.0<\/h4>\n\n<ul>\n<li>Moved article image sideloading into the plugin endpoint.<\/li>\n<li>Added media URL rewriting for article HTML and JSON-LD before draft meta is stored.<\/li>\n<li>The plugin endpoint now returns the generated media map and featured media ID.<\/li>\n<\/ul>\n\n<h4>0.3.1<\/h4>\n\n<ul>\n<li>Centralized plugin contract naming constants for pre-release rename readiness.<\/li>\n<li>Added WordPress.org-ready license metadata and readme scaffold.<\/li>\n<\/ul>\n\n<h4>0.3.0<\/h4>\n\n<ul>\n<li>Added authenticated plugin REST endpoint for idempotent article draft create\/update.<\/li>\n<li>Added media map meta storage.<\/li>\n<\/ul>","raw_excerpt":"Publishes approved SeoVision articles with their SEO title, meta description, Open Graph tags, structured data and local images.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/368297","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=368297"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/seovisionio"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=368297"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=368297"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=368297"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=368297"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=368297"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=368297"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}