blainehansen

Skiracing Sponsored Posts

A system allowing the skiracing.com team to mark their articles with a sponsor.

published: December 2, 2016 - last updated: May 13, 2024

The Skiracing.com team needed the ability to mark certain of their posts as Sponsored, both for ethical reasons of journalistic transparency, as well as to allow the sponsor in question to receive attention and clicks.

The needed behavior was for a box to appear on the post editing page allowing the team to select one or more sponsor brands for a post, and then for the post to show the logos of each of those brands, enabled as links to the brands' websites, in the header of the article. The fact that the post was sponsored also needed to be indicated on index pages listing the article.

The first task was to allow the indication on the post edit page.

This block simply adds the meta box in the correct place to the edit page for the correct post types.

add_action( 'add_meta_boxes', 'sponsored_article_box' );
function sponsored_article_box() {
	add_meta_box(
		'sponsored_article_box',	// this is HTML id of the box on edit screen
		'Sponsor Brands',	// title of the box
		'sponsored_article_box_content',	// function to be called to display the checkboxes, see the function below
		['post', 'premium', 'stories', 'gallery'],        // on which edit screen the box should appear
		'side',	// part of page where the box should appear
		'low'	// priority of the box
	);
}

Then a function outputting the box content. I created a custom post type called Sponsor Brands for the team to have a list of the sponsors they could add, allowing their logos and links to reside in one place. Then this meta box simply outputs a list of the potential options, making the checkbox of each true if the database indicates that brand has already been added.

An option was also added to mark a post as sponsored, but leave its brand unspecified.

// display the metabox
function sponsored_article_box_content( $post ) {
	// nonce field for security check
	// nonce field for all your meta boxes of same plugin
	wp_nonce_field( plugin_basename( __FILE__ ), 'sr_sponsor_brands_nonce' );

	global $sr_sponsored_articles_unspecified_id;
	$post_id = $post->ID;

	// get the list of brands that could potentially be added as sponsors for this post
	$brand_query = new WP_Query( array('post_type' => 'sponsor-brand', 'posts_per_page' => -1) );
	$brands = $brand_query->get_posts();

	// get id of all current sponsors, if any
	// use this array to checkmark any brands that are currently added as sponsors
	// this might be empty or null, and that's okay because that means this post doesn't have any sponsorship data attached to it
	$current_sponsor_id_array = get_post_meta($post_id, 'sr_sponsor_brands', true);

	// this simply checks if a general "sponsored but unspecified" id is the only thing
	// NOTE: I didn't include this helper function in my description since it would just be clutter
	$sponsored_checked = array_only_contains_unspecified_id($current_sponsor_id_array) ? 'checked' : '';
	echo '<ul><li><input id="dummy_brand" type="checkbox" name="sr_sponsor_brands[]" value="' . $sr_sponsored_articles_unspecified_id . '" ' . $sponsored_checked . '/> Sponsored, but Brand Unspecified</li></ul>';

	// go through a loop, outputting the boxes, checking the current id at each one and marking the box
	echo '<ul>';
	foreach($brands as $brand) {
		$sponsored_checked = in_array($brand->ID, $current_sponsor_id_array) ? 'checked' : '';
		echo '<li><input class="sponsor_brand" type="checkbox" name="sr_sponsor_brands[]" value="' . $brand->ID . '" ' . $sponsored_checked . '/> ' . $brand->post_title . '</li>';
	}
	echo '</ul>';
}

This jQuery prevented the "Sponsored but Unspecified" from being clicked in addition to any other values.

// if the dummy box is checked, uncheck others
$("#dummy_brand").change(function() {
	if ($(this).is(":checked")) {
		$(".sponsor_brand").each(function() {
			$(this).prop('checked', false);
		});
	}
});

// if any of the other boxes are checked, uncheck dummy
$(".sponsor_brand").change(function() {
	if ($(this).is(":checked")) {
		$("#dummy_brand").prop('checked', false);
	}
});

Then of course we need to ensure this data is saved correctly when the post is saved.

// save data from checkboxes
add_action( 'save_post', 'sponsored_article_box_save' );
function sponsored_article_box_save( $post_id ) {
	// check if this isn't an auto save
	if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
		return;

	// security check
	if ( !wp_verify_nonce( $_POST['sr_sponsor_brands_nonce'], plugin_basename( __FILE__ ) ) )
		return;

	// now store data in custom fields based on checkboxes selected
	$result = update_post_meta( $post_id, 'sr_sponsor_brands', $_POST['sr_sponsor_brands'] );
}

And finally, functions to output the information in the templates.

Different "modes" were allowed by the header function, since it could be used in many different contexts and needed to be aware of which one it was in for styling purposes.

function sponsored_article_tag($post_id, $red = false) {
	$sponsored_article = get_post_meta($post_id, 'sr_sponsor_brands', true);

	if ($sponsored_article) {
		$color_mode = $red ? 'red' : 'blue';
		return '<span class="sponsored_article_tag"><img class="sponsored_article_tag_image" src="' . get_template_directory_uri() . '/img/handshake_icon.' . $color_mode . '.png" alt="Sponsored Article"></span>';
	}
}

function sponsored_article_header($post_id, $mode = '', $red = false) {
	global $sr_sponsored_articles_unspecified_id;

	$sponsor_brand_id_array = get_post_meta($post_id, 'sr_sponsor_brands', true);
	if (empty($sponsor_brand_id_array)) {
		return;
	}

	if (array_only_contains_unspecified_id($sponsor_brand_id_array)) {
		return '<div class="sponsor_brand ' . $mode . '"><span class="sponsor_brand_left">Sponsored Content ' . sponsored_article_tag(null, $red) . '</span></div>';
	}

	$give_string = '<div class="sponsor_brand ' . $mode . '"><span class="sponsor_brand_left">Sponsored Content ' . sponsored_article_tag(null, $red) . '</span>';

	$give_string .= '<span class="sponsor_brand_right">Presented by: ';
	foreach ($sponsor_brand_id_array as $brand_id) {
		$brand = get_post($brand_id);
		$image_src = wp_get_attachment_image_src( get_post_thumbnail_id($brand_id), 'post')[0];
		$imageTag = '<img class="sponsor_brand_logo" src="' . $image_src . '" alt="' . $brand->post_title . '"/>';

		$brandWebsite = get_post_meta($brand_id, 'company_website', true);
		if ($brandWebsite) {
			$give_string .= '<a target=_blank href="' . $brandWebsite . '">' . $imageTag . '</a>';
		}
		else {
			$give_string .= $imageTag;
		}

	}

	$give_string .= '</span></div>';

	return $give_string;
}

Completing this of course included scss styling work, but there's nothing terribly interesting about any of it.

This project required deeply diving into the wordpress api to add functionality to the core post editing system. Overall I had to do quite a bit of reading, but I dug up the answers and the system works great.

Want to hear from me in the future?

Usual fine print, I won't spam you or sell your info.
Thank you! Come again.