Register Additional HTML Attributes for TinyMCE and WP KSES

Out of the box, WordPress gives content creators a fair amount of flexibility in what can be entered into the Visual and Text editors. They can enter all sorts of HTML tags which can be a blessing or a curse. You may find you want to register additional HTML elements or attributes for them to use, or to remove ones you don’t want them to use.

To keep additional HTML attributes from being stripped by WP Kses when the post is saved, you’ll need to register them on the $allowedposttags global. Here’s what it would look like:

add_action( 'init', 'vipx_allow_contenteditable_on_divs' );
function vipx_allow_contenteditable_on_divs() {
	global $allowedposttags;

	$tags = array( 'div' );
	$new_attributes = array( 'contenteditable' => array() );

	foreach ( $tags as $tag ) {
		if ( isset( $allowedposttags[ $tag ] ) && is_array( $allowedposttags[ $tag ] ) )
			$allowedposttags[ $tag ] = array_merge( $allowedposttags[ $tag ], $new_attributes );

With just that snippet, however, you’ll find that you can save the proper markup in the Text editor but it gets stripped out in the Visual (TinyMCE) editor. To get around this, you’ll need to also register your new attributes with TinyMCE:

add_filter('tiny_mce_before_init', 'vipx_filter_tiny_mce_before_init');
function vipx_filter_tiny_mce_before_init( $options ) {

	if ( ! isset( $options['extended_valid_elements'] ) ) {
		$options['extended_valid_elements'] = '';
	} else {
		$options['extended_valid_elements'] .= ',';

	if ( ! isset( $options['custom_elements'] ) ) {
		$options['custom_elements'] = '';
	} else {
		$options['custom_elements'] .= ',';

	$options['extended_valid_elements'] .= 'div[contenteditable|class|id|style]';
	$options['custom_elements']         .= 'div[contenteditable|class|id|style]';
	return $options;

Embedding rich media in your post

As a member of VIP, you are able to embed rich media into your posts. This document will quickly explain how embedding works.

Commonly Used Embeds

Looking to embed content from YouTube, Hulu, InstagramVimeoBlip.tvCNNMoney videoDaily MotionGist or Reddit? Simply copy the URL in your browser bar while viewing the video or photograph, and paste it into your editor on its own line. We’ll automatically embed the content for you.

Note: Make sure the URL is not linked or formatted in any way, and that it is on its own line. You can read more about WordPress’s custom embeds here.

Looking to embed content from Scribd, Slideshare, Wufoo, Flickr Video, SoundCloud or Bandcamp? Each of these services offers a shortcode that you can paste directly into your post editor. Click on the service’s name above for more information on how to embed their content.

Scripts, iFrames, and Objects VIP handles embeds and scripts that haven’t already been whitelisted above in a special way for security protection. Here’s how to embed code that begins with `<script>, <iframe>, <object>`.

1) In your post editor, there is a tool above the formatting bar that allows you to include scripts. You can click either “Add Media” or “Add Embed.”

Screen Shot 2013-07-22 at 5.55.27 PM

2) A window will pop up. Be sure to select the “Insert Embed” tab. Paste the script code into this window, and click “Insert.”

Screen Shot 2013-07-22 at 5.55.56 PM

3) A shortcode will be inserted in your post, representing your protected embed.

Custom Widths/Heights For “Protected Embeds”

Our “protected embeds” feature attempts to make an educated guess at the ideal width or height of your embed, but if the object you’re embedding doesn’t make those values obvious, we’re not always able to do that. Instead, you can specify your own width or height attributes for the iframes in the shortcode, as such:

protected-iframe id="#" info="#" width="100%" height="200"

Importing Content with Embeds

If you’re importing content from a non-VIP site, you may have embedded scripts, iframes and objects that need to work on your VIP site. To ensure this happens, please open a support ticket and request that we handle the initial import for you, which will allow that embedded content to pass through the import process. Then, when you next edit one of those posts or pages, the embed will be automatically converted to a protected embed.

Web Fonts and Custom Typefaces

To use a fancy typeface or font on your VIP site(s), we recommend using Typekit or Google Web Fonts. Both have very easy-to-integrate APIs, are optimized to work across a wide range of popular browsers, and have large libraries of beautiful fonts.

For licensed or custom typefaces or fonts, include font files within your themes and embed them using the @font-face declaration in your CSS.

@font-face and cross-domain

Because we serve static assets (like JS and CSS) from CDN domains (e.g., we set a “Access-Control-Allow-Origin” header for all font files to prevent cross-domain issues in certain browsers (like Firefox). Older base64-based approaches are no longer needed as workarounds.

robots.txt uses the core robots.txt file and adds a number of default entries, such as the sitemap, to optimize your site. To modify the file, you can hook into the do_robotstxt action, or filter the output by hooking into robots_txt (source).

Example: Mark a directory as “nofollow”

function my_disallow_directory() {
	echo "User-agent: *" . PHP_EOL;
	echo "Disallow: /path/to/your/directory/" . PHP_EOL;
add_action( 'do_robotstxt', 'my_disallow_directory' );


Note that we cache the robots.txt for long periods of time. This means that you’ll need to force the caches to clear after any changes, by going to Settings > Reading from your Dashboard and toggling the privacy settings.

On domains

On any subdomain of, the robots.txt output will be hard-coded to return a “Disallow for all user agents” result. This is to prevent search engines from indexing content hosted on development/staging sites.

Custom Fields

The custom fields UI isn’t included on — it’s clunky, dependent on theme customizations, and poses some security risks. You’ll want to explicitly add any individual custom fields that your themes or plugins use using a plugin like Fieldmanager

Customizing Invites

On, new users need to be invited to your site. The default behavior for invitations is to allow any user accept an invitation regardless of whether their email address matches what the invitation was sent to. Here’s a helper function you can use to force the invitation email to match the user’s email address:


Infinite Scroll

Some of the standard themes on support infinite scroll, meaning additional posts are loaded and displayed as you approach the bottom of the page. Our metrics have shown that this increases reader retention. You can see it in action at or

If you would like to add this functionality to your theme, please visit the Jetpack support page on Infinite Scroll.

By default on, Infinite Scroll is only enable on the home page. You can use the infinite_scroll_archive_supported filter to enable it in additional contexts within the theme.

For example:

function mytheme_infinite_scroll_archive_supported() {
    return is_home() || is_archive();
add_filter( 'infinite_scroll_archive_supported', 'mytheme_infinite_scroll_archive_supported' );

Note: when you enable Infinite Scroll, the posts per page value is changed to seven. Every Ajax load puts seven more posts and counts as one pageview.

Using Data from Stats

We’ve created various helper functions that make it easy to work with Stats data. You can find these functions in the following helper file: vip-helper-stats-wpcom.php. This file is auto-loaded for you.

Note: Please use these functions with care as they can be fairly resource-intensive and can cause issues if abused under high traffic scenarios.

Getting Most Popular Posts

The following helper function returns popular posts for a specified number of days: wpcom_vip_top_posts_array. It returns data in the raw format returned by the Stats API. If you don’t care much about the actual view count, you can easily use the returned post_id to fetch the full post objects.

This function fetches the list of posts (and pages) that have had the most views recently, and doesn’t do any filtering of the resultant data.

If you want to exclude pages from the listing on your sites, or previously popular posts that you have now deleted, then you need to add code to your theme to filter these out from display.

Getting Other Data

Using the wpcom_vip_get_stats_array function, you can retrieve the following data:

  • views (daily views)
  • postviews (top posts)
  • referrers (top referrers)
  • searchterms (top referring search terms)
  • clicks (top outbound clicks)
  • authorviews (top authors)

Stats API Stats has a nice API to fetch all sorts of cool data too! For access to this data outside of your site/theme, please read our documentation here.

Sitemaps – News and XML

All public sites come with XML and News sitemaps built-in. The sitemaps are automatically generated, cached for a 24-hour period, and updated whenever a post is published, updated, or deleted (assuming the site is public; sitemaps aren’t produced if your site is still set to private).

You can learn more about the sitemaps here:

You can customize some of the output by hooking into various filters. A non-exhaustive list of examples is below. If you’re interested in modifying something beyond what’s shown below, just let us know.

The sitemap code is auto-loaded for local testing. You can find it in plugins/vip-do-not-include-on-wpcom/wpcom-plugins/wpcom-sitemap.php.

Comprehensive Sitemaps

The built-in sitemaps only include a sampling of the data from each site. If you’d like all your content indexed in sitemaps, check out the Comprehensive Sitemaps plugin. XML Sitemap: Exclude certain posts

add_filter( 'sitemap_skip_post', 'x_sitemap_skip_post', 10, 2 );  // Regular sitemap
add_filter( 'wpcom_sitemap_news_skip_post', 'x_sitemap_skip_post', 10, 2 );  // News sitemap

function x_sitemap_skip_post( $skip, $post ) { // $post is an object with properties: ID, post_type, post_modified_gmt, comment_count
	if ( get_post_meta( $post->ID, 'x_skip_post', true ) )
		$skip = true;
	return $skip;
} XML Sitemap: Include additional post types

By default, sitemaps only include posts. Use the following to include additional post types.

add_filter( 'wpcom_sitemap_post_types', 'x_sitemap_add_gallery_post_type' );

function x_sitemap_add_gallery_post_type( $post_types ) {
	$post_types[] = 'gallery';
	return $post_types;
} News Sitemap: Include additional post types

By default, news sitemaps only include posts. Use the following to include additional post types.

add_filter( 'wpcom_sitemap_news_sitemap_post_types', 'x_sitemap_add_gallery_post_type' );

function x_sitemap_add_gallery_post_type( $post_types ) {
	$post_types[] = 'gallery';
	return $post_types;
} News Sitemap: Change the publication name

add_filter( 'wpcom_sitemap_news_sitemap_item', 'x_filter_news_sitemap_name', 10, 2 );

function x_filter_news_sitemap_name( $item, $post ) {
	$item['news:news']['news:publication']['news:name'] = 'My Publication'; // modify as needed for your site
	return $item;
} XML Sitemap: Modify the changefreq of an item

Creates a sliding changefreq for posts, based on their last modified time.

function x_wpcom_sitemap_url( $url, $post_id ) {
	$yesterday 	= strtotime( '-1 day' );
	$last_week 	= strtotime( '-7 days' );
	$last_mod 	= strtotime( $url['lastmod'] );

	if( $last_mod > $yesterday ){
		$url['changefreq'] = 'hourly';
	} elseif( $last_mod > $last_week ) {
		$url['changefreq'] = 'daily';
	} else {
		$url['changefreq'] = 'monthly';

    return $url;

add_filter( 'sitemap_url', 'x_wpcom_sitemap_url', 10, 2 );

home_url() vs site_url()

When working with domain-mapped sites on, home_url() and site_url() will return different values.

  • home_url() returns the primary mapped domain (e.g.
  • site_url() returns the * URL (e.g.

A few notes:

  • home_url() will only return the mapped domain on or after the init has fired. Calling it before then will return the domain.
  • If you accidentally use site_url() in your templates, theme-side links will still redirect correctly to the home_url() equivalent.
  • home_url() is the preferred method, as it avoids the above redirect.

Ready to get started?

Drop us a note.

No matter where you are in the planning process, we’re happy to help, and we’re actual humans here on the other side of the form. 👋 We’re here to discuss your challenges and plans, evaluate your existing resources or a potential partner, or even make some initial recommendations. And, of course, we’re here to help any time you’re in the market for some robust WordPress awesomeness.