Installing and activating plugins on VIP Go

Before reading this, it will be useful to read our documentation on your VIP Go codebase.

This is the preferred approach for installing and activating plugins for VIP Go:

  • Add the plugins to your plugins directory
  • Activate the plugins via a plugin loader file in client-mu-plugins

Plugins can also be activated via the “Plugins” screen on your WordPress admin area.

If you are familiar with developing for WordPress.com hosted VIP sites, you no longer need to bundle your custom plugins with your theme and we would encourage installing to the plugins directory in your site’s repository.

Installing plugins

On VIP Go, all plugins must be installed by adding them to your Git version control repository, hosted on GitHub. Having the plugin code in git version control allows multiple developers in your own team, and of course in the VIP team, to work on your code together, maintain and improve the site, and debug issues. On VIP Go, we deploy the code from git, which also allows us to roll back to a previous “known good” state, if the site encounters issues. (Please note, adding plugins via the WordPress admin area is not supported.)

As the VIP Go workflow is Git-based, you can also reference submodules in your repository which will make maintaining third-party code easier. VIP Go currently only supports public submodules, e.g. you cannot submodule a private GitHub repository or a Git repository hosted elsewhere which requires authentication.

Note that some plugin repositories, such as that for AMP for WordPress, contain unbuilt versions whereby the command, composer install, needs to be run for the plugin to work properly. Repositories like this one cannot be referenced as submodules as we do not run these build commands on the server. In such cases, developers should use one of the other methods outlined on this page.

Installing to the plugins directory

On VIP Go, there is a dedicated plugins directory in the root of the site git repository; this directory works similarly to WP_CONTENT_DIR . '/plugins/' in a self-hosted WordPress installation. Installing plugins to this location is a matter of adding the plugin to a unique directory in plugins, e.g. plugins/my-plugin, commit, then push to GitHub (see our documentation on how code is deployed on VIP Go).

We recommend that third-party plugins always go in the plugins directory, to allow for better compatibility with systems (like security-related plugin updates) that expect plugins to be in a specific location. Custom plugins can go in either the plugins or client-mu-plugins directory, depending on the preferred loading behaviour.

Loading or activating plugins in the plugins directory

The plugins in the plugins directory can be activated and deactivated from the standard Plugins menu in the WordPress admin area, however, we recommend loading your plugins from code. Loading plugins in code results in more control and a greater consistency across your production, non-production environments, and local development environments.

If loading your plugins via code, you should use a plugin loader file in client-mu-plugins (example) using the wpcom_vip_load_plugin() function:

wpcom_vip_load_plugin( 'plugin-name' );
// Note this requires a specific naming structure: /plugin-name/plugin-name.php
// You can also specify a specific root file: wpcom_vip_load_plugin( 'plugin-name/plugin.php' );

Installing to the client-mu-plugins directory

On VIP Go, there is a dedicated client-mu-plugins directory in the root of the site git repository; this directory works similarly to WP_CONTENT_DIR . '/mu-plugins/' in a self-hosted WordPress installation.

If you want your plugins to be installed as MU (“must use”) plugins then you can place them in the client-mu-plugins directory. We strongly recommend that only custom (and not third-party) plugins go in the client-mu-plugins directory, depending on the preferred loading behaviour. Specifically, client-mu-plugins should be for any code that should be auto-loaded or run earlier in the WordPress load process.

MU plugins behave differently than plugins installed in the plugins directory, so be sure to read up on the pros and cons and make a measured choice. If you have any questions, we’re happy to help.

Note that plugins contained within a directory in the client-mu-plugins directory will need a “loader” file in the root of this directory to require the main plugin file. The contents of the loader file can be as simple as:

<?php
require WPCOM_VIP_CLIENT_MU_PLUGIN_DIR . '/my-plugin/my-plugin.php';

When do plugins run?

Depending on how the plugin is loaded, it will hook into the WordPress load order at different points (from latest to earliest):

  • Manually activated from the wp-admin Plugins screen (supported, but code activation is preferred): before the plugins_loaded hook
  • wpcom_vip_load_plugin( 'plugin-name' ) in your theme functions.php (supported, but code activation from client-mu-plugins is preferred): before the after_setup_theme hook
  • wpcom_vip_load_plugin( 'plugin-name' ) from client-mu-plugins (recommended): on the muplugins_loaded hook

Sometimes plugins may have dependencies on running before specific hooks, in order to hook on very early actions or filters you will need to load from client-mu-plugins.

Version updates and maintenance

We recommend that you always run the latest version of each of the plugins on your site, however, this is not a requirement as we recognise that different development teams run different maintenance and patch cycles.

Automatically generated Pull Requests from our bot

The VIP team maintains a bot, wpcomvip-bot, to generate pull requests updating some plugins and themes. We do not currently have full coverage of all plugins used by all VIPs and the bot generated PRs are offered as a convenience; it remains the responsibility of individual VIP client developer teams to maintain the plugins used for their sites.

Remaining consistent with standard VIP practice, PRs will not be merged on your behalf unless they contain critical security fixes or resolve an existing or potential site outage. We recommend you merge the code into a development and/or non-production environments and test there before deploying to production.

Security fixes

Where the VIP team become aware of a security update, we will issue and merge PRs for affected production sites as soon as we can.

Featured partner plugins

On VIP Go, our Featured Partner plugins are installed and maintained the same as other plugins, read the documentation above and follow the directions. If you have any questions, please get in touch.

ACF 5 and VIP Go

Advanced Custom Fields (ACF) is a popular plugin that many VIP clients choose to use on their sites. However, ACF is not available for clients on the WordPress.com VIP platform, and has not undergone a full, line-by-line review for use on VIP Go. Clients wishing to use ACF on VIP Go accept the security and performance risks of using it. This page outlines some additional steps needed to make ACF more secure and performant, but should not be interpreted as the equivalent of a line-by-line review, or VIP’s approval of the plugin.

Please note that while ACF 5 can be used on most VIP Go sites, ACF 4 is unavailable. If in doubt about whether ACF is allowed for use on your VIP site, please contact us.

When using ACF 5 and ACF 5 Pro, several additional steps are needed in order to make ACF secure, and avoid performance issues:

  • Hide the Admin UI
  • Define fields in PHP
  • Use taxonomies for searchable fields
  • Avoid the_field and escape
  • Secure fields that allow arbitrary output

Hide the ACF Admin UI

The fields UI can be used to add arbitrary fields, including unsafe fields. For example it can allow an admin to display the passwords of users. You can disable the UI using this filter:


add_filter('acf/settings/show_admin', '__return_false');

Define Fields in PHP

In order to make sure that all ACF usage is secure, define the fields in PHP or local json, rather than at runtime. This way they remained versioned and safe. This can be done via the import export menu of a local developer environment to setup the fields available and export them to PHP.

Documentation on how to do this can be found here on the ACF website.

Alternatively, fields can be defined via the local JSON feature as described here, but keep in mind that saving local JSON will not work in production as the filesystem is read only, nor is it desirable as it would bypass the security benefits.

Being Mindful of Taxonomy Term Storage

If an ACF field is going to be queried, filtered, or searched for in a post query, use the taxonomy data checkbox so that the field is stored as a term, not a post meta value. This ensures performance is not impacted by expensive meta queries on the frontend

the_field and Escaping

the_field has no context as to when or where it is called. So how is it to know if it should be using esc_url, esc_attr or wp_kses_post? It doesn’t, which makes it dangerous from a security point of view. Instead, use get_field in combination with an escaping function, e.g.

$url = get_field( 'custom_link' );
echo esc_url( $url );

Flexible content is the exception to this, and should be clearly marked on usage via comments

Fields That Use Arbitrary Output

If the field types that allow arbitrary output are to be used, they must be accounted for in the acf/format_value and equivalent filters such as acf/format_value/type=textarea.

For example:

function vip_make_acf_text_areas_safe( $value, $post_id, $field ) {
	return wp_kses_post( $value );
}

add_filter('acf/format_value/type=textarea', 'vip_make_acf_text_areas_safe', 10, 3);

This way different escaping can be applied via different `$field` values. Alternatively, if all fields of that type use the same escaping, this can be done instead:

add_filter('acf/format_value/type=textarea', 'wp_kses_post', 10, 1);

For more information, see: https://www.advancedcustomfields.com/resources/acf-format_value/

Incorporate Co-Authors Plus template tags into your theme

Co-Authors Plus is a plugin maintained by WordPress.com VIP which makes it easy to assign one or more bylines to a post. Upon activation, you’ll be able to start assigning multiple bylines right away. In order for those bylines to appear on the frontend, you may need to make some small modifications to your theme.

Available Template Tags

WordPress offers template tags like the_author() and the_author_posts_link() to display byline information associated with each post post. You might see these inside files like single.php and author.php

Co-Authors Plus has similar template tags for displaying multiple bylines. These are located in the template-tags.php file. The most relevant are:

/**
 * Outputs the co-authors display names, without links to their posts.
 * Co-Authors Plus equivalent of the_author() template tag.
 *
 * @param string $between Delimiter that should appear between the co-authors
 * @param string $betweenLast Delimiter that should appear between the last two co-authors
 * @param string $before What should appear before the presentation of co-authors
 * @param string $after What should appear after the presentation of co-authors
 * @param bool $echo Whether the co-authors should be echoed or returned. Defaults to true.
 */
function coauthors( $between = null, $betweenLast = null, $before = null, $after = null, $echo = true ){
	return coauthors__echo('display_name', 'field', array(
		'between' => $between,
		'betweenLast' => $betweenLast,
		'before' => $before,
		'after' => $after
	), null, $echo );
}

/**
 * Outputs the co-authors display names, with links to their posts.
 * Co-Authors Plus equivalent of the_author_posts_link() template tag.
 *
 * @param string $between Delimiter that should appear between the co-authors
 * @param string $betweenLast Delimiter that should appear between the last two co-authors
 * @param string $before What should appear before the presentation of co-authors
 * @param string $after What should appear after the presentation of co-authors
 * @param bool $echo Whether the co-authors should be echoed or returned. Defaults to true.
 */
function coauthors_posts_links( $between = null, $betweenLast = null, $before = null, $after = null, $echo = true ){
	return coauthors__echo('coauthors_posts_links_single', 'callback', array(
		'between' => $between,
		'betweenLast' => $betweenLast,
		'before' => $before,
		'after' => $after
	), null, $echo );
}

/**
 * Outputs the co-authors display names, with links to their websites if they've provided them.
 *
 * @param string $between Delimiter that should appear between the co-authors
 * @param string $betweenLast Delimiter that should appear between the last two co-authors
 * @param string $before What should appear before the presentation of co-authors
 * @param string $after What should appear after the presentation of co-authors
 * @param bool $echo Whether the co-authors should be echoed or returned. Defaults to true.
 */
function coauthors_links($between = null, $betweenLast = null, $before = null, $after = null, $echo = true ) {
	return coauthors__echo('coauthors_links_single', 'callback', array(
		'between' => $between,
		'betweenLast' => $betweenLast,
		'before' => $before,
		'after' => $after
	), null, $echo );
}

Each of these template tags will present different aspects of the multiple bylines. For instance, the first will display the first and last name of each co-author without any links. The second will display the first and last name of co-author linking back to their author profile page. And so on.

Integrating Template Tags Into Your Theme

To integrate Co-Authors Plus, you’ll want to replace existing author template tags in your theme with a simple conditional that uses the Co-Authors Plus template tags if Co-Authors Plus is available. The conditional prevents your site from breaking (e.g. white screen of death) if Co-Authors Plus isn’t activated.

For example, here’s how you would update the_author_posts_link() to instead use coauthors_posts_links():

if ( function_exists( 'coauthors_posts_links' ) ) {
    coauthors_posts_links();
} else {
    the_author_posts_link();
}

However, the example above is a relatively simplistic way of presenting bylines. There’s a good chance your theme will need an adaptation of it.

For instance, here’s how the change looks for the Hybrid theme:

function hybrid_entry_author_shortcode( $attr ) {
	$attr = shortcode_atts( array( 'before' =&gt; '', 'after' =&gt; '' ), $attr );
	if ( function_exists( 'coauthors_posts_links' ) ) {
		$author = coauthors_posts_links( null, null, null, null, false );
	} else {
		$author = '<span class="author vcard"><a class="url fn n" href="' . esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ) . '" title="' . esc_attr( get_the_author_meta( 'display_name' ) ) . '">' . get_the_author_meta( 'display_name' ) . '</a></span>';
	}
	return $attr['before'] . $author . $attr['after'];
}

Here’s the new function for TwentyTen:

if ( ! function_exists( 'twentyten_posted_on' ) ) :
/**
 * Integrate Co-Authors Plus with TwentyTen by replacing twentyten_posted_on() with this function
 */
function twentyten_posted_on() {
	if ( function_exists( 'coauthors_posts_links' ) ) :
		printf( __( '<span class="%1$s">Posted on</span> %2$s <span class="meta-sep">by</span> %3$s', 'twentyten' ),
			'meta-prep meta-prep-author',
			sprintf( '<a href="%1$s" title="%2$s" rel="bookmark"><span class="entry-date">%3$s</span></a>',
				get_permalink(),
				esc_attr( get_the_time() ),
				get_the_date()
			),
			coauthors_posts_links( null, null, null, null, false )
		);
	else:
		printf( __( '<span class="%1$s"<Posted on</span> %2$s <span class="meta-sep">by</span> %3$s', 'twentyten' ),
			'meta-prep meta-prep-author',
			sprintf( '<a href="%1$s" title="%2$s" rel="bookmark"><span class="entry-date">%3$s</span></a>',
				get_permalink(),
				esc_attr( get_the_time() ),
				get_the_date()
			),
			sprintf( '<span class="author vcard"><a class="url fn n" href="%1$s" title="%2$s">%3$s</a></span>',
				get_author_posts_url( get_the_author_meta( 'ID' ) ),
				esc_attr( sprintf( __( 'View all posts by %s', 'twentyten' ), get_the_author() ) ),
				get_the_author()
			)
		);
	endif;
}
endif;

Here are a couple more threads you can look at for insights:

Co-Authors Plus: Add guest bylines to your content

Co-Authors Plus is a plugin maintained by WordPress.com VIP which makes it easy to assign one or more bylines to a post. With the guest authors feature, you can assign a byline to content without having to create a corresponding WordPress account. And, if your theme is using the appropriate template tags, you can create guest bylines without any additional configuration.

Creating or Editing Guest Authors

On WordPress.com VIP, guest authors can be created and managed under “Users” -> “Guest Authors”.

Each guest author can have many of same fields a normal WordPress user would typically have, including display name, email address, and website. You can assign a featured image in order to override the avatar typically associated with the email address.

Once you’ve created your guest author, his or her byline can be assigned to a post using the normal Co-Authors Plus interface.

Details for Developers

Incorporating new profile fields

The guest authors feature of Co-Authors Plus was written with extensibility in mind. The default set of profile fields can easily be manipulated using accessible filters. If you want to place your field in one of the existing post meta boxes, it can be as simple as the following example:

Then, for use on the frontend, the new field is automatically loaded onto the $coauthor object.

Migrating users to guest authors with wp-cli

If you’re performing a migration of users to co-author guest authors, there are a couple of helpful wp-cli commands packaged with the plugin:

  • wp co-authors-plus create-guest-authors will create guest authors for all of your existing users
  • wp co-authors-plus assign-user-to-coauthor will then assign all of the posts associated with old user to your new guest author

So, the steps to migrate all of your users/authors to your WordPress.com VIP site would include:

  1. Install the Co-Authors Plus plugin on your staging site
  2. Run the create-guest-authors CLI command to create the Guest Authors automatically
  3. Perform an export from your staging site and save the resulting WXR file. The WP-CLI guest authors will be included in this export.
  4. Create a mapping CSV file for editorial users who require real logins from their login on the staging site to their login on the production site (these may or may not be be the same).
    The CSV file format is as simple as:

    johnsmith, johnsmithwp
    janesmith, janesmith
    johndoe, johndoe123
    
  5. Send us the WXR and mapping CSV, and we’ll run the import for you. While Co-Authors Plus will bring over all the bylines, we’ll use the mapping CSV to preserve the bylines for users with real logins.
  6. Have your team QA the imported authors.

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.