Limit content by product purchase with Easy Restricted Content for WooCommerce

A while ago I released a book called Don’t be an Idiot: Learn to run a viable business and with it I delivered a set of interviews that were only available to purchasers.

Of course I didn’t want people who didn’t purchase to be able to see the videos so I had to write some code to restrict my page.

Now I’ve written a plugin called Easy Restricted Content for WooCommerce (available for $39.99 for a single site) that lets you do the same thing without any code at all.

Lets see how we do it.

Getting Started

I’ve already got WooCommerce installed and a ‘book’ product added. I also have Easy Restricted Content for WooCommerce installed and activated.

Now we need to create a page to hold our custom interviews to go with our product. Go to pages and choose add new. Now name your page ‘Awesome Interviews’ then put some content in the page.

pages-add-new 14-07-15, 8.32.49 AM

Restricting the page to a product purchase

Now on the right side of the page screen you’ll see a box called ‘Restrict Content’. Click on the select box to see the available products in your store. You can type inside the select box and it will start to filter the available products based on what you type.

Find the product you want users to purchase to be able to see your content and click on it to select it.

restrict-typing 14-07-15, 8.46.53 AM

You can also choose to totally remove the page from the WordPress menu for users that haven’t purchased the product. To do this check the box below our select box.

Creating a Custom Message

By default Easy Restricted Content for WooCommerce will provide a message to users that says:

This content is restricted to users that have purchased $product

$product will be the name of the product with a link to the product so that someone could go purchase it.

If you want to provide a custom message, maybe a sample video interview then you can do this via the custom message option in Easy Restricted Content for WooCommerce.

On your page under the main content box is a content area called ‘Custom Restricted Message’. Check the box that says ‘Add a custom message:’ to see the new content entry area open.

Preview

Now type your custom message in to the editor. If you want to link to the product in your content then you can use the built in WooCommerce button in the WordPress editor.

When you’re done make sure you save the post and make sure it’s published for your users.

Making sure that users create an account

Yes our product is restricted, but we’re not quite done yet since by default users can purchase as a ‘guest’ which means they don’t have an account. We need users to have an account because they can only see the content if they are signed in.

Let’s change the default settings of WooCommerce to ensure that our users are required to create an account.

Go to WooCommerce Settings, and choose the Checkout tab. Now you need to uncheck the box that says ‘Enable Guest Checkout’.

require-accounts 14-07-15, 8.40.45 AM

Once you have the guest checkout box unchecked, scroll to the bottom and click save to save the setting.

That’s it, you now have your content restricted by product purchase on your site.

Screencast

Why you SHOULD document all your code

There is a school of thought that says if a function/method/class is small and abstracted you don’t need to write a documentation block for it. So write them small and you can skip the documentation.

Then there’s me who say, document your function/methods/classes and stop being lazy.

Yeah I said lazy and here is why I think you should be documenting ALL THE THINGS.

1. Future You Isn’t as Smart as Current You

During that late night coding session fueled up on coffee you have so many awesome ideas. You write your best code ever complete with unicorn tears.

Unfortunately future you is going to think you’re an idiot when you cut corners and didn’t write that documenation.

Future you will remember that you came up with an awesome solution here, but have no idea how it actually works. Where does one get more unicorn tears so you can understand what’s going on now?

Writing proper documentation on your functions may just let future you in on the late night epiphany you had for your brilliant solution.

2. Future devs are smarter than you

Developers are a pretty arrogant bunch.

We think that whatever our current solution is, it’s better than the code that was written before.

We conviently ignore all those times we look at terrible code then when we dig through source control we realize that bad code is our fault.

Of course we have no idea if scope changed at the last second on the previous developer and the code simply needed to work to hit a deadline.

Maybe the previous developer had a family issue that meant they simply couldn’t focus on the task at hand. I know something like having my kids in the hospital would do that to me.

Writing proper documenation is going to help show a future developer that you’re not an idiot. I’ve put comments in acknowledging that a part of the code feels like a ‘hack’ but we needed to launch so it ‘works’ for now.

Sure that future developer may still dislike the code they see, but they at least don’t think you’re a moron.

3. You’re going to learn

Spending time documenting the parameters that are used with a WordPress filter or action is going to force you to actually learn what is happening.

It’s so easy to copy/paste some code from a tutorial and have a general idea of what’s happening in it. Of course you may have no idea what that variable being passed in to your filter actually is.

Take some time, track the filter down and look at the root function. Figure out what the data being passed is and then write up the proper documentation for it.

You’re going to understand the WordPress internals so much better and you’re on your way to becoming a better WordPress developer.

4. You’re not lazy

When I look at code that has no documentation it’s usually a sign of a lazy developer all around.

Often I’m going to find a bunch of other code that’s been tried and then commented out but never deleted.

So now I have to dig through commented out code in addition to all the other code in the project.

Maybe you clean up after yourself, but I’m on edge as soon as I see that we don’t have documentation on our code.

Code Documenation is not…

So you do actually document your code, but that’s not really user facing or outside developer facing documentation for your project. It’s lazy to just generate documentation and call it your plugin documentation.

It’s like putting social media buttons on your site and then telling yourself that you have a social strategy. You don’t, you have extra javascript running on your site and usually a bunch of 0’s showing nobody liked your articles.

In a future post I’ll cover what proper user facing documentation consists of.

WordPress Documentation stanards

If you don’t know how to document your code then check out the WordPress Documenation Standards.

Each project reference them and follow them. You’ll start understanding more and write better code if you do.

Using pre_get_posts with meta_query

pre_get_posts is an awesome action (does anyone else think of it as a filter like me) that lets you alter the WordPress query arguements just before WordPress gets it’s post.

Say you want to add 20 posts to RSS feeds instead of the standard WordPress 10 posts.

function more_rss_pleeese( $query ){
    if ( is_feed() ){
        $query->set( 'posts_per_page', '20' );
    }
}
add_action( 'pre_get_posts', 'more_rss_pleeese' );

Now all your feeds have 20 posts while your pages still have 10 posts.

It has a few tricks for the first timer though. Did you realize that hooking pre_get_posts will also change your posts queries in the WordPress admin? Make sure you use the is_admin() conditional to exclude the WordPress admin unless you really want to change it.

Everything WP_Query has right??

In theory pre_get_posts can do everything that is available in WP_Query right??

Riiiight??

Yeah actually it can’t quite do everything. Specifically it can’t use the full awesomness avalibale to meta_query in WP_Query.

Lets start by looking at how we’d write our WP_Query to get posts that either don’t have a meta_key set at all, or that have a meta_key that matches the current user.

<?php
$args = array(
    'meta_query' => array(
        'relation' => 'OR',
        // this is the part that gets a key with no value
        array(
            'key'     => '_restricted_key',
            'value    => 'oeusnth', // just has to be something because of a bug in WordPress
            'compare' => 'NOT EXISTS',
        ),
        array(
            'key'     => '_restricted_key',
            'value'   => get_current_user_id(),
            'compare' => '==',
        ),
    ),
);
 
$custom_posts = new WP_Query( $args );
 
// now your loop
?>

Our first array would get any post that doesn’t have our meta_key _restricted_key set at all. WordPress has a ‘bug’ that means you have to set something for the value here. So I just mash the keyboard to get something in there.

Our second array would get any posts with the same meta_key with the value that matches our current_user_id. So if it was 42, we’d get posts with the value 42 for the meta_key.

Now take a look at the relation arguement. That allows us to specify the relationship between the 2 arrays. So it says get all posts with the key not set OR with the key value of 42.

Awesome, now we dance.

Now lets look at that in the context of pre_get_posts.

function custom_posts( $query ){
 
    if ( ! is_admin() ){
        $query->set( 'meta_query', array(
            'relation' => 'OR',
            // this is the part that gets a key with no value
            array(
                'key'     => '_restricted_key',
                'value    => 'oeusnth', // just has to be something because of a bug in WordPress
                'compare' => 'NOT EXISTS',
            ),
            array(
                'key'     => '_restricted_key',
                'value'   => get_current_user_id(),
                'compare' => '==',
            ),
        );
    }
 
}
add_action( 'pre_get_posts', 'custom_posts' );
?>

Theoretically we should now get the same results but are setting the WP_Query args just before it gets the posts from the database.

Unfortunately it won’t work. We can pull the ‘relation’ field and either one of the arrays and find that they work on their own but they don’t work together.

So how on earth do we use pre_get_posts to do a double array style meta_query?

Magic or really just another filter

To accomplish our tasks we need to actually use a filter in WordPress called posts_where.

posts_where allows us to filter the SQL that WordPress is using directly by adding a WHERE claus to our code. So lets take a look at our new pre_get_posts filter which also uses the posts_where filter to get posts with 2 meta_key values.

<?php
function custom_posts( $query ){
 
    if ( ! is_admin() ){
        $query->set( 'meta_query', array(
            // this is the part that gets a key with no value
            array(
                'key'     => '_restricted_key',
                'value    => 'oeusnth', // just has to be something because of a bug in WordPress
                'compare' => 'NOT EXISTS',
            ),
        );
    }
 
    if ( is_user_logged_in() ){
        add_filter( 'posts_where', 'custom_where' );
    }
 
}
add_action( 'pre_get_posts', 'custom_posts' );
 
function custom_where( $where = '' ){
 
    global $wpdb;
 
    $where .= " OR (( $wpdb->postmeta.meta_key = '_restricted_key' AND $wpdb->postmeta.meta_value = get_current_user_id() ))";
 
    remove_filter( 'posts_where', 'custom_where' );
 
    return $where;
 
} // custom_where
?>

Now our above code uses a single array in our pre_get_posts. That array will get posts that do not have a meta_key set.

Then if is_user_logged_in (since if they don’t have a user id we can read we can’t get other posts) we add our new filter to our SQL. This filter says OR if the meta_key _restricted_key has a meta_value of our current user id.

Now we get the results we want.

Yes it’s a bit confusing for a new person with WP_Query but it works.

Screencast

Restrict Content Pro or Paid Memberships Pro

There are a bunch of membership plugins out there. Look at the title, I’m not covering them all today. If you want a comparison of way more membership plugins/products for WordPress then you need to go read these posts from Chris Lema.

Today I’m going to tell you about the 2 membership plugins I use the most in my client work and why/when I choose one over the other.

  1. Paid Memberships Pro
  2. Restrict Content Pro

A note on Code Quality

I’m a developer so of course I’m going to talk about the code quality of a plugin. Both plugins are great as far as I’m concerned. Nothing has ever stuck out as terrible to me.

Yes each plugin has parts I wish were built different but code I wrote 6 months ago also falls in to that category. Every developer writes code that they aren’t proud of later.

Cost

PMPPro is totally FREE. None of the code costs anything. There is a cost for support and the official though. If you’re just getting in to using it then you really should pay for it since the documentation is great.

RCP is not free but it’s not very expensive at $42 USD a year for a single site. For $155 you can get all the plugins that are around for RCP as well so that’s what I recommend clients get out of the gate. Then they can use RCP on any future sites and have all the plugins they may need already. There are also a bunch of free plugins for RCP in the WordPress.org repository.

Depends on Scope 99% of the time

Usually the requirements of the project decide which plugin I’m going to use for me. Let’s look at a few examples.

The first simple project requirement that pushes me to one plugin or the other is if we need to have a WordPress Multisite site tied to a subscription. If we do than I pick PMPPro since there is already a plugin that does that for you.

One currently doesn’t exist for RCP.

If you need to build a job board (and I’ve built a few) then you want to use RCP since it already has a bridge for WP Job Manager.

Do we need memberships and a downloadable store? Then RCP is your choice with it’s member discounts for Easy Digital Downloads.

There are a whole host of other plugins that are only found on one platform so in almost every site I’ve built the choice has already been made for me by the requirements.

If either option works then I usually end up choosing RCP. It just feels ‘easier’ to work with overall, which is possibly just habit/experience with RCP and many of Pippin’s other plugins.

Why use shuffle to randomize WordPress Comments

Last week I showed off a plugin called Random Reviews for WooCommerce. Really it was an assignment given to my students at BCIT and I wanted to provide an example for them.

One of the questions that came up was:

Why did you use shuffle() instead of array_rand()?

Good question and there are lots of bad code examples out there so lets walk through what each function does in the context of my plugin.

First we’ll look at what the functions do.

shuffle()

Acording to the PHP documentation shuffle():

This function shuffles (randomizes the order of the elements in) an array.

It returns true or false so it either did or didn’t work. Lets look at an example.

$foo = array( '1', '2', 'something' );
 
shuffle( $foo );
 
// now $foo could look like or at least something random
array( '2', '1', 'something' );

How about a more complex array example.

$foo = array(
    '0' => array( 'something', 'nothing', 'of' ),
    '1' => array( 'blue', 'green', 'pink' ),
    '2' => array( '1', '2', '3' ),
);
 
shuffle( $foo );
 
// could look like...
array(
    '1' => array( 'blue', 'green', 'pink' ),
    '0' => array( 'something', 'nothing', 'of' ),
    '2' => array( '1', '2', '3' ),
);

It’s worth noting that in earlier versions of PHP shuffle() wasn’t really good at being random but that’s not a problem now. Really it’s not a problem for my random reviews either since as long as the comments are ‘fairly random’ I’m okay.

array_rand()

According to the PHP documentation array_rand():

Picks one or more random entries out of an array, and returns the key (or keys) of the random entries.

So it has 2 parameters

array_rand( $array, $pick )

  • $array – the array you want randomized
  • $pick – how many elements you want out of the array

And a usage example.

$foo = array( '1', '2', 'something' );
 
$random_foo = array_rand( $foo );
 
// now $random_foo could look like or at least something random
array( '2', '1', 'something' );

And now our more complex array.

$foo = array(
    '0' => array( 'something', 'nothing', 'of' ),
    '1' => array( 'blue', 'green', 'pink' ),
    '2' => array( '1', '2', '3' ),
);
 
$random_foo = array_rand( $foo );
 
// $random_foo could look like...
array( '1', '0', '2' );

So while shuffle leaves our array intact and just shuffles things around array_rand only gives us the keys. That means later on we still need to have the $foo variable around and then reference the ‘random’ bits we want by the keys in $random_foo.

That may be a great thing to do sometimes, but not for our random comments. We can see why in a second when we figure out what type of array we’re dealing with and how WordPress handles the comments array.

shuffle to randomize comments

Here is a basic version of our plugin code that could be dropped in to your theme functions.php file to randomize all comments.

function wptt_random_comments( $comments, $post_id ){
    shuffle( $comments );
    return $comments;
} // wptt_random_comments
add_filter( 'comments_array', 'wptt_random_comments', 10, 2 );

Now lets look at what WordPress gives us in our $comments variable passed to our filter.

Array
(
    [0] => stdClass Object
        (
            [comment_ID] => 2
            [comment_post_ID] => 1
            [comment_author] => admin
            [comment_author_email] => admin@wpthemetutorial.com
            [comment_author_url] => 
            [comment_author_IP] => 192.168.50.1
            [comment_date] => 2014-05-30 04:31:50
            [comment_date_gmt] => 2014-05-30 04:31:50
            [comment_content] => I'm an admin and I'm making a comment now.
            [comment_karma] => 0
            [comment_approved] => 1
            [comment_agent] => Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.76.4 (KHTML, like Gecko) Version/7.0.4 Safari/537.76.4
            [comment_type] => 
            [comment_parent] => 0
            [user_id] => 1
        )

    [1] => stdClass Object
        (
            [comment_ID] => 3
            [comment_post_ID] => 1
            [comment_author] => author-one
            [comment_author_email] => curtis+authorone@curtismchale.ca
            [comment_author_url] => 
            [comment_author_IP] => 192.168.50.1
            [comment_date] => 2014-05-30 04:47:31
            [comment_date_gmt] => 2014-05-30 04:47:31
            [comment_content] => Author one is making a comment and I'm an author.
            [comment_karma] => 0
            [comment_approved] => 1
            [comment_agent] => Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.76.4 (KHTML, like Gecko) Version/7.0.4 Safari/537.76.4
            [comment_type] => 
            [comment_parent] => 0
            [user_id] => 2
        )

    [2] => stdClass Object
        (
            [comment_ID] => 4
            [comment_post_ID] => 1
            [comment_author] => author-two
            [comment_author_email] => curtis+authortwo@curtismchale.ca
            [comment_author_url] => 
            [comment_author_IP] => 192.168.50.1
            [comment_date] => 2014-05-30 04:47:56
            [comment_date_gmt] => 2014-05-30 04:47:56
            [comment_content] => Now author two is making a comment.
            [comment_karma] => 0
            [comment_approved] => 1
            [comment_agent] => Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.76.4 (KHTML, like Gecko) Version/7.0.4 Safari/537.76.4
            [comment_type] => 
            [comment_parent] => 0
            [user_id] => 3
        )

)

Now if we pass an array like that through array_rand all we’re going to get back is the keys for the comments. That means before we send the comments out of the filter back to WordPress we’d need to reassemble the array. If we only sent the keys back then WordPress would give us an error since it expects to see an array of objects when it builds out the comments.

Using shuffle means that the array stays intact and is just random. Nothing left for us to do but head off to the next coding challenge for the day.

Screen Shot 2014-06-27 at 12.43.57 PM

Randomizing WooCommerce Reviews

By default the WooCommerce reviews are going to display in the order they were submitted to your site. That can present a few issues though.

The last (or first) review is what everyone will see. What if we want to randomize the rievews for the product so that each time a user visits the page they will see a different review in the first position?

Today I’m going to show you how.

Getting our Comments

The first thing we need to recognize is that WooCommerce stores the reviews as WordPress commments. That means we can look at the filters that are provided for comments in WordPress.

Checking out the WordPress filters page the one we want is comments_array. We can find usage of this filter in the comments_template function.

Here is what a few lines of the function look like.

/**
 * Filter the comments array.
 *
 * @since 2.1.0
 *
 * @param array $comments Array of comments supplied to the comments template.
 * @param int   $post_ID  Post ID.
 */
$wp_query-&gt;comments = apply_filters( 'comments_array', $comments, $post-&gt;ID );

We can see that it takes the comments in our $wp_query object and passes them to our filter along with the $post->ID that goes with the post WordPress is currently getting comments for.

Randomizing our WooCommerce Reviews

Now that we know where we need to hook to get our comments lets look at our code to randomize the comments.

class SFN_Random_Comments{
 
    function __construct(){
 
        add_filter( 'comments_array', array( $this, 'randomize_comments' ), 10, 2 );
 
    } // construct
 
    /**
     * Randomizes the reviews for a WooCommerce site
     *
     * @since 1.0
     * @author SFNdesign, Curtis McHale
     *
     * @param array         $comments       required            The array of comments
     * @param int           $post_id        requride            The post_id we are getting comments for
     */
    public function randomize_comments( $comments, $post_id ){
 
        if ( get_post_type( $post_id ) !== 'product' ) return $comments;
 
        shuffle( $comments );
 
        return $comments;
 
    } // randomize_comments
 
} // SFN_Random_Comments
 
new SFN_Random_Comments();

First we have a class and then we call our filter from inside the __construct function.

Then our main function uses get_post_type to make sure that we are currently on a product. If we are not on a product then just return the $comments without altering them.

If we are on a product then we use the PHP function shuffle. shuffle simply randomizes the array. We don’t need to set another variable or anything. Just return $comments after shuffle and we have our reviews randomized.

You can go get the plugin on Github.

Screencast

Screen Shot 2014-07-02 at 9.19.10 AM

Getting Started with WordPress Development

So you’ve heard about this WordPress thing and you want to get started do you? The question is…

Where on earth do you start?

The first question to ask yourself is why do you want to work with WordPress anyway? Is it because you actually believe the platform is awesome or do you just see that lots of people use it and thus there may be some money to be made?

I’m not here to judge your reasons, but if you just think there is money to be made are you really going to stick it out long term?

Now on to what I think a WordPress developer should know to really have a decent handle on WordPress.

Queries

A solid WordPress developer should have a decent handle on:

So now you have links for the major ways to get post data out of WordPress but when do you use them?

WP_Query

I use WP_Query when I have multiple queries on a page or if we need extra data on the front-page.php template of the site. I almost never use it when I need to return HTML (when using get_the_title() in a function say).

get_posts

In plugins I’m usually using get_posts and passing the post_id in to my functions then returning HTML.

get_post

Used to get a single post by the post_id.

$custom_post = get_post( 32 );

Now $custom_post is a single post object for the post with the post_id 32. I use this sometimes but not very often.

pre_get_posts

pre_get_posts is great when you need to exclude a single category from the usual WordPress archive page queries. Running a custom WP_Query there is just going to get you in a world of pain.

It’s also great for modifying your RSS feeds.

function change_rss( $query ){
 
    if ( ! is_feed() ) return $query;
 
    $query->set( 'posts_per_page', 100 );
 
} // change_rss
add_action( 'pre_get_posts', 'change_rss' );

The above code makes my RSS feed have 100 posts but leaves the rest of my stock WordPress queries alone.

The Loop

The WordPress loop is in your template files usually and looks like:

<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
 
    <!-- post content goes here -->
    <?php the_title(); ?>
 
<?php endwhile; else: ?>
 
    <h3>Oops!! Looks like something went wrong. Please get in touch with the site <a href="mailto:<?php echo get_bloginfo('admin_email'); ?>">administrator</a> and we'll get this sorted out</h3>
 
<?php endif; ?>
 
<?php wp_reset_postdata(); ?>

Notice I’m using wp_reset_postdata() right at the bottom. That’s going to get your $wp_query global all cleaned up for you at the end so that your loops don’t conflict with each other.

If you want to pass a custom WP_Query to your loop then it would look like:

$args = array(
    'post_type'      => 'custom_post_type',
    'posts_per_page' => '20'
);
 
$custom_loop = new WP_Query( $args );
 
if ( $custom_loop->have_posts() ) : while ( $custom_loop->have_posts() ) : $custom_loop->the_post(); ?>
 
    <!-- post content goes here -->
    <?php the_title(); ?>
 
<?php endwhile; else: ?>
 
    <h3>Oops!! Looks like something went wrong. Please get in touch with the site <a href="mailto:<?php echo get_bloginfo('admin_email'); ?>">administrator</a> and we'll get this sorted out</h3>
 
<?php endif; ?>
 
<?php wp_reset_postdata(); ?>

Now my loop has my custom post content showing up in it instead of the default WordPress query content.

WP_User_Query

Similar to WP_Query but for getting users out of the WordPress database.

$args = array(
    'role' => 'Administrator'
);
 
$admins = new WP_User_Query( $args );
 
foreach ( $admins->results as $users ){
    echo $users->display_name;
}

The above code is going to get me all the users that are WordPress Administrators and echo out their display_name.

Actions and Filters

Our second big idea is WordPress actions and filters.

Actions are fired when something happens in WordPress. Like our pre_get_posts action above. I happens just before WordPress actually gets our posts from the database but after the Query parameters have been set.

Filters always pass data in and allow us to change the data as it passes through. A great example is changing the stock email address that WordPress sends all emails from.

function change_email( $email ){
    return 'your@email.com';
}
add_filter( 'wp_mail_from', 'change_email' );

Our code above changes the stock email to your@email.com so all emails going out of WordPress will be from that new email address.

For more on actions and filters you should check out Pippin’s Plugin 101 course (linked later) or my book (linked later).

enqueue

No we don’t just go around throwing our JS and CSS files in script tags in our headers and footers. WordPress has a built in system to enqueue your scripts/styles for you properly.

This is going to help make sure that you don’t end up with 9 versions of jQuery messing each other up. If you’re adding a script to WordPress you should be using wp_enqueue_script hooked to wp_enqueue_scripts.

Styles should be added with wp_enqueue_style and also hooked to wp_enqueue_scripts.

If you’re adding scripts or styles to the WordPress admin use the same functions but hook to admin_enqueue_scripts.

Make sure you limit your admin scripts to only the page you need them on.

AJAX

Sure there are a bunch of ways to make an AJAX call that can work, but WordPress has a set of hooks built in just for making AJAX calls.

As usual if WordPress has a default way to do something you should just stick with that way.

Not sure how to do that yet well I’ve got great post for you.

Seperation of code for theme/plugin

I’ve actually written a whole blog post on this before so I’ll summarize now and you can go read it all.

If a feature is likely to live past the current visual style of your site then it should be in a plugin.

Build a Widget

Yes you should know how to build a WordPress Widget (and what one is).

And check out how to build a widget.

Build a Shortcode

Shortcodes can be very useful to get advanced content/layouts in WordPress.

You should know how to build one if you want to know WordPress. Check out the Plugin Development 101 link later for some training on building a shortcode.

Escape all the things

The first 2 rules of dealing with users on the web are:

Never trust the user

Same goes for rule 5 and 8.

To that end you need to escape and sanitize input/output in the database. WordPress comes with a bunch of escaping functions that you should be using first. Then see what PHP has. Then get fancy and write your own escaping functions.

Theme Review Guidelines

“Hey” you say, I’m going to build plugins not themes so I can skip the Theme Review Guidelines.

No you can’t.

Your plugins are still going to have to interface with WordPress themes.

You will have to dig in to a theme for client work and make some changes.

You should know what the best practices are in building a theme.

Who to Follow

So there is some foundational knowledge for getting to know WordPress

Tom McFarlin

Tom is a solid WordPress developer with a background in a few other languages. His main blog covers business, WordPress and life. He’s done an excellent series lately on the (philosophy on WordPress](http://tommcfarlin.com/wordpress-philosophy-the-vocal-minority/).

Tom is also the WordPress editor for code.tutsplus.com.

Pippin

Pippin is a very smart developer and the founder of Easy Digital Downloads. He writes tutorials on WordPress pretty consistently and if you’re looking at getting in to plugin development he has a great Plugin Development 101 series.

Don’t forget to listen to Apply Filters which is a developer focused WordPress podcast.

Paulund

Paul Underwood has been killing it lately with great tutorials on how to build WordPress properly. Look through the site and make note of all the awesome resources.

Brad Touesnard

Brad focuses on a few things, not just WordPress tutorials. He has a great view of the WordPress landscape from code to building a product.

One of his posts that I adopted right away was this one about extending WP_Query.

Curtis McHale

Yeah that’s me and I write on my personal site about business mainly.

This is WP Theme Tutorial and here I write about building things with WordPress.

I also wrote a book called Becoming a WordPress Development Professional that covers most of the content above in more depth. It’s meant as a guideline for you to start your journey to becoming awesome with WordPress.

WHOA!!!

Yeah that’s a lot that I really think a WordPress developer should know. Seems pretty daunting of you’re just starting out doesn’t it.

WordPress isn’t just some ‘easy’ thing you can pick up and be awesome at. It takes some real work. I think that a solid developer from another language is likely to be able to get most of this in a few months of projects.

Sure there will still be edge cases they don’t quite know how to handle but they’ll be a decent developer that’s not writting terrible code.

If you’re just starting remember I was just starting to and I wrote bad code. You’re going to write bad code as well. No way around that.

Find a code mentor that’s willing to do some reviews for you. Send your first 5 projects to them and ask about what your doing and a critique.

You’ll learn way faster that way.

easy digital downloads form HTML

Moving the Easy Digital Downloads Purchase link to the top of your WordPress Content

The default Easy Digital Downloads styling is quite good. It’s by and large going to fit with most themes.

But not always.

As I was building the plugin store for my agency site SFNdesign I needed to move the purchase buttons above the content. By default they are displayed below the content on your page.

Today we’re going to look at how you can move the EDD purchase link above you content.

If you just want the code, jump to the end it’s right above the screencast.

Finding the Content

The easiest way to find where our purchase form is to right click on the purchase form and ‘view source’ or ‘inspect element’ depending on your browser tools.

easy digital downloads form HTML

See where it says id="edd_download_purchase_form" we’re going to search for that ID in the EDD plugin. So that means you need to search inside your project for the edd_download_purchase_form string.

We’re going to get a few results, but the important one can be found in the includes/template-functions.php file.

Our purchase form sits inside the edd_get_purchase_link function. Now we need to find where that is called.

For this search we can simply search inside the current file.

That’s going to lead us to the code below found near the top of our template-functions.php file.

/**
 * Append Purchase Link
 *
 * Automatically appends the purchase link to download content, if enabled.
 *
 * @since 1.0
 * @param int $download_id Download ID
 * @return void
 */
 
function edd_append_purchase_link( $download_id ) {
    if ( ! get_post_meta( $download_id, '_edd_hide_purchase_link', true ) ) {
        echo edd_get_purchase_link( array( 'download_id' =&gt; $download_id ) );
    }
}
add_action( 'edd_after_download_content', 'edd_append_purchase_link' );

Now we know that using the edd_after_download_content hook EDD adds the purchase link to the end of our content.

Now that’s not a standard WordPress hook so before we move on lets see where it’s added to our site. We can find it on line 273 or see it below.

/**
 * After Download Content
 *
 * Adds an action to the end of download post content that can be hooked to by
 * other functions.
 *
 * @since 1.0.8
 * @global $post
 *
 * @param $content The the_content field of the download object
 * @return string the content with any additional data attached
 */
function edd_after_download_content( $content ) {
    global $post;
 
    if ( $post &amp;&amp; $post-&gt;post_type == 'download' &amp;&amp; is_singular( 'download' ) &amp;&amp; is_main_query() &amp;&amp; !post_password_required() ) {
        ob_start();
        do_action( 'edd_after_download_content', $post-&gt;ID );
        $content .= ob_get_clean();
    }
 
    return $content;
}
add_filter( 'the_content', 'edd_after_download_content' );

You can see that it uses the_content filter and adds the action to the end of our content.

There is also a corresponding edd_before_download_content filter that should put your content before the download content. Note that currently it doesn’t actually put your content before all of the WordPress content which seems odd to me and I opened an issue on it.

Now moving that button.

So far we know that our button is added via the edd_after_download_content hook. Our goal is to move it to the top of our WordPress content.

That means we need to use remove_action to remove it from it’s current place.

Then we need to add it back to our post content at the top.

Let’s see the code that does it. It should go in your theme (or child theme) functions.php file.

&lt;?php
/**
 * Puts the purchase links before the content
 *
 * @since 1.0.0
 * @global $post
 * @author SFNdesign, Curtis McHale
 *
 * @param $content The the_content field of the download object
 * @return string the content with any additional data attached
 */
function sfn_edd_purchase_at_top( $content ) {
    global $post;
 
    if ( $post &amp;&amp; $post-&gt;post_type == 'download' &amp;&amp; is_singular( 'download' ) &amp;&amp; is_main_query() &amp;&amp; !post_password_required() ) {
        ob_start();
        $download_id = absint( $post-&gt;ID );
        if ( ! get_post_meta( $download_id, '_edd_hide_purchase_link', true ) ) {
            echo edd_get_purchase_link( array( 'download_id' =&gt; $download_id ) );
        }
        $new_content .= ob_get_clean();
        $content = $new_content . $content;
    }
 
    return $content;
}
add_filter( 'the_content', 'sfn_edd_purchase_at_top' );
remove_action( 'edd_after_download_content', 'edd_append_purchase_link' );

Right at the bottom we start with removing the purchase link from our hook.

Then we us add_filter on the_content. the_content filter runs on all the WordPress content so that’s the $content variable being passed in.

Now we need to make sure that we only run this filter if we are:

  1. On a download post type $post->post_type == 'download'
  2. Singular download, not an archive page is_singular( 'download' )
  3. In the main query is_main_query()
  4. Not on a password protected post ! post_password_required()

If all those conditions are met we need to start an output buffer. That’s going to let us capture the content in our edd_get_purchase_link function regardless of if it’s sent with return or echo.

Now we capture the output of edd_get_purchase_link with our $new_content var and the ob_get_clean function.

Then we make our original post $content equal to our $new_content plus add on the original $content to the end.

We finish off be returning the content to our filter so that we actually have something output on the page.

That’s it! We now have our purchase links at the top of our WordPress content.

Screencast

Does your online store need a slider?

Go take a look around at online stores, heck even look at a basic website, you’re likely to see a slider. How many people actually question what a slider is really good for though?

Lets start by looking at why companies and designers put sliders on sites.

Why Sliders???

Theoretically adding a slider to a site does a few things for you.

  1. Gets more important content on your site. You may have more than 1 important product or marketing message to highlight so a slider puts 2 or 3 or 4 on the homepage which of course is the most important page on your site.
  2. Allows you to engage users with differing desires on your sites. So the construction worker can see power tools on one slide at your hardware store and the home shopper can see the garden furniture they are thinking about.
  3. Tell marketing or management that their latest ‘great idea’ is on the homepage

The reality is that the only thing that sliders effectively accomplish is the 3rd item above. They let you tell marketing or management that their latest idea is now on the homepage and is important.

Sliders make it easy for you to never have to say NO to a content idea on someones site. NO is hard to say sometimes and not having to say it is easy.

Okay so I say that sliders aren’t really that useful. How about we look at some stats to prove it.

How Users Click on Sliders

Let’s start by talking about how many users interact with sliders. A common percentage of users to click on a slider is around 1%. Yes that’s right, if 100 people visit your site then 1 person will click on it.

Not only that, but out of that 1% of people that actually click on the slider, 80% of them are clicking on the first slide.

The slider itself is effectively dead based on user interaction but anything past the first slide is even deader.

User Interaction with Slider Content

What about if we gave a user a specific task like we would in a user testing scenario. This study asked users to find if Siemens have any discounts on washing machines?

Guess what, despite the washing machine discount being the biggest thing on the page in the biggest font the user failed to find the discount. They actually gave up figuring that Siemens didn’t have sales and said

I wouldn’t choose [a Siemens appliance] unless I was very rich

Yeah that’s right they couldn’t find the biggest thing on the page and Siemens is expensive. Good job sliders.

Relevancy

Surely sliders let you serve relevant content to a wider range users though. Right??

Well first off, is the slider actually displaying content relevant to the current visitor? Maybe if their coming to see your sale on garden furniture in spring but that also means that the person coming to look for power tools gets to see the sales on garden furniture which have little to do with their point in coming to the site.

Next, how long do you stay on a slide. Assuming that the user is in fact the right user for your slider is it going to switch to fast for them? Usually it doesn’t stay around long enough and just as a user decides that they want to click on it the slide changes.

Then they have to decide again if they want to engage with the current content.

So you just keep throwing new cognitive load on a user.

Site Speed Impacts

Even outside of user interactions, how to sliders affect with site speed? We already know that Google looks at sitespeed for your rankings so it’s an important consideration.

When you load a slider you’re loading:

  • All the images
  • All the JS needed to make the slider work (and JS is blocking)
  • Extra HTML and PHP to hold the slides

That’s a lot of stuff to load. Looking at this comparison of NEXTGEN Gallery you’ll see that it takes 5 seconds to load. Then take a look at this evaluation of popular WordPress sliders, a bunch of them take 4 seconds to load.

So you’re going to add 4 seconds of time to load your site in many cases and Google will penalize your ranking for that slowness.

Not only that, but a 1 second delay can reduce your conversions by 7%. That real money left on the table on a store and I like money.

The simple fact is that adding load time to your site means decreasing your conversions which means you’re going to make less money.

SEO Impacts

First off it’s likely that your slider code has lots of headings thereby cluttering up your page hierarchy with content to index. Lots of them actually make everything an H1 heading, which means it’s the most important thing on the page.

Some even use flash still? (Yeah WTF).

Oh and we just talked about sitespeed and SEO.

Even worse than the above SEO issues is that so many sites just put shallow content in the sliders. A slider is used as a copout for a decent content strategy. They toss well written, well thought out content on the homepage away in favour of a slider.

If you have little or no content relevant to the keywords your trying to rank for how on earth do you expect to rank for them?

Instead of using a slider for content, get a content strategy and put well thought out content on your homepage.

Accessibility

Have you ever tried to use the modern web with an assistive device like a text based web browser? Go ahead and download Lynx and try it, the book will be here.

So how much fun was that? Not fun at all was it. Some people have things like Lynx as their fulltime web experience.

Did you know that web accessibility is law in the UK?

Okay so how do sliders affect accessibility?

The simple fact is that sliders are bad for accessibility. They’re going to make your site harder for users to navigate.

Sliders and Mobile Devices

Did you know that mobile shopping grew by 21% in the 2013 holiday season in the US? Users are going to come to your site and shop via a mobile device of some sort.

First off we know that sliders are slow (see above) and of course fast internet speeds are not everywhere. LTE only become fairly common in my city in the last 6 months and there are still lots of spots that only give you 3G speeds.

Not only are they slow, you’ve got to load all those images. Few sliders actually provide smaller images to mobile devices. They usually just downsize the large image via CSS.

Sure gigabytes of bandwidth are cheap in some countries but in Canada I pay twice what it costs my US friends for 1GB of transfer in a month. Your big slider is just pushing me to my data cap (and extra cost).

Most mobile users actually just scroll past a slider on a site that is if they don’t get stuck in some hell where a swipe up is translated in to a slide change by the slider. Then they may just leave the site.

But ‘The Fold’

Okay here’s our last point of discussion, the fold.

The fold started as a newspaper term. You put your best content at the top of the page ‘above the fold’ since that is what people would see and would attract them to purchase the paper.

When it came to web ‘the fold’ started to mean the part of the page on your current screen and screens were around 600px tall. Oh and the internet was really, really, really slow. So it might take a minute to load a web page, each click was super precious.

When a user hit your page you wanted to make sure that the first content was so engaging that they’d look more on your site because a click was so precious to the user.

Did I mention that users didn’t really scroll either. They just looked at what was visible and decided if it was worth it.

If that was all true, maybe a slider would get more content in to that top portion of the screen that was so precious. Right?

Now the page fold is a myth. There is life below 600px. The myth of the fold has been busted.

So there is no fold and using a slider to get content above the fold is a waste of time.

One good time for sliders

At the beginning I said that all a slider really does is let you tell the marketing department or management that their latest idea is on the homepage. That is a valid reason for a slider.

I wouldn’t turn down a project that ‘had to have’ a slider at all. I’d work to educate them and if they had to have it I’d use the best option available which would be Envira.

Like this content? It’s part of an upcoming book I’m writing on getting started with eCommerce for WordPress. You can sign up for the email list for updates or order the book now to get all the content as it comes out and early release versions. That means purchase now for book + videos at $29.99 instead of $150.

How I setup WordPress for Development – Part 1

Every site I deal with has some initial setup to deal with. First off I don’t want to email site users by accident from staging or local enivornments. Second I want to be able to log a bunch of data in the system like password resets…Third I want to be able to change configurations based on my environment.

An example of the third item would be restricting access to the site on the staging server, but not on live/local. I don’t want to go in and make admin changes if at all possible so I automate it all via code.

Today we’re going to look at how I deal with email logging and my starting setup for site environments.

Developer Constants

To start we need an mu-plugins folder in our wp-content folder. If you’re not familiar with mu-plugins, it’s a special folder that runs before all other plugins and it’s a MUST USE. So any code in there is always activated and running.

Now in to our mu-plugins folder we need to add our Developer Constants plugin to start defining our environments.

Place it in a folder called wptt-developer-constants. So our files should look like:

  • wp-content
    • mu-plugins
      • wptt-developer-constants/wptt-developer-constants.php

A quirk of the mu-plugins folder is that it won’t execute code inside folders. That means we need to add a bootstrap file at the base level to include all of our included plugins.

Go ahead and create a file called wptt-bootstrap.php inside the mu-plugins folder and put the code below inside it.

<?php
// calls our developer constants plugin
require_once( 'wptt-developer-constants/wptt-developer-constants.php' );

Now we’re actually calling our developer constants so lets configure the constants for our environments.

Open the wptt-developer-constants.php file and take a look at the first few lines inside the class.

    protected $live = array(
        'http://yourlivesite.com'
    );
 
    protected $staging = array(
        'http://yourstagingsite.com',
    );
 
    protected $local = array(
        'http://yourlocalsite.com',
    );

Here we have 3 variables that need to be changed to our environments. For the WP Theme Tutorial Screencasting environments I have them set as:

    protected $live = array(
        'http://wpthemetutorial.com'
    );
 
    protected $staging = array(
        'http://staging.wpthemetutorial.com',
    );
 
    protected $local = array(
        'http://screencast.wpthemetutorial.com',
    );

Now WordPress has another ‘quirk’ that I’ve never really been able to figure out. If we only define our environments here then password resets won’t be captured as expected because the variables aren’t defined yet.

I’m guessing it has to do with the same ‘quirk’ I blogged about a few weeks when I talked about wp_mail not always being pluggable.

Anyway the way to make sure our environments are defined properly is to add a few lines to your wp-config.php file as well.

$local = array(
    'http://screencast.wpthemetutorial.com',
);
 
define( 'LIVE_ENV', serialize( $local ) );
 
$staging = array(
    'http://staging.wpthemetutorial.com',
);
define( 'STAGING_ENV', serialize( $staging ) );
 
$live = array(
    'http://wpthemetutorial.com'
);
define( 'LIVE_ENV', serialize( $live ) );

Now you may ask why we serialize the array? Constants can’t handle array’s which means we need to serialize them. I’ve had more than one site that is multisite, which means that our live/local/staging environments have multiple URL’s for each environment.

Adding WP_Logging

My second step is to add WP_Logging so I can log events for my sites. It may be an error case (like if login failed for some reason) or maybe I’m chasing a bug.

Whatever the case is I almost always find a reason to log something during development.

We’ll add WP_Logging to our mu-plugins folder in a folder called wp-logging.

Then we need to include it with our bootstrap file. Add the code below to wptt-bootstrap.php.

// getting WP_Logging ready
require_once( 'wp-logging/WP_Logging.php' );

Now WP_Logging is installed and we can use it but we can still encounter an issue. Currently WP_Logging would just log things and never prune the logs. We want some sort of log pruning, which is included in WP_Logging but turned off by default.

In our wptt-bootstrap.php file we need to add to turn on pruning with the code below.

/*
 * Turns on WP_Logging Pruning
 */
function wptt_activate_pruning( $should_we_prune ){
    return true;
} // activate_pruning
add_filter( 'wp_logging_should_we_prune', 'wptt_activate_pruning', 10 );
 
// sets the cron job for pruning
$scheduled = wp_next_scheduled( 'wp_logging_prune_routine' );
if ( $scheduled == false ){
    wp_schedule_event( time(), 'hourly', 'wp_logging_prune_routine' );
}

First we turn on the pruning in WP_Logging then we set up an hourly cron job for pruning. By default WP_Logging will prune logs older than 2 weeks, but there is a filter where you can change that time.

There are a number of other configuration changes that can be made, go check out the docs on WP_Logging to see.

Logging Email with WPTT Basic Email Logging

Now we’re ready to start logging emails. Download the plugin above from Github and add it to your plugins folder.

If you skipped adding WP_Logging to your mu-plugins folder don’t worry. WPTT Basic Email Logging includes a copy if it doesn’t find a copy of WP_Logging already installed.

Once the plugin is installed, activate it and we’re rocking email logs on our staging and local environments.

Next week I’ll walk through some of the other ways I use the environment constants and WP_Logging to catch things on the site.

Screencast