Meta and Micro data is important. I think the web should be more semantic. I’ve collected a bunch of different approaches to handling them in WordPress. I’m tidying the collection up to include in this theme and making my notes about it here.
Microdata
I used to participate in the WHATWG group mostly as a listener. I like to sign up for new W3C groups anytime there’s something interesting afoot. I paid close attention to the Microdata specs because I’ve always thought this type of semantic data could change how the web operates. I use a moderate amount of microdata based on the Scheme.org vocabulary in the templates.
Metadata
I’m not a big fan of the terms ‘meta tags’ or ‘SEO’. I got a call recently to deal with a website that kept redirecting all of the archive page and after 15 minutes or so, I determined that it was simply a setting the SEO tool they had installed. If you properly use the core excerpt field for meta descriptions, mark up your pages correctly and don’t spam the internet, you’ll be fine without a bloated SEO plugin. WordPress 5.7 is adding in additional robots.txt fields and WordPress 5.5 added XML sitemaps.
I’ve also set up the theme so display most of the meta properties for Facebook’s Open Graph and Twitter’s Card features. Many other sites also respect these two meta fields. One of the more complex features I’ve added is the ability to embed videos into posts and It’s nice to have the video show up in a text message if I just send the link. The video URL is handled by either a query for any video containers in the post or page. I’ve also set a custom meta-box in Gutenberg called media_url which will allow you to manually set the video url for those hosted by third parties. The I’ve set a template to use the core Featured Image for the video poster but it can be overwritten by setting the Featured Image URL in the DWP Gutenberg Meta Settings.
dwp-gutenberg/src/index.js
import { registerPlugin } from "@wordpress/plugins";
import { PluginSidebar, PluginSidebarMoreMenuItem } from "@wordpress/edit-post";
import { __ } from "@wordpress/i18n";
import { PanelBody, TextControl, TextareaControl } from "@wordpress/components";
import { withSelect, withDispatch } from "@wordpress/data";
let PluginMetaFields = (props) => {
return (
<>
<PanelBody
title={__("Media URL", "textdomain")}
icon="format-video"
intialOpen={ true }
>
<TextControl
value={props.text_metafield}
label={__("video for meta", "textdomain")}
help="( postmeta: dwp23_media_meta_url )"
onChange={(value) => props.onMetaFieldChange_media_url(value)}
/>
</PanelBody>
</>
)
}
let PluginMetaFields_poster_image_url = (props) => {
return (
<>
<PanelBody
title={__("Poster Image URL", "textdomain")}
icon="format-image"
intialOpen={ true }
>
<TextControl
value={props.text_metafield_poster_image_url}
label={__("image for video poster", "textdomain")}
help="( postmeta: dwp23_image_meta_url )"
onChange={(value) => props.onMetaFieldChange_poster_image_url(value)}
/>
</PanelBody>
</>
)
}
PluginMetaFields_media_url = withSelect(
(select) => { return {
text_metafield: select('core/editor').getEditedPostAttribute('meta')['dwp23_media_url_meta']}
})(PluginMetaFields_media_url);
PluginMetaFields_media_url = withDispatch(
(dispatch) => { return {
onMetaFieldChange_media_url: (value) => {
dispatch('core/editor').editPost({meta: {dwp23_media_url_meta: value}})
}
}
})(PluginMetaFields_media_url);
PluginMetaFields_poster_image_url = withSelect(
(select) => { return {
text_metafield_featured_image_url: select('core/editor').getEditedPostAttribute('meta')['dwp23_poster_url_meta']
}
})(PluginMetaFields_poster_image_url);
PluginMetaFields_poster_image_url = withDispatch(
(dispatch) => { return {
onMetaFieldChange_poster_image_url: (value) => {
dispatch('core/editor').editPost({meta: {dwp23_poster_url_meta: value}})
}
}
})(PluginMetaFields_poster_image_url);
registerPlugin( 'dwp-guten-sidebar', {
icon: 'welcome-widgets-menus',
render: () => { return (
<>
<PluginSidebarMoreMenuItem
target="dwp-guten-sidebar"
>
{__('Meta Options', 'textdomain')}
</PluginSidebarMoreMenuItem>
<PluginSidebar
name="dwp-guten-sidebar"
title={__('Post Meta', 'textdomain')}
>
<PluginMetaFields />
<PluginMetaFields_featured_image_url />
</PluginSidebar>
</>
)}
})
/inc/theme-base.php
/***********************************************************
######################## Metadata ##########################
************************************************************/
add_action('wp_head', 'dwp23_metadata', 5);
function dwp23_metadata() {
global $post;
if (is_singular()) {
if (has_post_thumbnail($post->ID)) {
$img_src = wp_get_attachment_image_src(get_post_thumbnail_id( $post->ID ), 'full'); $img_src = $img_src[0];
} elseif ( metadata_exists( 'post', get_the_ID(), 'featured_image_url' ) ){
$img_src = get_post_meta( get_the_ID(), 'featured_image_url', true );
} else {
$custom_logo_id = get_theme_mod( 'custom_logo' );
$logo = wp_get_attachment_image_src( $custom_logo_id , 'full' );
$img_src = $logo[0];
}
if($excerpt = $post->post_excerpt) {
$excerpt = strip_tags($post->post_excerpt);
$excerpt = str_replace("", "'", $excerpt);
} else {
$excerpt = get_bloginfo('description');
}
?>
<meta name="description" content="<?php echo $excerpt; ?>"/>
<meta property="og:title" content="<?php echo the_title(); ?>"/>
<meta property="og:description" content="<?php echo $excerpt; ?>"/>
<meta property="og:type" content="article"/>
<meta property="og:url" content="<?php echo the_permalink(); ?>"/>
<meta property="og:site_name" content="<?php echo get_bloginfo(); ?>"/>
<meta property="og:image" content="<?php echo $img_src; ?>"/>
<meta property="fb:app_id" content="<?php echo get_option('dwp23_theme_meta_facebook'); ?>" />
<meta name="twitter:site" content="@<?php echo get_option('dwp23_theme_meta_twitter'); ?>">
<meta name="twitter:creator" content="@<?php echo get_option('dwp23_theme_meta_twitter'); ?>">
<meta name="twitter:title" content="<?php echo the_title(); ?>">
<meta name="twitter:description" content="<?php echo $excerpt; ?>">
<meta name="twitter:image" content="<?php echo $img_src; ?>">
<?php
} else {
return;
}
$content = do_shortcode( apply_filters( 'the_content', $post->post_content ) );
$media_url = get_post_meta( get_the_ID(), 'dwp23_media_url_meta', true );
$media = get_media_embedded_in_content( $content );
if( !empty($media) ) {
?>
<meta property="og:video" content="<?php echo $media_url; ?>" />
<meta property="og:video:secure_url" content="<?php echo $media_url; ?>" />
<meta property="og:video:width" content="1280" />
<meta property="og:video:height" content="720" />
<meta property="og:video:type" content="video/mp4" />
<meta name="twitter:card" content="player">
<meta name="twitter:player" content="<?php echo get_permalink();?>container/" />
<meta name="twitter:player:width" content="1280" />
<meta name="twitter:player:height" content="720" />
<meta name="twitter:player:stream" content="<?php echo $media_url; ?>" />
<meta name="twitter:player:stream:content_type" content="video/mp4" />
<?php
} else {
?>
<meta name="twitter:card" content="summary_large_image">
<?php
}
}
add_action( 'init', 'dwp23_read_container_endpoint' );
function dwp23_read_container_endpoint(){
add_rewrite_endpoint( 'container', EP_PERMALINK);
}
add_filter( 'single_template', 'dwp23_read_container_template' );
function dwp23_read_container_template( $template = '' ) {
global $wp_query;
if( ! array_key_exists( 'container', $wp_query->query_vars ) ) return $template;
$template = locate_template( 'single-container.php' );
return $template;
}
add_shortcode( 'video', 'dwp23_video_embed' );
function dwp23_video_embed( $attr, $content='' ) {
if ( ! isset( $attr['poster'] ) && has_post_thumbnail() ) {
$poster = get_post_meta( get_the_ID(), 'media-poster', true );
$attr['poster'] = $poster;
}
return wp_video_shortcode( $attr, $content );
}
I’m setting the options for the Facebook App ID and the Twitter Username in the theme settings page.
/inc/theme-settings.php
add_action('admin_init', 'dwp23_theme_meta_settings_fields');
function dwp23_theme_meta_settings_fields() {
add_settings_section(
'dwp23_theme_meta',
'Theme Meta',
'dwp23_theme_meta_settings_callback',
'dwp-theme-options'
);
register_setting(
'dwp-section',
'dwp23_theme_meta_twitter'
);
add_settings_field(
'meta-twitter',
'Twitter Username',
'dwp23_theme_meta_twitter',
'dwp-theme-options',
'dwp23_theme_meta'
);
register_setting(
'dwp-section',
'dwp23_theme_meta_facebook'
);
add_settings_field(
'meta-facebook',
'Facebook App ID',
'dwp23_theme_meta_facebook',
'dwp-theme-options',
'dwp23_theme_meta'
);
}
function dwp23_theme_meta_settings_callback() {
echo '<p>These fields control the meta and micro data output.</p>';
}
function dwp23_theme_meta_twitter() {
?>
<div class="row g-3 align-items-center">
<div class="col-auto">
<div class="input-group flex-nowrap">
<span class="input-group-text" id="addon-wrapping">@</span>
<input name="dwp23_theme_meta_twitter" type="text" class="form-control" placeholder="Username" aria-label="" aria-describedby="addon-wrapping" value="<?php echo get_option('dwp23_theme_meta_twitter'); ?>">
</div>
</div>
<div class="col-auto">
<span class="form-text">
<a href="https://developer.twitter.com/en/docs/twitter-for-websites/cards/" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-patch-question" viewBox="0 0 16 16">
<path d="M8.05 9.6c.336 0 .504-.24.554-.627.04-.534.198-.815.847-1.26.673-.475 1.049-1.09 1.049-1.986 0-1.325-.92-2.227-2.262-2.227-1.02 0-1.792.492-2.1 1.29A1.71 1.71 0 0 0 6 5.48c0 .393.203.64.545.64.272 0 .455-.147.564-.51.158-.592.525-.915 1.074-.915.61 0 1.03.446 1.03 1.084 0 .563-.208.885-.822 1.325-.619.433-.926.914-.926 1.64v.111c0 .428.208.745.585.745z"/>
<path d="M10.273 2.513l-.921-.944.715-.698.622.637.89-.011a2.89 2.89 0 0 1 2.924 2.924l-.01.89.636.622a2.89 2.89 0 0 1 0 4.134l-.637.622.011.89a2.89 2.89 0 0 1-2.924 2.924l-.89-.01-.622.636a2.89 2.89 0 0 1-4.134 0l-.622-.637-.89.011a2.89 2.89 0 0 1-2.924-2.924l.01-.89-.636-.622a2.89 2.89 0 0 1 0-4.134l.637-.622-.011-.89a2.89 2.89 0 0 1 2.924-2.924l.89.01.622-.636a2.89 2.89 0 0 1 4.134 0l-.715.698a1.89 1.89 0 0 0-2.704 0l-.92.944-1.32-.016a1.89 1.89 0 0 0-1.911 1.912l.016 1.318-.944.921a1.89 1.89 0 0 0 0 2.704l.944.92-.016 1.32a1.89 1.89 0 0 0 1.912 1.911l1.318-.016.921.944a1.89 1.89 0 0 0 2.704 0l.92-.944 1.32.016a1.89 1.89 0 0 0 1.911-1.912l-.016-1.318.944-.921a1.89 1.89 0 0 0 0-2.704l-.944-.92.016-1.32a1.89 1.89 0 0 0-1.912-1.911l-1.318.016z"/>
<path d="M7.002 11a1 1 0 1 1 2 0 1 1 0 0 1-2 0z"/>
</svg>
</a>
</span>
</div>
</div>
<?php
}
function dwp23_theme_meta_facebook() {
?>
<div class="row g-3 align-items-center">
<div class="col-auto">
<div class="input-group flex-nowrap">
<span class="input-group-text" id="addon-wrapping">#</span>
<input name="dwp23_theme_meta_facebook" type="text" class="form-control" placeholder="App ID" aria-label="" aria-describedby="addon-wrapping" value="<?php echo get_option('dwp23_theme_meta_facebook'); ?>">
</div>
</div>
<div class="col-auto">
<span class="form-text">
<a href="https://developers.facebook.com/docs/sharing/webmasters/" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-patch-question" viewBox="0 0 16 16">
<path d="M8.05 9.6c.336 0 .504-.24.554-.627.04-.534.198-.815.847-1.26.673-.475 1.049-1.09 1.049-1.986 0-1.325-.92-2.227-2.262-2.227-1.02 0-1.792.492-2.1 1.29A1.71 1.71 0 0 0 6 5.48c0 .393.203.64.545.64.272 0 .455-.147.564-.51.158-.592.525-.915 1.074-.915.61 0 1.03.446 1.03 1.084 0 .563-.208.885-.822 1.325-.619.433-.926.914-.926 1.64v.111c0 .428.208.745.585.745z"/>
<path d="M10.273 2.513l-.921-.944.715-.698.622.637.89-.011a2.89 2.89 0 0 1 2.924 2.924l-.01.89.636.622a2.89 2.89 0 0 1 0 4.134l-.637.622.011.89a2.89 2.89 0 0 1-2.924 2.924l-.89-.01-.622.636a2.89 2.89 0 0 1-4.134 0l-.622-.637-.89.011a2.89 2.89 0 0 1-2.924-2.924l.01-.89-.636-.622a2.89 2.89 0 0 1 0-4.134l.637-.622-.011-.89a2.89 2.89 0 0 1 2.924-2.924l.89.01.622-.636a2.89 2.89 0 0 1 4.134 0l-.715.698a1.89 1.89 0 0 0-2.704 0l-.92.944-1.32-.016a1.89 1.89 0 0 0-1.911 1.912l.016 1.318-.944.921a1.89 1.89 0 0 0 0 2.704l.944.92-.016 1.32a1.89 1.89 0 0 0 1.912 1.911l1.318-.016.921.944a1.89 1.89 0 0 0 2.704 0l.92-.944 1.32.016a1.89 1.89 0 0 0 1.911-1.912l-.016-1.318.944-.921a1.89 1.89 0 0 0 0-2.704l-.944-.92.016-1.32a1.89 1.89 0 0 0-1.912-1.911l-1.318.016z"/>
<path d="M7.002 11a1 1 0 1 1 2 0 1 1 0 0 1-2 0z"/>
</svg>
</a>
</span>
</div>
</div>
<?php
}
References:
- Semantic Web – https://en.wikipedia.org/wiki/Semantic_Web
- RDFa – https://www.w3.org/TR/rdfa-primer/
- Schema.org – https://schema.org/
- Facebook – Open Graph Protocol – https://ogp.me/
- Twitter – https://developer.twitter.com/en/docs/twitter-for-websites/cards/overview/markup