A metabox for your layout options

It’s no surprise I can be vocal when I am reviewing a theme. Many know that I’ll spurt out what I end up seeing in a lot of themes. There is one thing that really does drive me crazy though and that is when people copy and paste code to their theme without actually knowing how the code actually works.

One file that has really been rather popular is using a metabox in order to choose the layout of the post or page. That’s great. I love that ability but then again who doesn’t love having that control? I think what drives me bonkers is that this code is super predictable before the file is opened. The file adds two actions. One to add_meta_boxes and the second to the save_post hooks. Great! Very super simplistic and hard to mess up, right?

I created a gist with the name of the theme changed. The code is the same that I’ve seen in many other themes though. From authors who have several themes in the repo to new authors to the repo.

One of the first things that you may notice is the use of two globals.

global $post, $deargodwhy_sidebar_layout;

First there should be no reason for that and second those hooks pass the $post object. We’ll look at the first one: add_meta_boxes

Using the developer resources as a guide we see that the hook for adding metaboxes uses two arguments. The post type and the post.

do_action( 'add_meta_boxes', string $post_type, WP_Post $post )

The first one being the string of the current post type and the second being the WordPress post object. Now, what’s super cool about that is that we can now register a metabox in any post type we want and it does on every post type if we don’t pay attention to what we are doing. There is also one more hook we could potentially use in order to use a metabox in only one specified post type. That hook is add_meta_boxes_{$post_type} and the developer docs does explain it a little better. For example, if you wanted to add a metabox to only your EDD downloads, you could do something like:

add_action( 'add_meta_boxes_download', 'jmc_download_extra_metaboxes', 10 );
function jmc_download_extra_metaboxes( $post ) {
	// Do our magic here.
}

In order to now add our metabox we do need to use the function add_meta_box() so let’s take a look at how that function is used.

add_meta_box( $id, $title, $callback, $screen = null, $context = 'advanced', $priority = 'default', $callback_args = null )

Now, if you want to take a closer look at what each one of those parameters actually does then I suggest you take a look over the developer docs. It is well worth your time if you really want to understand how to create a better user experience and understand how WordPress creates those.

Another thing I wanted to look over was the save_post callback being used. The biggest issue is the use of the foreach() loop.

foreach( $deargodwhy_sidebar_layout as $field ){  
        //Execute this saving function
        $old = get_post_meta( $post_id, 'deargodwhy_sidebar_layout', true ); 
        $new = sanitize_text_field( $_POST['deargodwhy_sidebar_layout'] );
        if ( $new && $new != $old ) {  
            update_post_meta($post_id, 'deargodwhy_sidebar_layout', $new );  
        } elseif ('' == $new && $old ) {  
            delete_post_meta( $post_id,'deargodwhy_sidebar_layout',  $old );  
        } 
     }

The reason is it is creating an unnecessary loop. The only thing that needs to be checked is the value being input and to make sure that it is a valid option. A quick change to the code and we have:

if ( ! empty( $_POST['deargodwhy_sidebar_layout'] )
	&& in_array( $_POST['deargodwhy_sidebar_layout'], array( 'right-sidebar', 'no-sidebar' ), true )
	) {
	update_post_meta( $post_id, 'deargodwhy_sidebar_layout', $_POST['deargodwhy_sidebar_layout'] );
}

Knowing those things we could make changes to the file and not only will we have a better understanding of how WordPress creates metaboxes but we have a file that doesn’t pollute the $GLOBALS with info that we only use in one place. If you look at what those changes were in the diff, you will see that not a lot was changed. It only looks that way since I removed a lot of the linting errors.

My tour of creating a PHPCodeSniffer Standard

It’s no surprise I like to learn new things. I also like to read things when I can. Okay, a lot of the time I skim through things but I make an effort to learn from it.

The last couple of weeks I’ve looked at working with the PHPCodeSniffer. I installed it and never really played with it. Not until last week did I realize my version was out of date. Easy fix but something to keep in mind. The current version at the time of this writing is 2.7.1 and that’s what I am currently running on my Windows machine as well as my MBP.

So, here are some of the things I learned from attempting to create a standard and a sniff.

Lessons Learned

  • Read the docs
  • Carefully read the docs
  • Experiment

One of the first things to understand is that you need to know where your PHPCS standards reside.  There you will see a series of folders and files. The one that we will focus on is the Standards folder. This is where our newly created Standards will live.

The steps

We will outline the steps needed to create both a standard as well as a sniff. They are:

  1. Create the directories
  2. Create the XML file
  3. Create the sniff or sniffs if you want more

Sounds super simple right?

So let’s move on!

The first thing we will do is create the directories. The first one is the name of our standard. The second directory will be Sniffs and this is where all of our sniffs will reside. For example, WordPress standards looks like the following image:

Inside of the folder we will then create the ruleset.xml file with the needed information for our standard. It will look something like the following image:

Now, we can finally create our sniffs. But wait there is one thing you should know before making this and that is that you need to have a category folder.

Why?

It makes it easier to organize and know what we are looking for as well. For the sake of this post, I created an Example folder and inside of that I created a ListSniff.php file. Pay close attention to that detail as it will matter down the line. I learned this one because of errors I kept getting.

So, our new folder structure should look a little like:

codesniffer\standards\
codesniffer\standards\YourStandard
codesniffer\standards\YourStandard\ruleset.xml
codesniffer\standards\YourStandard\Sniffs
codesniffer\standards\YourStandard\Sniffs\Example
codesniffer\standards\YourStandard\Sniffs\Example\ListSniff.php

Now we can create our sniff!

This is where the fun really begins for some.

The important thing here is both the file and the class inside of the file must end with Sniff. For my example I named it ListSniff.php so the class would look like:

<?php
/** Example class created for demonstration purposes only **/
class YourStandard_Sniffs_Example_ListSniff implements PHP_CodeSniffer_Sniff {

	public function register() {
		return array();
	} // end register()

	public function process( PHP_CodeSniffer_File $phpcsFile, $stackPtr ) {

		// Tokenize the file, right?
		$tokens = $phpcsFile->getTokens();

		// We need this in order show what the error actually is.
		$phpcsFile->addError( $error, $stackPtr, $code = '', $data = array(), $severity = 0, $fixable = false );

		// We can even add a warning if we want.
		$phpcsFile->addWarning( $warning, $stackPtr, $code = '', $data = array(), $severity = 0, $fixable = false );
	} //end process()
}

You will see two very interesting things here. The first one being the naming of the class. It needs to be:

class {Folder of the standard name}_{the Sniffs folder}_{subfolder of Sniffs}_{your sniff}Sniff

And the second being the implements PHP_CodeSniffer_Sniff. This is straight from their documentation:

Each sniff must implement the PHP_CodeSniffer_Sniff interface so that PHP_CodeSniffer knows that it should instantiate the sniff once it’s invoked.

The reason is because the PHP_CodeSniffer_Sniff interface has two methods that need to be utilized. The register() and process() methods.

The methods

The first one – register – simply does that. You register what tokens you want the CodeSniffer to catch. PHP.net does provide a super handy list with all the available tokens.

Now, let us take a look at how the addError and addWarning sort of work so you can get an idea of what you can do.

First the addError:

/**
@param string  $error    The error message.
@param int     $line     The line on which the error occurred.
@param int     $column   The column at which the error occurred.
@param string  $code     A violation code unique to the sniff message.
@param array   $data     Replacements for the error message.
@param int     $severity The severity level for this error. A value of 0
                         will be converted into the default severity level.
@param boolean $fixable  Can the error be fixed by the sniff?
*/

What’s really nice is that the addWarning does nearly the same:

/**
@param string  $warning  The error message.
@param int     $line     The line on which the warning occurred.
@param int     $column   The column at which the warning occurred.
@param string  $code     A violation code unique to the sniff message.
@param array   $data     Replacements for the warning message.
@param int     $severity The severity level for this warning. A value of 0
                         will be converted into the default severity level.
@param boolean $fixable  Can the warning be fixed by the sniff?
*/

With that I created a random test sniff. It turned out a little like:

class Examplee_Sniffs_Example_FunctionSniff implements PHP_CodeSniffer_Sniff {

	public function register() {
		return array( T_FUNCTION );
	} // end register()

	public function process( PHP_CodeSniffer_File $phpcsFile, $stackPtr ) {

		// Tokenize it!
		$tokens = $phpcsFile->getTokens();

		// We want to check.
		if ( $tokens[ $stackPtr ]['content'] ) {

			// build up our message to show on the report.
			$warn = 'Make sure %s is prefixed';

			// build up the array of data we want to pass
			$data = array( trim( $phpcsFile->getDeclarationName( $stackPtr ) ) );

			// finally we add our warning. Could use addError().
			$phpcsFile->addWarning( $warn, $stackPtr, 'found', $data );
		}
	} //end process()
}

I did try it out on Twenty Seventeen functions file and got the following:

FOUND 0 ERRORS AND 14 WARNINGS AFFECTING 14 LINES
----------------------------------------------------------------------
  27 | WARNING | Make sure twentyseventeen_setup is prefixed
 200 | WARNING | Make sure twentyseventeen_content_width is prefixed
 222 | WARNING | Make sure twentyseventeen_fonts_url is prefixed
 257 | WARNING | Make sure twentyseventeen_resource_hints is prefixed
 274 | WARNING | Make sure twentyseventeen_widgets_init is prefixed
 315 | WARNING | Make sure twentyseventeen_excerpt_more is prefixed
 336 | WARNING | Make sure twentyseventeen_javascript_detection is
     |         | prefixed
 344 | WARNING | Make sure twentyseventeen_pingback_header is
     |         | prefixed
 354 | WARNING | Make sure twentyseventeen_colors_css_wrap is
     |         | prefixed
 371 | WARNING | Make sure twentyseventeen_scripts is prefixed
 433 | WARNING | Make sure twentyseventeen_content_image_sizes_attr
     |         | is prefixed
 460 | WARNING | Make sure twentyseventeen_header_image_tag is
     |         | prefixed
 479 | WARNING | Make sure twentyseventeen_post_thumbnail_sizes_attr
     |         | is prefixed
 499 | WARNING | Make sure twentyseventeen_front_page_template is
     |         | prefixed
----------------------------------------------------------------------

So you can see some versatility when using the PHPCodeSniffer. If you really want to help out the Theme Review team then I highly recommend heading over to the github repo and helping out.

As for me, I’ll be digging more into how to create random tests because I like dabbling and breaking those things.

WordPress and MediaElement styles

Okay, this is more of an experiment I was running because I was reached out by a friend about a theme question.

This actually got me thinking about the shortcodes. The audio and video ones in particular. The reason is that when you use the [audio] shortcode WordPress will use MediaElementJS and enqueue both the JavaScript file and the CSS stylesheet.

Turns our there is a filter for those! Well, two really. One for each one. I wanted to dig deep for this one because styling the player is something I have wanted to do for a long time. Today I found out I can!

Yes, I am excited about it.

Yes, I am writing about it.

Yes, I will share what I found with you.

The steps

This took me about a day’s worth of effort only because I didn’t really know what I was looking for. I knew I needed to figure out what styles were being used and where. By default WordPress registers a few scripts and styles for MediaElements. The styles are registered in the script-loader file. There are two that are registered. The second one –wp-mediaelement– depends on the first registered one so really it is loading two files when either shortcode is used.

Now, how did I find this out?

I looked up wp-mediaelement in the project and found 16 results. The first place I looked was the ajax-actions file only because it was the first result and I wanted to see how it was done as well. Looked at the file and noticed that it uses wpview_media_sandbox_styles() to get the stylesheet URLs but not exactly what I was looking for. Though it is a neat little function.

Next up was the media.php file. This was the one I really should have been looking in. 6 results and one was from the wpview_media_sandbox_styles function so I was making some progress I guess. In the first result it was inside of the wp_playlist_scripts() function and it enqueues the style and a playlist script. Not exactly what I need but getting close.

The second result landed me where I needed to be. A nice filter. The wp_audio_shortcode_library to be precise. What’s really nice is the video is much like it as well. It is wp_video_shortcode_library and they work the same way.

The Filters

apply_filters( 'wp_audio_shortcode_library', 'mediaelement' );
apply_filters( 'wp_video_shortcode_library', 'mediaelement' );

Pretty cool, right? So then how would it be used?

In the functions.php of your theme you could potentially do something like:

add_filter( 'wp_audio_shortcode_library', 'jmc_new_lib' );
add_filter( 'wp_video_shortcode_library', 'jmc_new_lib' );
function jmc_new_lib(){
	// Because we still want the script to load but not the styles
	wp_enqueue_script( 'wp-mediaelement' );
	wp_script_add_data( 'wp-mediaelement', 'conditional', 'lt IE 9' );

	// Enqueue our own styles here.
	wp_enqueue_style( 'jmc-media-styles', get_template_directory_uri() . '/css/media.css' );

	return '';
}

Super sweet if you ask me. That’s great and all but what about the markup? Now we have to think about what needs to be styled right?

Well, I’ve gone ahead and extracted some of the classes for both the audio and video markup to make it easier:

.mejs-offscreen {}
.mejs-container {}
.svg {}
.wp-audio-shortcode {}
.mejs-audio {}
.mejs-inner {}
.mejs-mediaelement {}
.mejs-layers {}
.mejs-poster {}
.mejs-layer {}
.mejs-controls {}
.mejs-button {}
.mejs-playpause-button {}
.mejs-play {}
.mejs-time {}
.mejs-currenttime-container {}
.mejs-currenttime {}
.mejs-time-rail {}
.mejs-time-total {}
.mejs-time-slider {}
.mejs-time-buffering {}
.mejs-time-loaded {}
.mejs-time-current {}
.mejs-time-handle {}
.mejs-time-float {}
.mejs-time-float-current {}
.mejs-time-float-corner {}
.mejs-duration-container {}
.mejs-duration {}
.mejs-volume-button {}
.mejs-mute {}
.mejs-horizontal-volume-slider {}
.mejs-horizontal-volume-total {}
.mejs-horizontal-volume-current {}
.mejs-horizontal-volume-handle {}
.mejs-clear {}
.wp-video {}
.wp-video-shortcode {}
.mejs-video {}
.mejs-overlay {}
.mejs-overlay-loading {}
.mejs-overlay-error {}
.mejs-overlay-play {}
.mejs-overlay-button {}
.mejs-volume-slider {}
.mejs-volume-total {}
.mejs-volume-current {}
.mejs-volume-handle {}
.mejs-fullscreen-button {}

Those are some of the common classes that are used in the final markup. It may not be a complete list but it is a starting point for many and I hope it does help somebody out in theming the audio and video post formats. I will mention this because I know it may be asked down the road: some of those classes may have inline styles so it would be w

I also created a quick gist for the files if you only wanted to target one media type. This is useful if the user has a playlist.

In the following example, I’ve removed the core action and am only documenting the basics.

// remove the scripts/styles originally enqueued
remove_action( 'wp_playlist_scripts', 'wp_playlist_scripts' );

// enqueue ours instead
add_action( 'wp_playlist_scripts', 'jmc_new_liststyles', 10, 2 );
function jmc_new_liststyles( $type, $style ) {
	// $type is the type of playlist either an 'audio' or 'video'.
	// $style can be core 'light' or 'dark' but can add more by having user input
	// and enqueueing the needed style.
	wp_enqueue_style( 'jmc-style', 'path/to/file' );

	// Because we still want to enqueue the script just not the style.
	wp_enqueue_script( 'wp-playlist' );
}

What’s cool about that is that you really can add your own styles if you want. I know there are some themes that take advantage of “skins” or color schemes and could really benefit from this. I may just try and do this on one of my days off or when I can. In the meantime I hope this helps some plugin authors and even more theme authors with styling and using core methods rather than relying on using wp_deregister_style() for this.

Getting WordPress Custom Post Types in the customizer

It’s no surprise I love a good theme. I love it even more when I get a choice of what can go where – extra cookie points if I don’t have to input that information. A while back I wrote out a post about presenting content within themes.

In the course of reviewing so many themes, I noticed one thing in common with many themes. They love trying to control my content, and that is great, as that is what they are meant to do; but when they start creating some content this is where my headache begins. Yes, there is some trivial content like a small span of text but that is very dismissable. What I’m talking about is when I see a theme using textarea when they register a theme setting. This concerns me. This worries me.

Why worry?

This worries me because I can insert a novel in that option. An entire novel if I wanted to. Okay, perhaps not an entire novel but about 4,294,967,295 characters in that theme option. Granted I’m never going to be doing that inside of an option but why would you even give me that option? What happens if I have a recipe that I would like to display? Would I have to copy and paste it or find a hackish way of doing it? I mean you could potentially use shortcodes but then you get an even bigger headache.

As a theme reviewer I try to think about different types of content a user could have. It makes it easy to know what to look for rather than having to rely on having the user ( me in this case ) input all that content or info all over again. Portability. It’s a great thing.

Using the customizer to get a live preview is what drove me to actually writing out this post. Earlier in the day I was trying out a different theme because I’m looking for a grid style. Not for the entire site but only some parts. Sort of a e-commerce if you will. I have the content, I have what I need. I am only looking to display it but when I’m asked to input what I already have over and over I just gave up.

Getting content

One of the things I would love to see more is a dropdown of my content. The customize menu component that was added to core is a great example of this. It gives you a list of all your pages, posts and even other post types that you have registered.

The other day I was talking to a few other theme reviewers and shared a snippet of code. I’ll post a simplified version first and then break it down and expand upon it as we progress.

add_action( 'customize_register', 'jmc_customize' );
function jmc_customize( $wp_customize ){
	// get list of post types
	// Check if we have any
		// create an associative array for later use
		// register our setting
		// register our control
	}
}

What’s super neat about that little snippet is that it will create a dropdown of post types that are registered. The second part of the snippet would be:

// get our setting and make sure it's not empty
if ( get_theme_mod( 'section-one-type' ) ) {
	// create a new WP_Query
	// create a new loop
	// make sure we wp_reset_postdata()
}

That little snippet would be in the template file of your choosing. What do you say we break it down a little to get an idea of what the code is actually doing, yeah?

What are we doing?

The first thing we do is hook to the customize_register hook and create our function callback. Inside of that function a lot of our logic will reside. We get all the available post types by using get_post_types() and create a variable to contain them if there are any.

// We create a variable to house our post types
$post_types = get_post_types( array( 'public' => true, '_builtin' => false ), 'object' );

Then we check to see if there are any and if there are we will create our setting and create our control.

// check to make sure we have any and if we don't we won't even register anything
if ( ! empty( $post_types ) ) { // register setting and control }

Now that we have our post types we need to create an associative array to pass our control. These are the options our users will see.

// create empty array to house types and names.
$types = array();
// we loop and assign.
foreach( $post_types as $type ){
	// name is the registered name and label is what the user sees.
	$types[$type->name] = $type->label;
}

We then register our setting.

// We register our setting.
$wp_customize->add_setting( 'section-one-type',
array(
	// we set a default just in case.
	'default' => key( $post_types ),
	// make sure we validate/sanitize our setting.
	'sanitize_callback' => 'jmc_post_type_validate',
) );

You will notice that I’m using a custom function in order to validate the setting. I called it jmc_post_type_validate and it looks like:

/**
 * Validate that we really are using a registered post type otherwise we return a default set
 * by the registered setting.
 *
 * @param  [type] $value   Value that is passed by customizer.
 * @param  [type] $setting The setting object.
 * @return [type] Return a validated value.
 */
function jmc_post_type_validate( $value, $setting ) {
	// Get a list of available post types.
	$post_types = get_post_types( array( 'public' => true, '_builtin' => false ), 'object' );
	// Check if what the user selected is valid.
	if ( array_key_exists( $value, $post_types ) ) {
		// If it is we return it.
		return $value;
	} else {
		// Otherwise return a default setting.
		return $setting->default;
	}
}

Finally, we create our control.

// register our control.
$wp_customize->add_control( 'section-one-type',
	// we pass our array.
	array(
		// The label for the markup.
		'label' => __( 'The post type for section one', 'jmc' ),
		// A helpful description that display in the customizer.
		'description' => __( 'Choose what post type you would like to show', 'jmc' ),
		// What setting this control is attached to.
		'settings' => 'section-one-type',
		// The type of control we want to display.
		'type' => 'select',
		// We pass our $types associative array we made earlier.
		'choices' => $types,
		// The section we want this to show on.
		'section' => 'static_front_page',
		)
);

Super cool, right?

That is great and all but now what about displaying that on the front end? Enter get_metadata() and WP_Query. Yes, a new loop. Even better is that we can now use metadata within post types. How amazing is that?

Take the following for example:

if ( ! get_theme_mod( 'section-one-type' ) === false ) {
	// create a new WP_Query.
	$type = new WP_Query(
		array(
			// use the post type passed in the setting.
			'post_type' => get_theme_mod( 'section-one-type' ),
			// depending on how many columns we want set it accordingly.
			'posts_per_page' => 3,
		)
	);

	// create our custom loop.
	if ( $type->have_posts() ) {
		while ( $type->have_posts() ) {
			$type->the_post();
			the_title( '<h3>', '</h3>' );
		}
	}
	// because we are nice, we reset the post data not the query.
	wp_reset_postdata();
}

This can be expanded of course but you sort of get the idea. I’ll post the snippet that does include a little more inline documentation in hopes it does help somebody out.

add_action( 'customize_register', 'jmc_customize' );
/**
 * Register our customizer settings for the theme.
 *
 * @method jmc_customize
 * @param  [type] $wp_customize The customizer object.
 */
function jmc_customize( $wp_customize ) {
	// get list of post types.
	$post_types = get_post_types( array( 'public' => true, '_builtin' => false ), 'object' );

	// check if there are types that can be used.
	if ( ! empty( $post_types ) ) {
		// create empty array.
		$types = array();
		// loop over post types.
		foreach ( $post_types as $type ) {
			// name is the registered type and label is what user sees.
			$types[ $type->name ] = $type->label;
		}

		// We register our setting.
		$wp_customize->add_setting( 'section-one-type',
			array(
				// we set a default just in case.
				'default' => key( $post_types ),
				// make sure we validate/sanitize our setting.
				'sanitize_callback' => 'jmc_post_type_validate',
			)
		);
		// register our control.
		$wp_customize->add_control( 'section-one-type',
			// we pass our array.
			array(
				// The label for the markup.
				'label' => __( 'The post type for section one', 'jmc' ),
				// A helpful description that display in the customizer.
				'description' => __( 'Choose what post type you would like to show', 'jmc' ),
				// What setting this control is attached to.
				'settings' => 'section-one-type',
				// The type of control we want to display.
				'type' => 'select',
				// We pass our $types associative array we made earlier.
				'choices' => $types,
				// The section we want this to show on.
				'section' => 'static_front_page',
				)
		);
	}
}
/**
 * Validate that we really are using a registered post type otherwise we return a default set
 * by the registered setting.
 *
 * @param  [type] $value   Value that is passed by customizer.
 * @param  [type] $setting The setting object.
 * @return [type] Return a validated value.
 */
function jmc_post_type_validate( $value, $setting ) {
	// Get a list of available post types.
	$post_types = get_post_types( array( 'public' => true, '_builtin' => false ), 'object' );
	// Check if what the user selected is valid.
	if ( array_key_exists( $value, $post_types ) ) {
		// If it is we return it.
		return $value;
	} else {
		// Otherwise return a default setting.
		return $setting->default;
	}
}

Oh, if you do want to use get_metadata() you can use it a little something like:

get_metadata( get_theme_mod( 'section-one-type' ), get_the_ID(), 'subtitle', true );

Copy paste is hurting your theme

I’ve seen quite a few themes that have been doing this thing of passing an argument to the function when they could be using a filter. I get it we all have our methods. But when there is a better practice would you rather use the better practice or a wrong one?

I choose the better practice. Any day. The last several themes I’ve encountered have been using a snippet that has driven me up a wall. A very unhappy wall. The following code appears to be used a quite a few themes and it needs to stop. Like quickly.

<?php
$commenter = wp_get_current_commenter();
$req = get_option( 'require_name_email' );
$aria_req = ( $req ? " aria-required='true'" : '' );

$comments_args = array(
	// change the title of send button
	// 'label_submit'=>'Send',
	// change the title of the reply section
	// 'title_reply'=>'Write a Reply or Comment',
	// remove "Text or HTML to be displayed after the set of comment fields"
	'comment_notes_after' => ' ',
	// redefine your own textarea (the comment body)
	'comment_field' => '<p class="comment-form-comment"><textarea id="comment" name="comment" aria-required="true" placeholder="'. __('Your Message...', 'text-domain') .'" rows="8" cols="37" wrap="hard"></textarea></p>',
	'fields' => apply_filters( 'comment_form_default_fields', array(
		'author' => '<div class="row"><div class="col-md-4"><p class="comment-form-author">' . '<input id="author" placeholder="'. __('Your name *....', 'text-domain') .'" name="author" type="text" value="' . esc_attr( $commenter['comment_author'] ) . '" size="30"' . $aria_req . ' />' . ( $req ? '<span style="color:red" class="required"></span>' : '' ) . '</p></div>',
		'email' => '<div class="col-md-4"><p class="comment-form-email">' . '<input id="email" placeholder="'. __('Your email *...','text-domain') .'" name="email" type="text" value="' . esc_attr(  $commenter['comment_author_email'] ) . '" size="30"' . $aria_req . ' />' . ( $req ? '<span style="color:red" class="required"></span>' : '' ) . '</p></div>',
		'url' => '<div class="col-md-4"><p class="comment-form-url">' . '<input id="url" placeholder="'.  __('Your website...', 'text-domain') .'" name="url" type="text" value="' . esc_attr( $commenter['comment_author_url'] ) . '" size="30" /></p></div></div>'
		) ),
);

comment_form($comments_args); ?>

The biggest reason is that you can use a filter for all of that. Not only is it cleaner it is much, much child-theme friendlier as well. Actually there are two filters that you can use as well! The first one is the comment_form_default_fields. The second is the comment_form_defaults. Each one returns an associative array.

Let’s look at the first one:

Default Fields

The default fields filter is super simple and you can add, remove and modify the input fields for the comment form. I can’t think of a major reason to want to add to that in a theme but that is a possibility. Let’s see a quick example of just removing the wrapping p tags:

add_filter( 'comment_form_default_fields', 'jmc_commentd_fields' );
function jmc_commentd_fields( $fields ) {

	// get the current commenter if available
	$commenter = wp_get_current_commenter();

	// core functionality
	$req      = get_option( 'require_name_email' );
	$aria_req = ( $req ? " aria-required='true'" : '' );
	$html_req = ( $req ? " required='required'" : '' );

	// Change just the author field
	$fields['author'] = '<label for="author">' . __( 'Name:', 'text-domain' ) . ( $req ? ' <span class="required">*</span>' : '' ) . '</label><input id="author" name="author" type="text" value="' . esc_attr( $commenter['comment_author'] ) . '" size="30" maxlength="245"' . $aria_req . $html_req . ' />';
	return $fields;

}

For those fields, there are three that are available:

  • name
  • email
  • url

Do make sure that that you are somehow showing that the site owner does or does not require the name and email to be filled out.

Form Defaults

The comment_form_defaults is very much like the comment_form_default_fields in that it returns an associative array as well. The possible keys are:

  • fields
  • comment_field
  • must_log_in
  • logged_in_as
  • comment_notes_before
  • comment_notes_after
  • action
  • id_form
  • id_submit
  • class_form
  • class_submit
  • name_submit
  • title_reply
  • title_reply_to
  • title_reply_before
  • title_reply_after
  • cancel_reply_before
  • cancel_reply_after
  • cancel_reply_link
  • label_submit
  • submit_button
  • submit_field
  • format

Very much like the previous filter we can use something like:

add_filter( 'comment_form_defaults', 'jmc_change_comment_form' );
function jmc_change_comment_form( $defaults ) {

	// Change the "cancel" to "I would rather not comment" and use a span instead
	$defaults['cancel_reply_before'] = '<span class="cancel-reply">';
	$defaults['cancel_reply_link'] = __( 'I would rather not comment', 'text-domain' );
	$defaults['cancle_reply_acter'] = '</span>';

	return $defaults;
}

It is very important that you return that array otherwise your fields will not show.

Presenting WordPress content with themes

There are a lot of ways of getting user content in WordPress. Plugins are ideal for creating that content and provide that functionality. Themes are better at presenting that content.

One of the many things we often see in theme reviews is what are known as pseudo post types. I love Justin’s post in regards to content and post types so do give a read to understand a little more. The following paragraphs will focus more on code and methods of not only getting content from the WordPress database but also on how to make use of that data for use in the customizer and on the front-end of a site.

The content

First we need to think about what we want to show on the page, right? For this example will focus on a single post. What’s super cool is that this can be applied to custom post types as well and not just the standard post. The function we will use for this is get_post_meta() and the developer documentation is a good starting point: https://developer.wordpress.org/reference/functions/get_post_meta/

What matters here is the returning value from that function:

(mixed) Will be an array if $single is false. Will be value of meta data field if $single is true.

That’s great, but what does it mean, right? Let’s say what we add a custom field to a post. Perhaps a subtitle. Depending on what you set your custom field to, you would use:

if ( $subtitle = get_post_meta( get_the_ID(), 'subtitle', true ) ) {
	printf( '<span class="subtitle">%s</span>', $subtitle );
}

That’s much easier than creating a custom meta box that would be lost when the theme is changed, right? I know you may be asking, but what about other post types? Well, we can use get_metadata() for that. The developer docs help here as well. All we do is change a small thing and we can use it in an archive style.

if ( $subtitle = get_metadata( get_post_type(), get_the_ID(), 'subtitle', true ) ) {
	printf( '<span class="subtitle">%s</span>', $subtitle );
}

Great!

Other types

So, with that you have a basic starting point of core’s built-in capabilities. Metadata is super useful and easy to get. Best part is that if that value doesn’t exist, we don’t output anything! No extra markup or code to worry about.

Now, there are tons of plugins out there that create all sorts of data and content for users. Let’s take a quick look at one that is pretty popular: testimonials.

What’s super cool is that by using a few function_exists() we can check if a plugin is active or not. The registration method may differ from plugin to plugin but the basic thing is to use the right function to check. There are a few functions we can use:

  • function_exists()
  • method_exists()
  • class_exists()
  • interface_exists()

That’s super cool and even core has one that can be useful as well:

  • post_type_exists()

Phenomenal if you want to check for a testimonial post type, or a portfolio, or a form, event, you name it. For this example I’ll use the Easy Testimonials plugin to test.

if ( class_exists( 'easyTestimonials' ) && post_type_exists( 'testimonial' ) ) {
	$testimonials = new WP_Query( array( 'post_type' => 'testimonial', 'posts_per_page' => 3 ) );
	if ( $testimonials->have_posts() ) {
		while ( $testimonials->have_posts() ) {
			$testimonials->the_post();
			the_title( '<h3 class="testimonial-title">', '</h3>' );
		}
	}
	wp_reset_postdata();
}

Super quick example. You can see what it is doing at the beginning. We are checking to see if the class easyTestimonials exists as well as the post type. If the functions return true then we can run our query. What’s nice is that now we are in control of how to present that content however we desire.

But what about the customizer?

I mentioned this a little earlier and we can use this as callback for a control. Yes, conditionally show a control based on what is active and available. Super awesome, right?

In this example we will use the custom fields suite plugin for a page customizer control. This is going to be a unique example in that we are going to check if the plugin is active and if a field is populated on a page. Now, it may not be the best example but it will give you an idea of how it works.

add_action( 'customize_register', function( $wp_customize ) {
	$wp_customize->add_setting( 'hero-location',
		array(
			'default' => 'above',
			'sanitize_callback' => 'jmc_sani_location'
		) );
	$wp_customize->add_control( 'hero-location-control',
		array(
			'settings'        => 'hero-location',
			'label'           => __( 'Hero Image Location', 'text-domain' ),
			'description'     => __( 'Where will the hero image show? Above or below the secondary menu location', 'text-domain'),
			'section'         => 'layout',
			'type'            => 'select',
			'active_callback' => 'jmc_check_field',
			'choices' => array(
				'above' => __( 'Above', 'text-domain' ),
				'below' => __( 'Below', 'text-domain' )
			)
		) );
}, 10 );
function jmc_check_field(){
	// bail if it's not a page
	if ( !is_page() ) {
		return false;
	}

	// check for the function and value
	if ( function_exists( 'CFS' ) && CFS()->get( 'hero-image' ) ) {
		return true;
	}

	// if nothing we won't show the control
	return false;
}

Let’s break it down a little bit. The part that really matters is:

'active_callback' => 'jmc_check_field'

This callback is what will handle our logic. This ultimately determines if that control will show or not. We want to begin with the assumption that it will not show so we always return false at the end. What’s super cool is that this can be done with sections as well. This next example is nice because we check for the available post types and can use a new WP_Query on the theme to get the content.

add_action( 'customize_register', 'jmc_customize' );
function jmc_customize( $wp_customize ) {
	// get list of post types
	$post_types = get_post_types( array( 'public' => true, '_builtin' => false ), 'object' );

	if ( ! empty( $post_types ) ) {
		$types = array();
		foreach( $post_types as $type ) {
			$types[$type->name] = $type->label;
		}

		$wp_customize->add_section( 'section-one',
			array(
				'title'           => __( 'Front Page sections', 'text-domain' ),
				'description'     => __( 'Choose what else shows on the front', 'text-domain' ),
				'active_callback' => 'jmc_haz_post_types'
			) );

		$wp_customize->add_setting( 'section-one-type',
			array(
				'default'           => '',
				'sanitize_callback' => 'jmc_post_validate',
				)
		);

		$wp_customize->add_control( 'section-one-control',
			array(
				'label'       => __( 'The post type for section one', 'text-domain' ),
				'description' => __( 'Choose what post type you would like to show', 'text-domain' ),
				'settings'    => 'section-one-type',
				'type'        => 'select',
				'choices'     => $types,
				'section'     => 'section-one'
			)
		);
	}
}

function jmc_haz_post_types() {
	$types = get_post_types( array( 'public' => true, '_builtin' => false ), 'object' )
	return empty( $types );
}

function jmc_post_validate( $value, $setting ){
	$post_types = get_post_types( array( 'public' => true, '_builtin' => false ), 'object' );
	if ( array_key_exists( $value, $post_types ) ){
		return $value;
	} else {
		return $setting->default;
	}
}

Then on the front-end we could do something like:

if ( ! get_theme_mod( 'section-one-type' ) === false ) {
	$type = new WP_Query(
	array( 'post_type' => get_theme_mod( 'section-one-type' ),
		'posts_per_page' => 3
		)
	);

	if ( $type->have_posts() ):
		while( $type->have_posts() ): $type->the_post();
		the_title( '<h3>', '</h3>' );
		endwhile;
	endif;
	wp_reset_postdata();
}

The thing to remember is that content should remain when the theme is switched. There should be no reason for a user to have to input the same thing over and over again on every theme switch – after all they are after the design and not the content of the theme. A good practice to keep in mind when creating your options is:

Do I give them decision A or decision B?

My dealing with layouts

The other day I got a chance to rewrite a bit of code. It was fun. It dealt with changing from an object to an associative array. It may sound super easy for some but for my mind it took a little bit to figure out the right way of creating a good way of filtering that out.

The issue was creating a good way to not only create a layout array with information but a decent way of a theme or plugin to filter it out. Creating some defaults was the easy part. I knew wp_parse_args would be a good solution to filling in any missing information. So I quickly began with what information was actually needed:

$defaults = apply_filters( 'jmc_layout_args', array(
	'layout_name'      => '',
	'layout_label'     => '',
	'image_src'        => '%s',
	'is_global_layout' => false,
	'is_post_layout'   => true,
);

Worked great for setting the main defaults. The next part was making sure the needed layout was filterable. Of course I had to use a foreach loop to iterate over that.

foreach ( $layouts as $id => $info ) {
	$layouts[ $id ] = wp_parse_args( $info, $defaults );
}

Cool. I managed to get the layout with some default information if nothing was passed. Well, what happens if you wanted to edit a particular layout’s data? I looked a little closer and realized I could pass one more argument to apply_filters. Yep!

foreach ( $layouts as $id => $info ) {
	$defaults = apply_filters( 'jmc_layout_args', array(
		'layout_name'      => '',
		'layout_label'     => '',
		'image_src'        => '%s',
		'is_global_layout' => false,
		'is_post_layout'   => true,
	), $id );
	$layouts[ $id ] = wp_parse_args( $info, $defaults );
}

Awesome! And with that we can now filter out not only the default arguments passed but on a layout basis if wanted by a theme or plugin.

Let’s say we have three layouts and wanted to change one. We would use something like:

add_filter( 'jmc_layout_args', function( $args, $id ) {
	if ( $id == 'three column' ) {
		return array(
		'is_global_layout' => true,
		'is_post_layout'   => false,
		);
	}
	return $args;
}, 10, 2 );

Quick, simple, and a little dirty.

In search of a notice API

Yes, I’m writing about the admin notices. I know some are already up to their ears with that and I’m on that boat. The admin area is – and should be – for the user. Any notices that appear need to be informational. Not a nag. With that being said, I know there are some that would like to promote or even encourage their users to leave a review. I’m for that but there is a time and a place.

The admin area isn’t one. Nor should it be.

I do think part of that is because there really is no proper function, or hook, for using those. [ There really should not be. ] But for the sake of experimentation I went and played around with a few things. This is what ensued:

The code

It is pretty quick and decently documented.

// because otherwise it will be a turd-nugget
add_action( 'admin_init', 'check_for_notice_dismiss' );
function check_for_notice_dismiss() {
	// we check if the setting is set, making sure that it does not return false
	if ( get_theme_mod( 'theme-notice' ) === false ) {
		// we add our action for the notice
		add_action( 'admin_notices', 'we_add_our_notice' );
		// super hackish only because there are some that aren't running 4.5 and
		// so they can't use wp_add_inline_script()
		add_action( 'admin_footer', 'our_jquery_action' );
		add_action( 'wp_ajax_our_jquery_dismiss', 'our_jquery_dismiss' );
	}
}

// callback for our admin notice
function we_add_our_notice() {
	// checking for our theme screen because we can
	if ( get_current_screen()->id !== 'themes' ){
		// get the message
		$string = __( 'How about you review our theme?', 'text-domain' );
		$html = '<div id="my-theme-notice" class="notice is-dismissible notice-info"><p>%s</p></div>';
		// the final outout of the notice
		printf( $html, $string );
	}
}

// hackish job so we don't have to enqueue an extra file
function our_jquery_action(){ ?>
<script>
jQuery( document ).on( 'click', '#my-theme-notice .notice-dismiss', function() {
	jQuery.ajax({
		url: ajaxurl,
		data: {
			action: 'our_jquery_dismiss'
		}
	})
})
</script>
<?php }

// callback function for our AJAX
function our_jquery_dismiss() {
	if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
		set_theme_mod( 'theme-notice', true );
	}
	die();
}

Really I only did test using a theme which does explain the use of get_theme_mod(). There is potential in creating some standard function in adding a dismissible notice.

Something along the lines of:

function wp_add_notice( $message, $dismiss = true, $type = '' ) {}

Or even as a class:

class WP_Notice {

	var dismiss = true;
	var types = array( 'info', 'error', 'updated', 'saved' );

	public function __construct( $message, $type, $dismiss ) {
		if ( !$message )
			return;
		// the rest of the code
	}

	private function render_message( $message, $classes ) {
		printf( '<div class="%s"><p>%s</p></div>', esc_attr( $classes ), esc_html( $message ) );
	}
}

I do think the class would be better suited for that but that could just be me. There you have it! Possible ideas to iterate over.

Adding, removing fonts from a theme

One request I often see in the forums is how to add, remove, and change fonts in themes. There are quite a few ways of going about it but ultimately depends on how the theme is not only using but loading the fonts. Core themes use a standard practice of creating an array, returning a URL created with add_query_arg(). Don’t get me wrong, I like that method. The only downside is it is inside of a pluggable function.

I’m not a huge fan of pluggable functions. What I am a fan of is filters and hooks. They are amazing!

Simply amazing!

The code

Okay, with that being said, what I like doing is creating a filter that a child theme can use or even a functionality plugin can use as well. In the following example I will load two fonts from Google. Yes, the most common font library used. Part of that reason is because they have a good API for their fonts. You can use your own but would require a different approach to the code.

So, onward!

function jmc_fonts(){
	$url = '';
	$fonts = array();
	$sets = apply_filters( 'jmc_sets', array( 'latin', 'latin-ext' ) );
	
	$fonts['lato'] = 'Lato';
	$fonts['crimson'] = 'Crimson Text:400,400italic,600';
	
	$fonts = apply_filters( 'jmc_fonts', $fonts );
	
	if ( $fonts ) {
		$url = add_query_arg( array(
			'family' => urlencode( implode( '|', $fonts ) ),
			'subset' => urlencode( implode( ',', $sets ) ),
		), 'https://fonts.googleapis.com/css' );
	}
	return $url;
}

Let’s break it down

Let’s take a look over the code a little bit.

We first define our function, in this case we prefix it with jmc and create a few variables. We then populate our $fonts array with the fonts we want to load. The $fonts variable is the array we want to be able to change. We do this by using apply_filters and passing it our created array of fonts. In this case the filter name is jmc_fonts.

If you look a little closer you can see we really created two filters. The first one is the jmc_sets that is a list of subsets to use. You can add or remove subsets almost in the same manner as the jmc_fonts. There is a slight difference in that you do declare the index number you want to remove. In the above latin would be $sets[0].

How to use

In a child theme’s function file, or your functionality plugin, you can now add, remove fonts to your liking. There are some people who like to use five fonts for their site. More power to them. So, let’s take a look at how we can use this, yeah?

add_filter( 'jmc_fonts', function( $fonts ){
	// add some fonts
	$fonts['ubuntu'] = 'Ubuntu:500,500';
	$fonts['merriweather'] = 'Merriweather:400,700';
	
	// remove the crimson since it does not suit design need
	unset( $fonts['crimson'] );
	
	// finally return new array
	return $fonts;
});

In the example, we are adding two new fonts, removing one, and finally returning the modified array. Pretty neat if you ask me. Now, this will only work with Google fonts the way it is currently coded. Do keep in mind Google has their own API for getting fonts so you need to know how that works and that is beyond the scope of this little guide so you will have to read up on your own.

Go. Create. Share!