Simple Instagram WordPress Widget, pt 1

One of the things I love about WordPress is its massive developer community. You know that brilliant feature you dreamt up? Somebody has probably already baked it into a theme or plugin. This is generally awesome, because it allows for rapid iteration and faster build times.

Too much of a good thing?

But every now and then, I feel this twinge of guilt that the bottomless pool of plugins is making me lazy. It’s almost too easy to pick up someone else’s code and use it without thinking. That’s why from time to time I like to roll up my sleeves and do something the old-fashioned (read: complicated) way.

So you want to build an Instagram widget

Awhile back I decided to add a simple grid of Instagram images to my site. The requirements were simple:

  1. Show a grid of recent images from my Instagram account.
  2. Each image should link to its corresponding location on Instagram.com.
  3. For images with captions, the caption should get pulled in as the link’s title and alt attributes.
  4. The whole thing should function as a WordPress widget. The number of images, user account ID, and widget title/subtitle should all be configurable.

Needless to say there are approximately a bazillion plugins floating around that offer this exact functionality. But since I knew Instagram has a super straightforward RESTful API, this seemed like a perfect excuse to learn a bit more about building WordPress widgets.

The widget framework

It took about five seconds of digging through the WordPress Codex to find the section about creating a widget. They helpfully provide an example that gives us a bare-bones structure to work with (all of which gets dropped into our theme’s functions.php file).

To kick things off, we extend the WP_Widget class and construct our widget. This is where we provide the ID, name & description that appears in the WordPress admin:

<?php class Instagram_Widget extends WP_Widget {

    // Register widget with WordPress.
    function __construct() {
        parent::__construct(
            'instagram_widget', // Base ID
            __('Instagram', 'text_domain'), // Name
            array( 'description' => __( 'Displays your recent images from Instagram.', 'text_domain' ), ) // Args
        );
    }

    } ?>

The back end widget form

Okay. So far so good. Now we need to figure out what fields we need to capture in the widget settings on the back end to output on the front end. We will need:

  1. A widget title
  2. An optional subtitle (just for good measure)
  3. An Instagram API key (unless you’re the only one who will ever use this widget, in which case you could always hard code it)
  4. A user ID (so we know which account to pull images from)
  5. A photo count (to manipulate the number of photos to display)

After looking at the boilerplate code provided by WordPress, it appears we’ll need to first designate some variables for each of our form fields, and then write our HTML with the corresponding <label> and <input> elements. Then for security we sanitize each of those form values as they’re saved by using strip_tags()

<?php /* Back-end widget form */
    public function form( $instance ) {
        if ( isset( $instance[ 'title' ] ) ) {
            $title = $instance[ 'title' ];
        }
        else {
            $title = __( 'New title', 'text_domain' );
        }
        if ( isset( $instance[ 'subtitle' ] ) ) {
            $subtitle = $instance[ 'subtitle' ];
        }
        if ( isset( $instance[ 'key' ] ) ) {
            $key = $instance[ 'key' ];
        }
        if ( isset( $instance[ 'user' ] ) ) {
            $user = $instance[ 'user' ];
        }
        if ( isset( $instance[ 'count' ] ) ) {
            $count = $instance[ 'count' ];
        }               
        ?>

        <p>
            <label for="<?php echo $this->get_field_id( 'title' ); ?>"><?php _e( 'Title:' ); ?></label> 
            <input class="widefat" id="<?php echo $this->get_field_id( 'title' ); ?>" name="<?php echo $this->get_field_name( 'title' ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>">
        </p>

        <p>
            <label for="<?php echo $this->get_field_id( 'subtitle' ); ?>"><?php _e( 'Subtitle:' ); ?></label> 
            <input class="widefat" id="<?php echo $this->get_field_id( 'subtitle' ); ?>" name="<?php echo $this->get_field_name( 'subtitle' ); ?>" type="text" value="<?php echo esc_attr( $subtitle ); ?>">
        </p>

        <p>
            <label for="<?php echo $this->get_field_id( 'key' ); ?>"><?php _e( 'Instagram API key:' ); ?></label> 
            <input class="widefat" id="<?php echo $this->get_field_id( 'key' ); ?>" name="<?php echo $this->get_field_name( 'key' ); ?>" type="text" value="<?php echo esc_attr( $key ); ?>">
        </p>

        <p>
            <label for="<?php echo $this->get_field_id( 'user' ); ?>"><?php _e( 'Instagram User ID:' ); ?></label> 
            <input class="widefat" id="<?php echo $this->get_field_id( 'user' ); ?>" name="<?php echo $this->get_field_name( 'user' ); ?>" type="text" value="<?php echo esc_attr( $user ); ?>">
        </p>

        <p>
            <label for="<?php echo $this->get_field_id( 'count' ); ?>"><?php _e( 'Number of images to display:' ); ?></label> 
            <input class="widefat" id="<?php echo $this->get_field_id( 'count' ); ?>" name="<?php echo $this->get_field_name( 'count' ); ?>" type="text" value="<?php echo esc_attr( $count ); ?>">
        </p>                

<?php 
    }

    /* Sanitize widget form values as they are saved. */
    public function update( $new_instance, $old_instance ) {
        $instance = array();
        $instance['title'] = ( ! empty( $new_instance['title'] ) ) ? strip_tags( $new_instance['title'] ) : '';
        $instance['subtitle'] = ( ! empty( $new_instance['subtitle'] ) ) ? strip_tags( $new_instance['subtitle'] ) : '';
        $instance['key'] = ( ! empty( $new_instance['key'] ) ) ? strip_tags( $new_instance['key'] ) : '';
        $instance['user'] = ( ! empty( $new_instance['user'] ) ) ? strip_tags( $new_instance['user'] ) : '';
        $instance['count'] = ( ! empty( $new_instance['count'] ) ) ? strip_tags( $new_instance['count'] ) : '';

        return $instance;
    }
?>

At first glance, this looks a bit overwhelming. But it’s actually a pretty basic pattern that gets repeated for each form field you want to include.

First there is a form() function that retrieves the values of each form field if they’ve already been saved, and provides the markup for how the form fields should be output on the front end. Of critical importance here is making sure your input name and value match up with the names included at the top of the function.

Then there’s the update() function. This just creates an array that is updated with the form values the user has entered. For security, it also uses strip_tags() to sanitize the data.

The front end display of the widget

Now it’s time to work our way up and create a widget() function that controls the output of our widget on the front end:

<?php /* Front end display of widget */
    public function widget( $args, $instance ) { /* 1 */
        $title = apply_filters( 'instagram_widget', $instance['title'] ); /* 2 */
        $subtitle = apply_filters( 'instagram_widget', $instance['subtitle'] );
        $key = apply_filters( 'instagram_widget', $instance['key'] );
        $user = apply_filters( 'instagram_widget', $instance['user'] );
        $count = apply_filters( 'instagram_widget', $instance['count'] );

        echo $args['before_widget']; /* 3 */
        if ( ! empty( $title ) ) {
            echo $args['before_title'] . $title . $args['after_title'];
        }
        if ( ! empty( $subtitle ) ) {
            echo $args['before_content'] . $subtitle . $args['after_content'];
        }
        if ( ! empty( $key ) && ! empty( $user ) && ! empty( $count )  ) { /* 4 */
            
            echo $args['before_content'];
            
            echo '<input type="hidden" id="insta-key" val="' . $key . '" />';
            echo '<input type="hidden" id="insta-user" val="' . $user . '" />';
            echo '<input type="hidden" id="insta-count" val="' . $count . '" />';

            echo $args['after_content'];
        }       
        echo $args['after_widget'];
    } ?>

Let’s walk through this step by step:

  1. First we create our widget() function. This accepts an array of WordPress arguments ($args) and our $instance array storing all of that juicy widget content.
  2. Then we declare some variables for each of our pieces of widget content that we’ve stored on the back end. We can name these variables whatever we want, but what’s important is that both items in apply_filters() match our widget ID and array item names we used earlier.
  3. Next we echo some WordPress arguments: before_widget, followed by our widget title and subtitle (if they exist), which get wrapped in WordPress before_title / after_title and before_content / after_content, respectively.
  4. Finally it’s time to spit out our data. We check to confirm that the necessary values exist on the back end (ideally this would be optimized with some error messaging just in case those values don’t exist, but for simplicity we will skip that step here). If they do, we place the values in a hidden input (to be picked up later by our JavaScript), and wrap them in before_content / after_content.

Handing off our PHP data to JavaScript

As an aside: There are a variety of opinions on how to best manage sharing data between the back-end and front end, and if it’s a topic you aren’t familiar with I highly recommend reading this Stack Overflow answer which outlines some popular approaches.

Because building long strings of JS in PHP can get annoying, I’ve opted to echo out the contents from our PHP to a hidden input on the page. That way the JS can live safe and sound in a separate file and just grab the values of those fields when it needs them.

Security

One thing to keep in mind is that for most use cases, we’d want to avoid sending our API key to the front end of the site, since technically somebody could steal it for use in their own applications. Usually it’s better to have this safely tucked away and executed on your server, especially if you are dealing with a service that allows you to edit or remove sensitive information.

For this project, however, I’m not too worried about it: Instagram has restrictions in place to ensure requests with this key can only originate from my domain, and the information you can retrieve with it is read-only data that’s already publicly available on Instagram.

To be continued…

We’re almost there! This covers all of the setup required to build out the structure of our little widget. In Part 2, I’ll cover the JavaScript we use to make our AJAX call and pull in those Instagram photos.

Continue to Part 2