Description
Open Growth Preferred Source Button for Google integrates Google’s documented Preferred Sources JavaScript button into WordPress.
Independent plugin, not affiliated with or endorsed by Google.
Features:
* Uses Google’s documented publisher.js integration and declarative button attributes.
* Optional custom-control mode using Google’s documented manual/programmatic integration.
* Custom controls can use a unique CSS selector or the data-ogpsb-preferred-source attribute, including native Bootstrap 5.3 / LiveCanvas markup.
* Automatic placement before, after, or both before and after singular content.
* Selectable public, publicly queryable post types.
* Per-content visibility control in the block editor sidebar.
* Light/dark theme, supported language override, translated language names in the admin UI, and direction-aware alignment.
* Shortcode: [open_growth_preferred_source].
* Dynamic Gutenberg block: open-growth/preferred-source-button.
* Early asset enqueue on pages where a button is expected, with configurable reserved height to reduce layout shift. Set the reserved height to 0 to disable it. The async Google script and layout CSS are loaded early when a placement is predictable; late manual placements receive a footer fallback so they still work and remain styled.
* Protected posts, feeds, embeds, AMP requests, and explicitly hidden content do not render the button.
* No remote calls from PHP.
* Read-only on-demand diagnostics in Settings to explain whether a specific post is configured for automatic or manual display, without contacting Google or changing settings.
Google only supports domain-level and subdomain-level publications in its Preferred Sources tool. Eligibility is controlled by Google, not by this plugin.
Shortcode
[open_growth_preferred_source]
Example:
[open_growth_preferred_source theme="dark" lang="es" alignment="center"]
Allowed values:
* theme: light, dark (case-insensitive).
* lang: a supported Google language code. An explicit lang="" forces automatic browser language.
* alignment: start, center, end. left and right are accepted as aliases.
Invalid values fall back to the corresponding plugin setting.
External services
This plugin loads Google’s Preferred Sources JavaScript library from https://news.google.com/swg/js/v1/publisher.js. In standard mode it is loaded on front-end pages where a button is expected to appear. In Custom control mode it is loaded on normal front-end pages because the trigger may live in a theme template, header, builder partial, popup, or dynamically inserted markup that WordPress cannot reliably predict server-side. The browser therefore makes a request to Google when that script loads. Google may receive standard request information such as the visitor’s IP address, browser/user-agent data, referrer information, and other data handled by Google’s service.
The library is required to render and operate Google’s interactive Preferred Source button. No request to Google is made by this plugin’s PHP code.
Service documentation: https://developers.google.com/search/docs/appearance/preferred-sources
Google Terms: https://policies.google.com/terms
Google Privacy Policy: https://policies.google.com/privacy
Blocks
This plugin provides 1 block.
- Preferred Source Button for Google Displays the Preferred Source button for Google.
Installation
- Upload the plugin ZIP in Plugins > Add New > Upload Plugin.
- Activate it.
- Go to Settings > Preferred Source Button.
- Configure placement, content types, theme, language, and alignment.
For a custom site control, choose Custom control. Add data-ogpsb-preferred-source to an existing HTML element or configure a unique CSS selector. In custom-control mode the plugin does not render Google’s standard button.
This release uses its own option and metadata names and does not migrate settings from earlier differently named builds.
FAQ
-
Is this plugin affiliated with Google?
-
No. Independent plugin, not affiliated with or endorsed by Google.
-
Does installing the plugin make my site eligible for Preferred Sources?
-
No. Check that your site appears in Google’s Preferred Sources tool; this plugin cannot make a site eligible.
Preferred Sources tool: https://www.google.com/preferences/source
Google documentation: https://developers.google.com/search/docs/appearance/preferred-sources
-
Are WordPress sites installed in subdirectories supported by Google?
-
Google documents eligibility at the domain and subdomain level only. A URL such as
example.com/blog/is not independently eligible. The settings screen shows a warning whenhome_url()contains a subdirectory path. -
The editor intentionally displays a stable placeholder. Google’s external library is not loaded into the editor merely to provide a preview. The live button renders on the front end.
-
How does automatic placement work with page builders?
-
The plugin uses WordPress
the_contentby default and includes dedicated frontend-output integration for Elementor (elementor/frontend/the_content) and Bricks (bricks/frontend/render_data, content area only). This covers builder layouts that bypass the normal WordPress content pass while preserving the same eligibility, duplicate, password, and per-content visibility checks.Oxygen does not apply WordPress
the_contentto Oxygen-designed singular content by default. Oxygen provides its own setting to enable that filter. If you keep that setting disabled, place the plugin shortcode where you want the button. For LiveCanvas and other builders, automatic placement works when the builder ultimately runsthe_content; otherwise use the shortcode in the builder output. The plugin does not modify another builder’s settings.Dynamic content inserted after Google’s library has initialized is still subject to Google’s standard declarative-mode limitation described below.
-
Can I disable the reserved minimum height?
-
Yes. Set Reserved minimum height to
0in Settings > Preferred Source Button. Theogpsb_button_min_heightfilter can still override the stored value. -
What happens when uninstalling on a very large multisite network?
-
Uninstall removes the plugin option and
_ogpsb_hidepost meta from every site in batches of 100. This avoids loading the complete site list into memory, but WordPress uninstall runs synchronously. On exceptionally large networks, host execution-time limits can interrupt cleanup before every site has been processed. If that applies to your network, run uninstall from a maintenance window or remove the two plugin data keys with a network-aware administrative process. -
What about content injected after the page has loaded?
-
Google’s standard implementation scans the DOM when its library initializes, so declarative buttons inserted later may not be rendered automatically. The plugin’s Custom control mode uses Google’s documented manual/programmatic API plus delegated click handling, so matching custom triggers added later can work without a DOM re-scan.
-
How do custom controls work with LiveCanvas, Picostrap, or Bootstrap 5.3?
-
Use ordinary HTML and keep your existing Bootstrap classes. For example:
<button type="button" class="btn btn-primary" data-ogpsb-preferred-source>Add to preferred sources</button>. The plugin uses native JavaScript and its owndata-ogpsb-preferred-sourceattribute; it does not depend on jQuery or Bootstrap JavaScript and does not use Bootstrap’sdata-bs-*namespace. -
Why does Custom control mode load its scripts on every normal frontend page?
-
Custom triggers can live outside post content—in a header, footer, template, popup, builder partial, or markup inserted later by JavaScript—so the plugin cannot reliably predict server-side which URLs contain a trigger. Custom control mode therefore loads Google’s library and the small trigger controller on normal frontend pages by design. If site-wide loading is not appropriate for your site, use the standard implementation or conditionally dequeue the integration in site-specific code.
-
Does the per-content visibility setting affect Custom Fields support?
-
No. The editor uses a dedicated REST field backed by the plugin’s private metadata key, so the plugin does not enable WordPress Custom Fields support or expose the Custom Fields UI merely to persist this setting.
-
What happens on AMP pages?
-
The plugin suppresses the button and third-party script on requests detected through the official AMP plugin’s
amp_is_request()function. -
What if my site uses Content-Security-Policy (CSP)?
-
Google’s documented implementation loads
https://news.google.com/swg/js/v1/publisher.js. Sites with a restrictive CSP must allow the required Google origin in theirscript-srcpolicy. Google’s documented standard snippet does not include a Subresource Integrity (integrity) attribute, so this plugin does not invent one for a remotely mutable script. -
Which filters are available?
-
ogpsb_should_display—(bool $should_display, WP_Post $post).ogpsb_button_html—(string $html, array $args).ogpsb_script_attributes— associative array of extra script attributes.ogpsb_button_min_height— reserved wrapper height in pixels; its input value is the saved setting (40by default,0disables reservation).ogpsb_languages— supported language code/name map. Use only codes accepted by Google.ogpsb_enable_elementor_integration— boolean; returnfalseto bypass the dedicated Elementor automatic-placement integration.ogpsb_enable_bricks_integration— boolean; returnfalseto bypass the dedicated Bricks automatic-placement integration.
-
Yes. That setting deliberately renders one button before and one after the content. Use a single-placement option if you want only one instance.
Reviews
There are no reviews for this plugin.
Contributors & Developers
“Open Growth Preferred Source Button for Google” is open source software. The following people have contributed to this plugin.
ContributorsTranslate “Open Growth Preferred Source Button for Google” into your language.
Interested in development?
Browse the code, check out the SVN repository, or subscribe to the development log by RSS.
Changelog
1.1.5
- Load admin settings JavaScript through WordPress enqueue APIs instead of an inline script tag.
- Sanitize final filterable button markup with an explicit HTML allowlist.
- Remove bundled translations intended for WordPress.org translation infrastructure.
1.1.4
- Fixed Custom control clicks that occur before Google’s async client is ready: configured triggers now suppress their native action immediately and queue one invocation until initialization completes.
- Added an explicit late-style fallback for manual buttons first discovered after
wp_head, so alignment and reserved-height CSS are not silently omitted. - Added early archive/posts-page scanning for stored shortcode, dynamic block, or declarative Google button markup, preserving early CSS/script loading when those manual placements are predictable.
- Restored per-content visibility for shortcode/block placements rendered inside non-singular loops, without treating archive term/user IDs as post IDs.
- Aligned early asset prediction with Diagnostics by recognizing stored
google-add-preferred-source-btnmarkup as a supported manual placement. - Revalidated the minimum supported WordPress version as 6.5.
wp_is_rest_endpoint()was introduced in WordPress 6.5; the 7.0 minimum stated in the 1.0.4/1.0.5 historical notes was unnecessarily restrictive. - Made the unexplained 1.1.2 changelog gap explicit without retroactively inventing release changes.
- Bumped plugin, block metadata, and stable-tag versions consistently to 1.1.4.
- Documented the intentional site-wide frontend asset loading used by Custom control mode for global and dynamically inserted triggers.
- Added filters to disable the dedicated Elementor or Bricks automatic-placement integrations without adding persistent settings.
- Reordered the historical changelog so versions remain in descending order.
- Added browser-native CSS selector syntax validation on the settings screen so invalid Custom control selectors cannot be saved unnoticed.
1.1.3
- Corrected diagnostics for password-protected content so unlocked content is not reported as permanently suppressed, and made explicit that
ogpsb_should_displayis not executed from the admin diagnostic because callbacks may depend on frontend context. - Hardened custom-selector storage and inline JavaScript encoding while preserving legitimate percent-encoded CSS selector sequences.
- Replaced REST-backed
metaediting with a dedicated boolean REST field, so unchecking deletes_ogpsb_hide, the sidebar updates only its own field, and the plugin no longer enables the Custom Fields UI. - Prevented automatic/manual duplicates regardless of whether a shortcode/block or automatic placement renders first.
- Removed the redundant shortcode
lang=""branch without changing automatic-language behavior. - Allowed explicit shortcode and dynamic-block rendering on non-singular front-end views such as archives and the posts page; automatic placement remains singular-only.
- Cleaned packaged PO/POT translator headers.
1.1.2
- Historical note: the 1.1.3 source package contained no 1.1.2 changelog entry. No changes are retroactively attributed to this version.
1.1.1
- Fixed Elementor automatic placement to use the document currently rendered by Elementor, preventing Theme Builder headers, footers, or popups from consuming the singular-content insertion.
- Fixed Custom control settings UI so the Content types row is hidden with the other standard-only placement controls.
- Registered the bundled
/languagesdirectory oninitso packaged translations load reliably in non-WordPress.org installations. - Hardened the language maintenance tool against web execution and allowed
--checkto validate legacy generated lists that predate a recorded source hash. - Restored release packaging hygiene by excluding repository-only
.distignoreandphpcs.xml.dist.
1.1.0
- Added Google’s documented manual/programmatic Preferred Sources integration as an opt-in Custom control mode.
- Added an optional custom CSS trigger selector and the builder-friendly
data-ogpsb-preferred-sourcetrigger attribute. - Added a small native-JavaScript delegated controller so triggers in LiveCanvas, templates, headers, popups, and dynamically inserted markup can invoke the flow without jQuery or Bootstrap coupling.
- Added the required
preferred-sources-control="manual"attribute only in Custom control mode; the existing standard mode remains unchanged. - Updated Gutenberg editor messaging and settings UI so standard-only controls are not presented as applicable in Custom control mode.
- Added custom-mode diagnostics without executing builders or contacting Google.
1.0.9
- Added a read-only diagnostics section in Settings for checking one post/page/CPT by ID.
- Diagnostics report public visibility, password protection, per-content hiding, automatic-display configuration, selected content type, stored manual placement, expected Google script loading, and custom visibility-filter presence.
- The diagnostic runs only on demand, makes no request to Google, changes no settings, and clearly distinguishes configuration checks from actual theme/page-builder execution.
1.0.8
- Optimized duplicate-button detection with a cheap string pre-check before invoking
WP_HTML_Tag_Processor. - Added per-request caching for candidate HTML that contains the marker string, avoiding repeated structural scans when builders or filters process the same rendered content more than once.
- Structural validation is still performed before treating the marker as a real button, so plain-text mentions do not suppress automatic placement.
1.0.7
- Added a deterministic release tool for refreshing and verifying the supported-language whitelist from Google’s official CSV.
- The release tool validates the CSV schema and language tags, rejects duplicates and malformed/empty responses, records the source SHA-256, writes atomically, and supports a non-mutating
--checkmode for CI/release gates. - The official CSV remains a build/release dependency only; WordPress runtime performs no language-list network requests.
1.0.6
- Added dedicated automatic-placement integration for Elementor frontend output and Bricks content-area rendering while retaining the standard
the_contentpath. - Documented Oxygen and LiveCanvas/page-builder behavior and the recommended shortcode fallback when a builder bypasses
the_content. - Added a configurable reserved minimum-height setting from 0 to 500 px;
0disables layout reservation. - Localized the language display names through the plugin text domain while keeping canonical Google language codes unchanged.
- Added repository-quality
phpcs.xml.distand.distignorefiles to the distribution package as requested. - Documented synchronous uninstall limitations for exceptionally large multisite networks; cleanup remains batched at 100 sites per query.
1.0.5
- Resolved manual-render post context from the singular queried object so stale global post data from secondary loops cannot affect visibility decisions.
- Registered the Classic Editor fallback per actual post editor using
use_block_editor_for_post(), including sites where the Classic Editor plugin disables the block editor for an otherwise block-capable post type. - Replaced plain-text duplicate detection with HTML-aware detection of the actual
google-add-preferred-source-btnattribute, preventing tutorial text from suppressing automatic placement. - Added the block metadata
versionfield for explicit block asset/version metadata. - Documented that REST-backed per-content visibility may require enabling WordPress Custom Fields support on selected post types.
- At the time, kept WordPress 7.0 as the minimum supported core version and removed an unnecessary compatibility guard around
WP_HTML_Tag_Processor; the minimum-version rationale was later corrected to WordPress 6.5 in 1.1.4.
1.0.4
- Raised the minimum supported WordPress version to 7.0 and removed compatibility branches for older core versions. This minimum was later corrected to WordPress 6.5 in 1.1.4 after revalidating the core API requirements.
- Split the block editor UI from the post visibility sidebar so
wp-editoris loaded only on supported post-editing screens, not in the Site Editor or Widgets editor. - Declared the block editor script in
block.jsonand kept the block on API version 3. - Switched REST-context detection to WordPress core
wp_is_rest_endpoint(). The function was subsequently revalidated as available since WordPress 6.5, which is why the minimum was corrected in 1.1.4. - Removed the server-side WP Consent API check because page caching can make per-visitor consent decisions unsafe at render time; consent integration remains intentionally outside this plugin’s scope.
- Replaced per-request plugin-header parsing with the
OGPSB_VERSIONconstant. - Removed the non-URI
Update URI: falseheader; WordPress.org-hosted plugins should not declare a third-party update URI. - Added a separate JavaScript translation catalog for the post visibility sidebar.
- Enqueued the tiny layout-reservation style early on eligible singular requests, including manual placements that may come from template parts or builders.
- Cached per-post visibility decisions within a request and improved the block editor inherited-language label.
- Release ZIP excludes repository-only
.distignoreandphpcs.xml.distfiles. - Kept
Contributors: jaimepaezv.
1.0.3
- Changed WordPress.org contributor placeholder to
jaimepaezv. - Fixed late-registered custom post type settings and delayed visibility-meta registration until init priority 99.
- Updated the editor sidebar to the current
wp.editorSlotFill and reactiveuseSelect/useDispatchdata hooks. - Ensured selected post types support REST-backed custom fields so the visibility toggle persists.
- Added Classic Editor/page-builder fallback visibility control without forcing a classic meta box into the block editor.
- Suppressed manual rendering in REST, archive, preview, and Customizer contexts.
- Added packaged JavaScript translations for the editor and included block metadata strings in translation catalogs.
- Added an
ogpsb_languagesfilter while retaining the embedded-list verification warning until the official CSV can be compared. - Replaced the abbreviated license file with the complete GPL v2 text.
1.0.2
- Removed compatibility coupling to earlier builds by using a unique namespace, constants, option name, metadata key, shortcode, block name, handles, and filters.
- Fixed block setting inheritance by removing attribute defaults from
block.json; unset attributes now inherit plugin settings and the block can explicitly choose automatic browser language. - Upgraded the block to API version 3, added
useBlockProps(), a selectable editor wrapper, a stable visual placeholder, translated controls, and select-based language input. - Replaced the classic visibility meta box with registered REST post meta and a block-editor document settings panel.
- Enqueued front-end assets early from singular query context rather than relying on loop state, so reserved height CSS can be present before content rendering. Predicted buttons load Google’s async script in the head; late manual rendering has a footer-only fallback.
- Prevented excerpt generation from consuming the automatic-injection guard and moved automatic injection after block/shortcode processing for stronger duplicate detection.
- Added context-safe post resolution so archive term/user IDs are never treated as post IDs.
- Switched alignment to logical start/center/end values for RTL compatibility while accepting left/right shortcode aliases.
- Added AMP suppression when the official AMP detection function is available.
- Limited selectable post types to public and publicly queryable types.
- Replaced regex script-tag mutation with
WP_HTML_Tag_Processor. - Cached the embedded language list per request and removed duplicate Chinese locale aliases from the embedded canonical list.
- Moved all translatable source strings to English and added editor-script translation loading.
- Added the required External services disclosure with service, terms, and privacy links.
- Added optional WP Consent API interoperability without adding a hard dependency.
- Updated WordPress.org metadata to
Tested up to: 7.1. - Added
phpcs.xml.distand.distignorefor release-quality tooling.
1.0.1
- Previous internal build.