PRC Schema - SEO
Provides schema generation, SEO meta tags, primary term management, preview panels, and site-level template defaults for titles & descriptions for PRC Platform sites.
by Seth Rubenstein · github.com/pewresearch/prc-schema-seo · website
Install
No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:
wp plugin install https://github.com/pewresearch/prc-schema-seo/archive/refs/heads/trunk.zipReadme
PRC Schema SEO
Provides schema generation, SEO meta tags, primary term management, preview panels, and site-level template defaults for titles & descriptions on Pew Research Center platform sites.
Features
- JSON-LD schema generation with post-type level overrides
- Meta tag output (title, description, canonical, robots, Open Graph, Twitter)
- Primary term selection per taxonomy
- Editor previews (search, social, chat, internal)
- Site Editor sidebar for global pattern configuration and noindex lists
- Extensible pattern token engine
- AI-powered SEO suggestions (title, description, social text) via the
prc-schema-seo/suggestWP AI ability - Real-Time Collaboration (RTC) compatible editor UI (WP 7.0+)
- Auto-redirects on slug/term changes via Safe Redirect Manager integration
- Redirect CSV import UI
- IndexNow search engine notification on publish
- Google Search Console URL Inspection integration
- Reading score WP Ability
- Parse.ly metadata via the official
wp-parselyplugin (see below)
Requirements
Required plugins (declared in the plugin header Requires Plugins field):
prc-scripts— provides@prc/components,@prc/icons, and shared webpack configurationprc-post-publish-pipeline— sync and async post lifecycle hooks used for cache invalidation, IndexNow, and Search Console
The plugin no longer depends on prc-platform-core. All shared JS components and scripts are sourced from prc-scripts.
Parse.ly (wp-parsely)
The platform uses Automattic’s wp-parsely on VIP. prc-schema-seo implements Parsely_Integration (includes/class-parsely-integration.php) so Pew SEO data (canonical URL, titles, authors, sections, keywords) is merged into Parse.ly’s metadata pipeline instead of maintaining a parallel tag system.
How it works
| Context | Behavior |
|---|---|
| Singular posts/pages (supported post types) | wp_parsely_metadata and wp_parsely_permalink enrich Parse.ly’s array with PRC SEO fields from Metadata::get_seo_data() / resolve_for_display(). Whether the plugin emits JSON-LD or repeated <meta name="parsely-*"> tags follows Parse.ly’s own settings in WP Admin (meta_type and related options). |
| Static front page, category/tag/custom tax archives, post type archives | PRC emits its own parsely-title / parsely-link / parsely-type meta tags from Parsely_Integration at wp_head priority 3, and suppresses wp-parsely's renderer by returning an empty array from wp_parsely_metadata (the renderer bails at its ! isset( $metadata['headline'] ) guard). Links are forced to the https scheme. This works for both json_ld and repeated_metas output formats. |
| RLS template post type | PRC suppresses wp-parsely output for this post type by returning an empty array from wp_parsely_metadata; PRC does not merge Parse.ly metadata for it. |
WordPress hooks used
| Hook | Purpose |
|---|---|
wpvip_parsely_load_mu |
On local environment, return true so VIP’s Parse.ly MU integration loads (matches production behavior in dev). |
wp_parsely_managed_options |
Forces force_https_canonicals to true so wp-parsely never rewrites non-post URLs to http://. |
wp_parsely_metadata |
Two roles: (1) on singular tracked posts, map PRC fields into Parse.ly’s metadata array (headline, url, thumbnail, keywords, articleSection, author/creator, dates); (2) on the front page, term archives, post type archives, and RLS template singular, return an empty array to suppress wp-parsely's own renderer (which bails when headline is unset). The wp_parsely_should_insert_metadata filter is intentionally not used because wp-parsely evaluates it once at plugin init (before wp has fired), making conditional-tag-based gating unreliable. |
wp_parsely_permalink |
Align Parse.ly link meta with the PRC canonical URL (prc_schema_seo_canonical_url filter applies); scheme forced to https. |
Caching
Home, term, and post type archive Parse.ly HTML fragments are cached with wp_cache_* in group prc_schema_seo_parsely_07142026, TTL 1 hour, keys such as parsely_tags_home, parsely_tags_term_{term_id}, and parsely_tags_post_type_archive_{post_type}. Term and home/archive fragments are invalidated on term meta updates and blogname/home/siteurl option changes. The WP-CLI migration command can warm term cache via the same Parsely_Integration::warm_term_cache() path.
Operational notes
- Canonical: Changes that affect
prc_schema_seo_canonical_urlor stored SEO data affect Parse.ly link andurlfields after cache invalidation or TTL expiry. - Keywords / section: Built from category, formats, research-teams, and internal markers (
get_parsely_tag_tokens,get_parsely_section); empty section is omitted soarticleSectionis not sent when there is no primary category name. - Troubleshooting: If Parse.ly tags are missing locally, confirm environment type is
local(for MU loader), thatwp-parselyis active, and that the current template is not one where insertion is intentionally disabled (see table above).
Pattern Tokens
Patterns can be set for title & description via Site Editor sidebar fields. The following tokens are available:
Static tokens:
%post_title%
%site_name%
%primary_category%
%post_type%
%year%
%author%
%categories% (comma-separated list of category names)
%tags% (comma-separated list of tag names)
Dynamic tokens:
%primary_term:taxonomy% (chosen primary term for the taxonomy, falls back to first term)
%terms:taxonomy% (comma-separated list of all term names for taxonomy)
Whitespace is normalized after replacement. Empty tokens resolve to an empty string.
Extending Tokens
Use the prc_schema_seo_pattern_tokens filter to register additional simple tokens (key/value pairs). Dynamic patterns should be handled separately (e.g. by also parsing custom placeholders inside a secondary filter if needed).
add_filter( 'prc_schema_seo_pattern_tokens', function( $tokens, $post_id ) {
$modified = get_post( $post_id ) ? get_post_modified_time( 'Y-m-d', false, $post_id ) : '';
$tokens['%modified_date%'] = $modified;
return $tokens;
}, 10, 2 );
Filters Reference
All hooks use the prc_schema_seo_ prefix. Return values must match the documented type; invalid types are ignored and logged.
Schema Output
| Filter | Signature | Purpose |
|---|---|---|
prc_schema_seo_should_output_schema |
(bool $should, int $post_id) |
Gate schema emission for a post. Return false to suppress. |
prc_schema_seo_minify_json |
(bool $minify) |
Control whether JSON-LD output is minified. Default false. |
prc_schema_seo_schema_type_default |
(string $type, string $post_type, int $post_id) |
Override the default schema type for a post type. |
prc_schema_seo_allowed_schema_types |
(array $types, string $post_type) |
Whitelist schema types available in the editor UI. Must return a flat array of strings. |
prc_schema_seo_schema_data |
(array $schemas, int $post_id, array $seo_data) |
Modify the full schema graph array just before JSON-LD serialization. |
// Example: mark a custom post type as SoftwareApplication
add_filter( 'prc_schema_seo_schema_type_default', function( $type, $post_type, $post_id ) {
if ( 'tool' === $post_type ) {
return 'SoftwareApplication';
}
return $type;
}, 10, 3 );
Schema Objects — Article / Person / WebPage
| Filter | Signature | Purpose |
|---|---|---|
prc_schema_seo_article_schema |
(Article $article, int $post_id, array $seo_data, string $schema_type) |
Adjust Article / NewsArticle / BlogPosting schema object before graph merge. |
prc_schema_seo_person_schema |
(Person $person, int $post_id, array $seo_data) |
Adjust Person schema (used for staff post type and author term pages). |
prc_schema_seo_webpage_schema |
(WebPage $webpage, int $post_id, array $seo_data) |
Adjust the final WebPage schema object. |
prc_schema_seo_webpage_graph_item |
(WebPage $webpage, int $post_id, array $seo_data) |
Adjust the WebPage item specifically as a graph item reference before the full schema merge. |
prc_schema_seo_breadcrumb_schema |
(BreadcrumbList $list, int $post_id, array $seo_data) |
Adjust the BreadcrumbList schema for a post. |
Schema Objects — Organization
| Filter | Signature | Purpose |
|---|---|---|
prc_schema_seo_organization_schema |
(Organization $org, int $post_id) |
Adjust the fully assembled Organization schema object. |
prc_schema_seo_organization_config |
(array $config) |
Override the Organization config array (name, URL, description, slogan, logo, social profiles). Replaces the full config; merge carefully. |
prc_schema_seo_organization_name |
(string $name) |
Override the organization name string. Used across schema, AI prompts, and Yoast migration. |
prc_schema_seo_organization_address |
(array $address) |
Override the postal address array (streetAddress, addressLocality, postalCode, addressCountry). |
prc_schema_seo_same_as |
(array $urls) |
Override the sameAs URL array on the Organization schema. |
prc_schema_seo_parent_organization |
(array $config) |
Override the parent organization config (name, url). Defaults to The Pew Charitable Trusts. |