Wednesday, March 12, 2014

How to get the Twitter tweets for a screen name using Application-only OAuth (PHP & cURL)

For a recent project, the designers had a sidebar that displayed the 5 most recent tweets from the client's twitter account. I was surprised to find out it's not as easy as it used to be. Twitter now requires you to use OAuth to display tweets from a certain account or hashtag.

If you ever come across a project like this, hopefully these steps below will help you out.

Step 1: You'll need to create a Twitter application to get an API key and secret which will allow you to make API request.
  • Go to: https://dev.twitter.com/ (make sure your logged in to twitter)
  • In the upper right side of the header you should see your avatar pict and a dropdown menu arrow.
  • Select "My Applications" from that dropdown and then click the "Create New App" button.
  • Give Twitter all the details about what your app does and then click "Create your Twitter Application".
  • Once your app is created, select it (if it's not already open) and select the tab at the top named "API Keys". This should show you your API key and API secret. Copy these two values, you will need them in the next steps.

Step 2: We'll start with the back end PHP code to make this work. These code examples are from an ajax callback in Wordpress, but you should be able to fit it into your project as needed. WARN: your API key and secret should not be visible on your site so don't put them in your javascript files.

First, we take the key and secret and create an access token out of them, then we can use the access token to get an application bearer token from Twitter. The bearer token is what we will use to make all our API requests. The Twitter documentation for this can be found here: https://dev.twitter.com/docs/auth/application-only-auth
add_action( 'wp_ajax_nopriv_twitter_tweets', 'twitter_tweets_callback' );

function twitter_tweets_callback() 
{
    //settings - example values
    $api_key = 'xvz1evFS4wEEPTGEFPHBog';
    $api_secret = 'L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg';
    $access_token = base64_encode(urlencode($api_key) . ':' . urlencode($api_secret));

    //connect to the twitter api and get bearer token
    $bearer_token = get_bearer_token( $access_token );

    //---

    //search for recent tweets for the client (method below)
    $tweets_json = get_user_timeline($bearer_token, '[client's screen name here]');
    
    //echo resulting json
    echo $tweets_json;
    
    die(); // this is required to return a proper result
}

function get_bearer_token( $access_token )
{
    $ch = curl_init( 'https://api.twitter.com/oauth2/token' );
    
    $post_fields  = array(
        'grant_type'    => 'client_credentials'
    );
    
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    
    curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_fields));
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Authorization: Basic ' . $access_token,
        'Content-Type: application/x-www-form-urlencoded;charset=UTF-8'
    ));
    
    //send request
    $response = json_decode( curl_exec($ch) );
    
    curl_close($ch);
    
    return $response->access_token;
}

Step 3: Hopefully, success! You now have a bearer token which allows you to make application-only API requests to many of the different REST resources that Twitter offers: https://dev.twitter.com/docs/api/1.1.

If we continue with the code above, we can now access the /statuses/user_timeline.json resource to pull tweets for a specific screen name.
//make a GET request using the Bearer token to access tweets for a screen name
function get_user_timeline( $bearer_token, $screen_name )
{
    $url = 'https://api.twitter.com/1.1/statuses/user_timeline.json';
    $fields  = array(
        'screen_name'   => $screen_name,
        'count'         => 5,
        'exclude_replies'=> 1
    );
    
    $ch = curl_init( $url . '?' . http_build_query($fields) );
    
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_POST, 0);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        'Authorization: Bearer ' . $bearer_token
    ));
    
    //send request
    $response = curl_exec($ch);
    
    curl_close($ch);
    
    return $response;
}


Step 4: On the front-end, we can use javascript via ajax to grab the json data from PHP and generate a list of tweets to be displayed on the webpage.
var itemTmpl = [
    '<li>',
        '<span class="name"><strong>${name}</strong> @NPCpgh</span>',
        '<span class="desc">${text}</span>',
        '<span class="date">${date}</span>',
        '</li>'
    ].join('\n');

$.post('/wp-admin/admin-ajax.php', { action: 'twitter_tweets' }, function(response)
{
    //turn json array of object into html array and join
    var html = $(json).map(function(index, item) 
               {
                   return itemTmpl.replace('${name}', item.user.name)
                                  .replace('${text}', item.text);
                                  .replace('${date}', item.created_at );
               }).toArray()
                 .join('\n');

    //update display list
    $(".results").html( html );

}, 'json');


Friday, March 7, 2014

Introducing grunt-namespacer Task

Grunt has caught my eye, again, as a nice way to "build" all my javascript into a small, clean file that I can include in the websites I work on.

With the Grunt setup complete on the Wordpress site/theme I am working on, I started looking for a way to concat, closure wrap, and compress/obfuscate all the JavaScript in my source folder so I can separate out my scripts into different files (kinda like how a bunch of node.js projects work). I figured, at the most, this would be nice for utility functions that I could copy from site to site with minimal code editing.

My searches on GitHub lead me to the following tasks: grunt-contrib-concat, grunt-wrap, grunt-contrib-uglify. But, after setting up these tasks and doing some test builds, I started thinking that it would be nice if there was a task that would create namespaces automatically to avoid method name collisions. The only task I could find that was close was grunt-autowrap but it didn't do exactly what I wanted. This lead me into looking at how to build your own grunt tasks.

This is what I ended up with: grunt-namespacer.

//Gruntfile.js
grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    
    //grunt-namespacer task config
    namespacer : {
        basic : {
            src: 'js/src/app/',
            dest: 'js/build/'
        }
    }
    ...
  });

  // Load the plugins for all tasks.
  grunt.loadNpmTasks('grunt-namespacer');

  // Default task(s).
  grunt.registerTask('default', ['namespacer']);

This task will take your javascript files and concat and closure wrap them based on your folder structure.

For example, if your folder structure in your js folder is like this:
+ src/
    + app/
        init.js
        + controls/
            checkbox.js
            tabs.js
            forms.js
        + models/
            person.js
            cart.js
        + utils/
            empty.js
            json.js
            ajax.js

Then after you run the namespacer task you will get a file named 'app.js' (base folder name), and be able to access everything like this:
app.init.run();

var person = new app.models.Person('Dave');
app.utils.ajax('/register', person, function(response)
{
    person.id = response.data.id;
});

Also, scope is based on folder so any function in tabs.js can access methods in forms.js without having to use the namespace.

This task is free to use and I welcome any comments to help improve it.

later,
-Jason