List Posts By A Co-Author

When using Co-Authors Plus, you may want to create a listing of all posts by a given co-author. Co-Authors Plus deviates slightly from how core WordPress works in that it stores byline information in a custom ‘author’ taxonomy. It works in this manner because a post (let it be a Post, Page, or Custom Post Type) can have multiple taxonomy terms associated with it.

To list some or all posts from a co-author, you’ll want to create a new WP_Query object based on the author term for a given user or guest author. Here’s an example of what that might look like:

// Get the author term based on a variable called $user_login
global $coauthors_plus;
$coauthor = $coauthors_plus->get_coauthor_by( 'user_login', $user_login );
$coauthor_term = $coauthors_plus->get_author_term( $coauthor );

// Build the query arguments
$args = array(
	'post_type' => 'post',
	'posts_per_page' => 10,
	'post_status' => 'publish',
	'tax_query' => array(
		array(
			'taxonomy' => 'author',
			'field' => 'slug',
			'terms' => $coauthor_term->slug,
		),
	),
);
$author_query = new WP_Query( $args );

if ( $author_query->have_posts() ) :
	while ( $author_query->have_posts() ) : $author_query->the_post();

	// Do your presentation

	endwhile;
endif;

Customizing Publicize Sharing Behavior

Publicize makes it easy to automatically share WordPress.com posts on Facebook, Twitter, Tumblr, LinkedIn, and Yahoo!

WordPress.com VIP clients can customize which shortlink Publicize uses, when Publicize shares to which accounts, and other behavior changes using standard filters.

Conditionally allow services or connections to be utilized

Publicize includes an extremely helpful wpas_submit_post? filter for determining whether a given post should be submitted to a given service or connection. It looks like this:

$submit_post = apply_filters( 'wpas_submit_post?', true, $post_id, $service_name, $connection_data ) )

$service_name will be a value like ‘twitter’ or ‘facebook’, and $connection_data will have metadata about the connection.

Here’s how you might reserve a “@vipwpnews” Twitter account exclusively for posts in the “WordPress” category:

add_filter( 'wpas_submit_post?', 'vipx_wpas_submit_post', 10, 4 );
function vipx_wpas_submit_post( $ret, $post_id, $name, $connection ) {

	$categories = get_the_terms( $post_id, 'category' );
	if ( 'twitter' == $name && '@vipwpnews' == $connection->meta['display_name'] ) {

		if ( is_array( $categories ) )
			$categories = wp_list_pluck( $categories, 'slug' );

		if ( empty( $categories ) || ! in_array( 'wordpress', $categories ) )
			$ret = false;

	}
	return $ret;
}

Note: This filter is applied both when the post is published and in the post metabox to determine which services should appear as available to the writer or editor. It will also let authors add their own services.

Add a checkbox to send new post updates to Publicize

When liveblogging or making frequent updates to a post, you may want to send multiple updates to Publicize.

Publicize’s default behavior is to only syndicated a post once. You can add a checkbox for writers or editors to publicize a post a second time using this code snippet:

add_action( 'publicize_form', 'vipx_filter_publicize_form' );
function vipx_filter_publicize_form( $form ) {
	global $post;

	// Only add the checkbox if the post was already published
	if ( 'publish' != $post->post_status )
		return $form;

	$checkbox = '<label for="vipx-republicize"><input type="checkbox" id="vipx-republicize" name="vipx-republicize" /> Re-Publicize Post?</label>';

	$form .= $checkbox;

	return $form;
}

add_action( 'save_post', 'vipx_action_save_post_republicize', 5 );
function vipx_action_save_post_republicize( $post_id ) {

	if ( isset( $_POST['vipx-republicize'] ) ) {
		update_post_meta( $post_id, '_publicize_pending', true );
		add_filter( 'wpas_force_publicize', '__return_true' );
	}
}

The message shared to the service will be whatever is in the Publicize textarea.

Implement your own URL-shortening service

By default the Publicize feature uses wp.me as a URL-shortening service. You can also use any other URL-shortening service that can return a shortened URL.

The easiest way is to filter pre_get_shortlink or get_shortlink (called by wp_get_shortlink). For an example, check out the bit.ly plugin.

The following filter is available to alter the URL sent in your update:

$url = apply_filters( 'wpas_post_url', wp_get_shortlink( $post->ID, true ), $post->ID, $this->slug );

Altering the default messages

It is also possible to change the default message that is created with the following filters:

apply_filters( 'wpas_default_message', $this->default_message, $this->slug )

 

apply_filters( 'wpas_default_prefix', $this->default_prefix, $this->slug )

 

apply_filters( 'wpas_default_suffix', $this->default_suffix, $this->slug )

These filters are used in the following context:

$title = sprintf( $this->default_prefix, $url ) . sprintf( $this->default_message, $post->post_title, $url ) . sprintf( $this->default_suffix, $url );

and allow the wildcards '%title%', %url% within the default_message/prefix/suffix variables.

You can also alter the message that is sent to Twitter / Facebook with a simple function attached to the following filter to Twitter:

$status_update = apply_filters( 'wpas_submit_twitter', array( 'status' => $title ), $object );

and to Facebook:

$status_update = apply_filters( 'wpas_submit_facebook', array( 'status' => $title ), $object );

Configure Ad Code Manager to manage the advertisements on your site

Ad Code Manager is a VIP-sponsored plugin designed to make it easier to manage the ad codes used to display advertisements on your site. There’s a little bit of work you’ll need to do up front, however, in order to integrate Ad Code Manager with your theme.

Note: WordPress.com Enterprise users should contact support and we’ll take care of the configuration for you.

The high-level idea behind Ad Code Manager is that it gives non-developers an admin interface to manage your ad codes. It then permits users to (optionally) target specific ad codes using conditionals like is_home() and is_single(). Ad codes are associated with positions in the theme through the use of ad tags.

Currently, Ad Code Manager easily integrates with Google Doubleclick For Publishers Async and Google AdSense. Other ad providers are supported with additional configuration.

Google AdSense and DoubleClick For Publishers Async

Let’s use AdSense as our first example. You’ll want to incorporate some of the default ad tags into your theme by use of do_action(). Here’s an example you might put in your header.php file:

do_action( 'acm_tag', '728x90_leaderboard' );

Once you’ve done so, you can select the “Google AdSense” provider in the admin. Ad codes can be registered against ad tags (positions) by choosing the ad tag from the drop down, entering the tag ID and publisher ID, and hitting “Add New Ad Code”.

Google AdSense configuration

And like that, your 728×90 leaderboard will appear on your site.

The Google AdSense configuration comes with many of Google’s suggested sizes. Additional ad tags can be registered by the way of filtering:

add_filter( 'acm_ad_tag_ids', 'acmx_filter_ad_tag_ids' );
function acmx_filter_ad_tag_ids( $ids ) {

	$ids[] = array(
		'tag'       => '100x100_smallsquare',
		'url_vars'  => array(
			'tag'       => '100x100_smallsquare',
			'height'    => '100',
			'width'     => '100',
			),
		'enable_ui_mapping' => true,
	);

	return $ids;
}

Keep in mind that you’ll still need to incorporate a do_action( 'acm_tag', '100x100_smallsquare' ); in your theme in order to display the ad tag.

If you choose Google DFP Async as your provider, you’ll likely need to register additional ad tags, as we only package two default ad tags.

Custom Ad Provider Implementations

As mentioned previously, other ad code providers are supported with additional configuration. Here’s an example of the different filters you would use to configure the older version of Google Doubleclick For Publishers:

<?php
/**
 * Define the default URL to be used when rendering ad codes
 */
add_filter( 'acm_default_url', 'acmx_filter_default_url' ) ;
function acmx_filter_default_url( $url ) {
	if ( 0 === strlen( $url )  ) {
		return "http://ad.doubleclick.net/adj/%site_name%/%zone1%;s1=%zone1%;s2=;pid=%permalink%;fold=%fold%;kw=;test=%test%;ltv=ad;pos=%pos%;dcopt=%dcopt%;tile=%tile%;sz=%sz%;";
	}
}

/**
 * Whitelist the DFP URL to be used in ad tags. The whitelist
 * helps prevent execution of arbitrary scripts
 */
add_filter( 'acm_whitelisted_script_urls', 'acmx_filter_whitelisted_script_urls');
function acmx_filter_whitelisted_script_urls( $whitelisted_urls ) {
	$whitelisted_urls = array( 'ad.doubleclick.net' );
	return $whitelisted_urls;
}

/**
 * Define the different ad tags (locations) you'd like to use in your theme
 */
add_filter( 'acm_ad_tag_ids', 'acmx_ad_tags_ids' );
function acmx_ad_tags_ids( $ad_tag_ids ) {
	return array(
			array(
					'tag' => '728x90-atf',
					'url_vars' => array(
						'sz' => '728x90',
						'fold' => 'atf',
						'width' => '728',
						'height' => '90',
				)
			),
			array(
					'tag' => '728x90-btf',
					'url_vars' => array(
						'sz' => '728x90',
						'fold' => 'btf',
						'width' => '728',
						'height' => '90',
				)
			) ,
			array(
					'tag' => '300x250-atf',
					'url_vars' => array(
						'sz' => '300x250',
						'fold' => 'atf',
						'width' => '300',
						'height' => '250',
				)
			),
			array(
					'tag' => '300x250-btf',
					'url_vars' => array(
						'sz' => '300x250',
						'fold' => 'btf',
						'width' => '300',
						'height' => '250',
				)
			),
			array(
					'tag' => '160x600-atf',
					'url_vars' => array(
						'sz' => '160x600',
						'fold' => 'atf',
						'width' => '160',
						'height' => '600',
				)
			),
			array(
					'tag' => '1x1',
					'url_vars' => array(
						'sz' => '1x1',
						'fold' => 'int',
						'pos' => 'top',
					)
			)
		);	
}

/**
 * Register the full <script> output to use with each ad tag
 */
add_filter( 'acm_output_html','acmx_filter_output_html', 5, 2 );
function acmx_filter_output_html( $output_html, $tag_id ) {
	$output_html = '<!-- DFP %pos% %sz% ad tag --> 
	<script language="JavaScript" type="text/javascript">
if (typeof ord==\'undefined\') {ord=Math.random()*10000000000000000;}
if (typeof(dfp_tile) == \'undefined\') dfp_tile=%tile%;
document.write(\'<script language="JavaScript" src="%url%ord=\' + ord + \'?" type="text/javascript"><\/script>\');
</script><noscript><a href="%url%ord=%random%?" target="_blank"><img src="%url%ord=%random%?" width="%width%" height="%height%" border="0" alt=""></a></noscript>
<!-- //DFP %pos% %sz% tag -->';
	return $output_html;
}

/**
 * Fine tune our output tokens
 * This is the real example of how easily you can modify output
 * depending on your ad network specs
 */
add_filter('acm_output_tokens', 'acmx_filter_output_tokens', 5, 3 );
function acmx_filter_output_tokens( $output_tokens, $tag_id, $code_to_display ) {
	global $dfp_tile;
	global $dfp_ord;
	global $dfp_pos;
	global $dfp_dcopt;
	global $wp_query;
	
	// We can't really rely on get_permalink() so use $_SERVER['REQUEST_URI] as bulletproof solution for generating unique pids
	$link = strlen( $_SERVER['REQUEST_URI'] ) > 1 ? sanitize_key( $_SERVER['REQUEST_URI'] ) : home_url();
	$output_tokens['%permalink%'] = str_replace( array( '/',':', '.' ), "", $link ); 
	$output_tokens['%random%'] = $dfp_ord;
	$output_tokens['%tile%'] = ++$dfp_tile;
	if (  false === $dfp_pos[ $code_to_display['url_vars']['sz'] ] ) {
		$output_tokens['%pos%'] = 'top';
		$dfp_pos[ $code_to_display['url_vars']['sz'] ] = true;
	} else {
		$output_tokens['%pos%'] = 'bottom';
	}
	if ( ! $dfp_dcopt ) {
		$output_tokens['%dcopt%'] = 'ist';
		$dfp_dcopt = true;
	} else {
		$output_tokens['%dcopt%'] = '';
	}
	
	$output_tokens['%test%'] = isset( $_GET['test'] ) && $_GET['test'] == 'on' ? 'on' : '';
	
	return $output_tokens;
}

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'] = ''; 
	
	$options['extended_valid_elements'] .= ',div[contenteditable|class|id|style]'; 
	return $options; 
}

Remove the slug from your custom post type permalinks

WordPress easily lets you use just the post_name value for Posts and Pages, without any performance impacts, thanks to some clever code in core. To achieve the same effect with a custom post type, you’ll need to use some trickery.

First, you’d register your custom post type as though you want to keep the slug in the URI:

/**
 * Register a custom post type but don't do anything fancy
 */
register_post_type( 'event', array( 'label' => 'Event', 'public' => true ) );

Next, we want to filter the permalink for our custom post type such that all published posts don’t have the slug in the URI:

/**
 * Remove the slug from published post permalinks. Only affect our CPT though.
 */
function vipx_remove_cpt_slug( $post_link, $post, $leavename ) {

	if ( ! in_array( $post->post_type, array( 'event' ) ) || 'publish' != $post->post_status )
		return $post_link;

	$post_link = str_replace( '/' . $post->post_type . '/', '/', $post_link );

	return $post_link;
}
add_filter( 'post_type_link', 'vipx_remove_cpt_slug', 10, 3 );

At this point, you’ll hit a 404 if you try to view the link. That’s because WordPress only knows the URI can be Posts or Pages. We need to tell it to also pay attention to our custom post type. Here’s how:

/**
 * Some hackery to have WordPress match postname to any of our public post types
 * All of our public post types can have /post-name/ as the slug, so they better be unique across all posts
 * Typically core only accounts for posts and pages where the slug is /post-name/
 */
function vipx_parse_request_tricksy( $query ) {

	// Only noop the main query
	if ( ! $query->is_main_query() )
		return;

	// Only noop our very specific rewrite rule match
	if ( 2 != count( $query->query )
		|| ! isset( $query->query['page'] ) )
		return;

	// 'name' will be set if post permalinks are just post_name, otherwise the page rule will match
	if ( ! empty( $query->query['name'] ) )
		$query->set( 'post_type', array( 'post', 'event', 'page' ) );
}
add_action( 'pre_get_posts', 'vipx_parse_request_tricksy' );

Voila! Custom post type permalinks without the slug.

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. s0.wp.com), you may run into cross-domain issues in certain browsers (like Firefox). The typical workaround is to send an “Access-Control-Allow-Origin” header from the CDN domain, but for improved security this is not enabled on WP.com.

As an alternate option, we recommend including the base64 version of your fonts in a CSS file as described here by Geoff Evason:

  1. Go to fontsquirrel.com and download the font-face kit you want to use.
  2. Go to the fontsquirrel font-face generator and upload the .ttf file from the kit you just downloaded.
  3. Select the ‘Expert’ option.
  4. For format select ‘TrueType’, ‘EOT’, and ‘SVG’. (Woff is a compressed format only supported by Firefox, but Firefox also support .ttf, so woff is extraneous)
  5. Under the CSS options select ‘Base64′ Encoding.
  6. Download and use the files provided.

robots.txt

WordPress.com 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: *n" . PHP_EOL;
	echo "Disallow: /path/to/your/directory/n" . PHP_EOL;
}
add_action( 'do_robotstxt', 'my_disallow_directory' );

Caching

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.

Custom Fields

The custom fields UI isn’t included on WordPress.com — 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 Easy Custom Fields.

Customizing Invites

On WordPress.com, new users need to be invited to your site. The default behavior for invitations is to allow any WordPress.com 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 WordPress.com user’s email address:

wpcom_invite_force_matching_email_address();

Infinite Scroll

Some of the standard themes on WordPress.com support infite 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 http://raanan.com or http://matt.wordpress.com.

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

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.