Activating a Plugin on WordPress.com VIP

Here at WordPress.com VIP, we have a large directory of plugins that have been reviewed and approved by our engineers. Here are the following ways to activate a plugin.

1. From Your Dashboard

Simply navigate to VIP > Plugins and Services in your dashboard, find the plugin you’d like, and click “Activate.”

Screen Shot 2013-02-27 at 5.06.38 PM

2. Activating a Shared Plugin In Your Theme

You should already have read access to our shared VIP plugins directory containing a growing list of plugins that you can use.

To test these plugins in your development environment, use your WordPress.com username and password to check out a copy of them from https://vip-svn.wordpress.com/plugins/ using Subversion. To match our environment, they should be checked out to wp-content/themes/vip/plugins/ and not wp-content/plugins/ like you would normally.

svn co https://vip-svn.wordpress.com/plugins/ wp-content/themes/vip/plugins

To load plugins from the shared themes/vip/plugins/ folder, you can use the following function call:

wpcom_vip_load_plugin( 'zoninator' );

3. Activating a Custom Plugin

If you are interested in using a plugin that isn’t available as a pre-approved VIP shared plugin, we will have to review it for stability, performance, and security before it can be used on WordPress.com. Please read more about Plugin Reviews here.

Once your plugin is approved, add it to the “plugins” directory within your theme directory. To activate it, require_once/include_once them in functions.php. e.g.:

require_once( __DIR__ . '/plugins/custom-plugin/custom-plugin.php' );

Change Your Pretty Permalinks or Add Custom Rewrite Rules

Permalinks are the permanent URLs to posts, categories, and other sections of your website. They generally look like this by default:

http://vip.wordpress.com/2012/09/04/new-liveblog-add-on/

http://vip.wordpress.com/category/plugins/

http://vip.wordpress.com/tag/liveblog/

URLs should be permanent and never change — hence the name permalink. When you’re first configuring your site though, you can modify them from the default structure. Some of the values you can use include:

%year% The year of the post, four digits, for example 2004
%monthnum% Month of the year, for example 05
%day% Day of the month, for example 28
%hour% Hour of the day, for example 15
%minute% Minute of the hour, for example 43
%second% Second of the minute, for example 33
%post_id% The unique ID # of the post, for example 423
%postname% A sanitized version of the title of the post (post slug field on Edit Post/Page panel). So “This Is A Great Post!” becomes this-is-a-great-post in the URI.
%category% A sanitized version of the category name (category slug field on New/Edit Category panel). Nested sub-categories appear as nested directories in the URI.

VIP

For WordPress.com VIP clients, if you’d like to customize these permastructs, you can use any of the following helper functions in your theme’s functions.php file:

/**
 * Enable a custom permastruct, if the site wants to use one that's not the WP.com default (/yyyy/mm/dd/post-name/)
 */
wpcom_vip_load_permastruct( '/%category%/%postname%/' );

/**
 * Enables a custom or no category base, if the site wants to use one that's not the WP.com default (/category/)
 */
wpcom_vip_load_category_base( 'section' );

/**
 * Enables a custom or no tag base, if the site wants to use one that's not the WP.com default (/tag/)
 */
wpcom_vip_load_tag_base( 'topic' );

Your theme’s rewrite rules are flushed automatically on every deploy (and when you switch themes), so you only need to worry about committing this change to the repository.

Custom rewrite rules can also be committed to your theme and incorporated in this manner.

Enterprise

Enterprise users have an admin interface for changing their permastructs. It appears at “Settings” -> “Permalinks”:

Enterprise permastruct screen

Writing Bin Scripts

Occasionally, you may find you need to access or transform data on your WordPress.com site. If it’s more than a dozen posts affected, it’s often more efficient to write what we call a “bin script.” In writing a bin script, you can easily change strings, assign categories, or add post meta across hundreds or thousands of posts. However, with great power comes great responsibility — any small mistake you make with your logic could have negative repercussions across your entire dataset.

Here are some guidelines we’d encourage you to follow when writing a bin script.

Use WP-CLI

To keep your scripts as lean and mean as possible, we highly encourage you to leverage WP-CLI, an awesome community framework.

When using WP-CLI, all you’ll need to write for your bin script is what’s called a “command.” Your command accepts zero or more arguments and performs a bit of logic. You won’t need to switch to the proper blog, handle arguments, etc., as all of that is done automatically.

Check out the great documentation on how to write a command. Once you’ve written your command and tested it throughly in your local environment, you can commit it to your theme in a manner similar to this:

if ( defined('WP_CLI') && WP_CLI )
	require_once( dirname( __FILE__ ) . '/php/class-wp-cli.php' );

When you’ve done so, open a ticket with us with explanation of what you’re trying to accomplish. We’ll review, test, and run it.

Keep These Points In Mind

Again, it can be easy to make a minor mistake with your script that causes a lot of pain. We encourage you to do the following:

  • Comment well and provide clear usage instructions. It’s important to be very clear about what each part is doing and why — commenting each step of your logic is a good sanity check. Comments are especially helpful when something maybe doesn’t work as intended and we need to debug to figure out why.
  • Allow for varying levels of verbosity. Similarly, provide a summary at the end with all results of the script.
  • Offer a way to do a test run without affecting live data. We can compare what the actual impact is versus the expected impact.
  • If you’re modifying lots of data on a live site, make sure to include sleep() in key places. This will help with load associated with cache invalidation and replication.
  • If you’re writing an importer, make sure to define( 'WP_IMPORTING', true ); at the top of your subcommand. This will ensure only the minimum of extra actions are fired.
  • Use core functions as much as possible, except when they might have undesired consequences. For instance, wp_update_post() fires a number of hooks you may not want fired when replacing strings in post_content. In this context, a direct SQL query would be a better choice, followed by clean_post_cache().

Creating Cache Groups with vary_cache_on_function

The Scenario

  • Batcache speeds up WordPress.com by reusing one response for many requests.
  • A typical URL is a resource that provides a consistent response. This is easy to cache.
  • For some URLs, the responses vary depending on the request. This is harder to cache.
  • Variants created for logged-in users are not cached at all; users always get fresh responses.
  • Variants created by inspecting other things, such as the User Agent, must be cached separately.
  • Failure to separately cache these variants results in wrong responses. This is bad.

Example Problem

Imagine you want to use Feedburner to serve your feeds. You want to redirect everyone requesting yourblog.com/feed/ to load it from feedburner.com/yourfeed/ instead. You set up your blog to redirect these requests. Then your Feedburner feed breaks because when Feedburner requests your feed, you refuse to give it the data, redirecting it to feedburner.com/yourfeed/ along with everyone else.

So you make an exception: redirect everyone except Feedburner. You inspect the User-Agent header and if it says “feedburner” you don’t redirect; you give Feedburner the feed. This is what the Feedburner plugin accomplishes. Now your feed works as designed and everyone gets what they are supposed to get. Except sometimes they don’t.

Now you’re running into a caching problem. We cache feeds. More correctly, we cache the responses generated by feed URLs so we can serve them more rapidly for subsequent requests. A redirect is also a response and it happens to be one of our favorite kinds of responses to cache.

The problem is that you have created a variant — the conditional redirect — without telling Batcache about it. Being unaware that your feed URL has several possible responses, Batcache merrily gives everyone whichever response is created for the first request after every cache expiration interval — maybe the redirect, maybe the feed — and it doesn’t care who gets which.

Hidden Mechanism

You have to tell the cache that you are serving a variant. But it’s not that simple because what you really have to tell the cache is how you knew that you were going to serve that variant. It needs to know this because its job is to receive requests and send correct responses. And it has to do that without loading themes, plugins, or more than about 2% of WordPress. That is why Batcache is so fast.

So if your URL is going to serve variants to anyone who is not logged in, you must inform Batcache how to determine which response goes with which request. That means giving it some logic to execute on its own, without help from your theme or plugins.

Example Problem Solved

Below is the standard logic to determine whether to serve the feed or the redirect. It is a variant determiner. You just didn’t call it that before.

function is_feedburner() {
    return (bool) preg_match("/feedburner|feedvalidator/i", $_SERVER["HTTP_USER_AGENT"]);
}

To make this logic available to Batcache, you have to give it a copy.

function is_feedburner() {
    if ( function_exists( 'vary_cache_on_function' ) ) {
        vary_cache_on_function(
            'return (bool) preg_match("/feedburner|feedvalidator/i", $_SERVER["HTTP_USER_AGENT"]);'
        );
    }
    return (bool) preg_match("/feedburner|feedvalidator/i", $_SERVER["HTTP_USER_AGENT"]);
}

See what we did there? We put the logic in a string and sent it to Batcache. Now the cache knows how to determine which variant goes with which request.

Solution Explained

The function vary_cache_on_function tells Batcache that the URL currently being served has variants, and how to determine them. It takes one argument, a string that will eventually be passed to create_function. (Click and get acquainted if you are not already. It can be tricky.) This is how your code will be used:

$fun = create_function('', $your_code);
$value = $fun();

This is a lightweight, fast way for Batcache to execute your code without loading your theme and plugins. But it’s not for lightweights. If you are keen on PHP security you might have noticed that any number of very small typos in your code would make it possible for anyone on the internet to execute arbitrary code on your blog, which would be both embarrassing and dangerous. This is equally true of all PHP programming but if you find create_function difficult, please ask for help.

More Mechanism

Very early in the code execution, long before WordPress has had time to figure out which blog was requested or who requested it, before it can include theme or plugin scripts, Batcache swoops into action and inspects the request. With only the superglobals to guide it ($_SERVER, $_GET, $_COOKIE) the cache searches for a pre-generated response. If it finds your “vary” code, it executes that and uses the result to narrow the search.

When Batcache finds a valid response for the current request, it serves the response and halts execution. That typically takes a few milliseconds which is quite fast. When Batcache finds none, the page must be generated from scratch. Batcache becomes dormant and waits until the response is complete. Then, reborn as an output buffer handler, it swoops back in, runs your code, adds the result to the metadata of the response, caches it, and releases the response to the client who requested it.

Other Problems

We haven’t thought of all the ways Batcache can break your variants. There are lots of good reasons to create variants: conditional redirects, different markup for different user agents, serving new features to specific IPs for testing, etc. You get the idea. If you plan on varying the response for any page that will likely cached, you should use vary_cache_on_function.

Tips

  • Your code is subject to the same limitations as Batcache: it can only use core PHP functions and inspect superglobals. While it is technically possible to gain functions by including scripts within your code, this would probably defeat the purpose of keeping the cache as speedy as possible. Avoid this, and certainly don’t do it without consulting a site administrator. They can also tell you how to disable the cache when necessary.
  • Batcache executes your “vary” code only during requests for the exact URL where you called vary_cache_on_function. Please limit your use of that function to URLs that actually have variants. That will help make every page load fast.
  • Get acquainted with create_function. Use single quotes and double quotes appropriately. Test thoroughly before committing or deploying code. If you aren’t sure, ask a site administrator for help.
  • This is worth repeating: when using create_function it is extremely easy to accidentally open the door to arbitrary code execution from the internet. Test, ask others to review your code, and test again.

Batcache

WordPress.com uses Batcache to store and serve cached versions of rendered pages. Batcache uses memcached as its storage and is aimed at preventing a flood of traffic from breaking your site. It does this by serving old pages to new users. This reduces the demand on the web server CPU and the database. It also means some people may see a page that is up to 5 minutes old.

Who receives a cached pageview?

By default, all new users receive a cached pageview.

New users are defined as anybody who hasn’t interacted with your domain—once they’ve left a comment or logged in, their cookies will ensure they get fresh pages. People arriving from Reddit won’t notice that the comments are a minute or two behind, but they’ll appreciate your site being up.

Cache Groups

We bucket users into different cache groups depending on the type of user they are. This means that you can vary the content you serve to each of these groups without worrying about cache collisions, as long as you use our helper functions.

* Logged-in user or commenter (no cache)
* Desktop
* Smartphone
* Dumbphone
* iPad
* Tablet

Exemptions

Note that URLs with query strings are automatically exempt from Batcache. This can be undesirable in many cases as popular pages linked to with query strings can significantly reduce the effectiveness of our caching setup and can affect the overall performance of your site.

Avoid server-side operations

Because Batcache caches fully rendered pages, per-user interactions on the server-side can be problematic. This means usage of objects/functions like $_COOKIE, setcookie, $_SERVER['HTTP_USER_AGENT'], and anything that’s unique to an individual user cannot be relied on as the values may be cached and cross-pollution can occur.

In most cases, any user-level interactions should be moved to client-side using javascript.

In some cases, we can help you set up Batcache variants if you’re limiting your interactions to a small set of distinct groups (e.g. serve different content for users depending on whether the cookie “customer-type” is set, or equals “paid” or “pending”). Please get in touch if this something you’re interested in setting up.

Additional Resources

Localizing & Translating Themes

For localizing and translating themes, you can use the built-in i18n features available in WordPress.

Below, we’ve outlined a few things to keep in mind.

“default” textdomain

On WP.com, you need to use the “default” textdomain when loading the translations. You can use any textdomain you like in the translation functions, but strings should be loaded with the “default” textdomain.

$textdomain = function_exists( 'wpcom_is_vip' ) ? 'default' : 'vip';

if( file_exists( $mofile ) )
        load_textdomain( $textdomain, $mofile );

// __( 'My translatable string', 'vip' );

Translation conflicts

If the translations in your theme conflict with existing translations on WordPress.com, you have two options:

  • If your translation is better/more accurate, you can submit it via our GlotPress instance
  • Use the _x and friends (_ex, _nx, etc.) with an added context so that translations in your theme are given priority over others.

Uncached Functions

WordPress core has a number of functions that, for various reasons, are uncached, which means that calling them will always result in an SQL query. Below, we outline some of these functions. We have a helper file with cached versions of some of the functions, which is automatically available to you.

Validating, Sanitizing, and Escaping

Your code works, but is it safe? When writing code for the WordPress.com VIP environment, you’ll need to be extra cautious of how you handle data coming into WordPress and how it’s presented to the end user. This commonly comes up when building a settings page for your theme, creating and manipulating shortcodes, or saving and rendering extra data associated with a post. There is a distinction between how input and output are managed, and this document will walk you through that.

Validating: Checking User Input

To validate is to ensure the data you’ve requested of the user matches what they’ve submitted. There are several core methods you can use for input validation; usage obviously depends on the type of fields you’d like to validate. Let’s take a look at an example.

Say we have an input area in our form like this:

<input id="my-zipcode" type="text" maxlength="5" name="my-zipcode" />

Just like that, we’ve limited my user to five characters of input, but there’s no limitation on what they can input. They could enter “11221″ or “eval(“. If we’re saving to the database, there’s no way we want to give the user unrestricted write access.

This is where validation plays a role. When processing the form, we’ll write code to check each field for its proper data type. If it’s not of the proper data type, we’ll discard it. For instance, to check “my-zipcode” field, we might do something like this:

$safe_zipcode = intval( $_POST['my-zipcode'] );
if ( ! $safe_zipcode )
$safe_zipcode = '';
update_post_meta( $post->ID, 'my_zipcode', $safe_zipcode );

The intval() function casts user input as an integer, and defaults to zero if the input was a non-numeric value. We then check to see if the value ended up as zero. If it did, we’ll save an empty value to the database. Otherwise, we’ll save the properly validated zipcode.

This style of validation most closely follows WordPress’s whitelist philosophy: only allow the user to input what you’re expecting. Luckily, there’s a number of handy helper functions you can use for most every data type.

Sanitizing: Cleaning User Input

Sanitization is a bit more liberal of an approach to accepting user data. We can fall back to using these methods when there’s a range of acceptable input.

For instance, if we had a form field like this:

<input id="title" type="text" name="title" />

We could sanitize the data with the sanitize_text_field() function:

$title = sanitize_text_field( $_POST['title'] );
update_post_meta( $post->ID, 'title', $title );

Behinds the scenes, the function does the following:

  • Checks for invalid UTF-8
  • Converts single < characters to entity
  • Strips all tags
  • Remove line breaks, tabs and extra white space
  • Strip octets

The sanitize_*() class of helper functions are super nice for us, as they ensure we’re ending up with safe data and require minimal effort on our part.

Escaping: Securing Output

For security on the other end of the spectrum, we have escaping. To escape is to take the data you may already have and help secure it prior to rendering it for the end user. WordPress thankfully has a few helper functions we can use for most of what we’ll commonly need to do:

esc_html() we should use anytime our HTML element encloses a section of data we’re outputting.

</pre>
<h4><!--?php echo esc_html( $title ); ?--></h4>
<pre>

esc_url() should be used on all URLs, including those in the ‘src’ and ‘href’ attributes of an HTML element.

<img alt="" src="<?php echo esc_url( $great_user_picture_url ); ?>" />

esc_js() is intended for inline Javascript.

var value = '<?php echo esc_js( $value ); ?>';

esc_attr() can be used on everything else that’s printed into an HTML element’s attribute.

<ul class="<!--?php echo esc_attr( $stored_class ); ?-->">

It’s important to note that most WordPress functions properly prepare the data for output, and you don’t need to escape again.

<h4><?php the_title(); ?></h4>

Conclusion

To recap: Follow the whitelist philosophy with data validation, and only allow the user to input data of your expected type. If it’s not the proper type, discard it. When you have a range of data that can be entered, make sure you sanitize it. Escape data as much as possible on output to avoid XSS and malformed HTML.

Take a look through the Data Validation Codex page  to see all of the sanitization and escaping functions WordPress has to offer.

Using A Shared/Common Theme Across Multiple Sites

Do you have multiple themes that are basically the same? Right now if you want to make a change to all of your sites, you likely have to commit the same code to each theme repository. This is time-consuming and requires more effort on both your and our part. By using a shared theme, changes made to one site can, at your discretion, be applied to all of your sites.

There are various techniques to implement a common theme.

Code-level Configuration

The trick is to replace any per-theme code with some code that is wrapped in conditional tags. Many of our VIPs create configuration arrays in their functions.php and then switch to the different configurations based on the value of home_url(). Here’s a quick example on how to approach this:

add_action( 'init', 'my_init_site', 1 ); // let's initialize our settings very early on

/**
 * Initilize settings based on the site url
 */
function my_init_site() {
	global $my_settings;

	$my_settings = array();

	switch ( parse_url( home_url(), PHP_URL_HOST ) ) {
		case 'cooltechreviewsite.com':
			$my_settings['twitter_username'] = 'cooltechreviewsite';
			break;
		case 'coolfoodiesite.com':
			$my_settings['twitter_username'] = 'coolfoodiesite';
			break;
	}
}

/**
 * Function gets a setting by name
 */
function my_get_site_setting( $name, $default_value = '' ) {
	global $my_settings;
	return isset( $my_settings[$name] ) ? $my_settings[$name] : $default_value;
}

function my_twitter_link() {
	$my_twitter_username = my_get_site_setting( 'twitter_username' );
	if( ! empty( $my_twitter_username ) )
		printf( '<a href="%s">Follow us on Twitter!</a>', esc_url( 'http://twitter.com/' . $my_twitter_username ) );
}

Other options include having a config folder with unique configurations/templates for each site:

function my_load_site_config() {
	$config_path = '';

	switch ( parse_url( home_url(), PHP_URL_HOST ) ) {
		case 'cooltechreviewsite.com':
		case 'dev.cooltechreviewsite.com':
			$config_path = __DIR__ . '/config/cooltechreviewsite.com/config.php';
			break;
		case 'coolfoodiesite.com':
		case 'qa.coolfoodiesite.com':
			$config_path = __DIR__ . '/config/coolfoodiesite.com/config.php';
			break;
	}

	if ( $config_path && file_exists( $config_path ) )
		require( $config_path );
	else
		trigger_error( 'Uh oh, config not found!' );
}

Options/Customizer Panel

A better, but more involved, solution is to use a settings page to control the per-theme settings. CheezCAP makes this easier. Going this route will allow you to change settings without having to commit code changes and is especially useful on a large network of sites.

You can also integrate this into the Theme Customizer to provide a real-time view of the options as they are updated.

Parent-Child Theme

In some cases, it may not be feasible to have the same theme work for all your sites (e.g. unique template requirements). If you do, the parent-child theme approach might make sense. The idea here is that you try to encapsulate as much of the functionality into the parent (sometimes called the “master”) and have child themes for individual sites to override things as necessary. Note that if you are planning for a large number of sites and themes, this is not a good solution.

Shared Plugins

If you can’t easily follow the common theme approach and are maintaining several themes, it might still sense to abstract shared functionality into plugins that be shared across these theme via a shared plugins repo. The idea here is that each theme will unique templates but will share the larger functional bits via plugins.