Fun with Integration

WordCamp Birmingham 2012

Fun with Integration

Using WordPress APIs to connect sites to everything else.

Samuel “Otto” Wood
WordCamp Birmingham 2012
http://wpyall2012.ottopress.com

About Me

Otto
otto@wordpress.org
otto@ottodestruct.com
http://ottopress.com
Twitter: @otto42
http://profiles.wordpress.org/users/Otto42

I write Plugins

(see anything in common?)

APIs

  • How they work (somewhat)
  • How to use them (to a degree)
  • How to use them in WordPress code (hopefully)

WordPress

It’s good for a lot of things.
(This presentation is being displaying with WordPress)

Many people use other things too.

Third party APIs

Step the First

One of the laws of software development is that all documentation sucks.

API documentation sucks more.

It’s made by developers, for developers. You are therefore not expected to understand it.

Too bad that you often have to do just that.

How to read API documentation:

  • As a guideline
  • As a conceptual idea
  • Mostly, as if it’s actually incorrect.
  • Because it probably is.

API Documentation is invariably out-of-date, old, unmaintained, or sometimes just flat out wrong.

https://dev.twitter.com/docs/api

https://developers.facebook.com/docs/reference/api/

https://developers.google.com/+/api/

Step the Second

Code Libraries

Most APIs offer pre-made libraries in several languages.

Don’t use them.

Or rather, use them to learn how the API works, then write your own code.

The problem with libraries

  • You end up spending time learning the library instead of the fundamental principles
  • If you use the library in public code, then it often conflicts with other plugins/themes using the same library
  • Libraries often don’t mesh as well with your own code or style
  • Not using the library is often easier, in the long run

Javascript libraries

Facebook and Twitter and others sometimes offer Javascript libraries for various things.

Sometimes, using them isn’t optional, and can be okay if you’re careful.

Use wp_enqueue_script() properly.

Step the Third

Fun with “Applications”

All modern services require that you set up with them in advance. This is usually called an “Application”.

https://developers.facebook.com/apps
https://dev.twitter.com/apps
https://code.google.com/apis/console/ (They call it a “Project”)

“Free”-ish

Sometimes you’ll need to apply to be a developer or otherwise prove you’re not a spambot.

For example, Facebook requires a SMS or credit card verification (it’s still free in cost though).

Setting up an Application

Setting up an app involves telling them your URLs and other non-important “display” information.

Note of importance: URLs must be exact.

If you don’t have a “www” in your URL, then don’t put one in the application. (It’s shocking how many people get this wrong.)

Side effects

Side effect of this: Your development site generally must be LIVE on the internet.

Side-side effect of this: Applications are generally tied to the site, you will need a separate one for a dev site in many cases.

When you create an Application on any service, you’ll get 2 new pieces of information (minimum):

  • An application identifier of some sort
  • A “secret”.

Secrets are used to validate your API requests. They’re kinda like a password.

About “Secrets”…

Secrets are called “secrets” for a reason.
Don’t give them away, to anybody, ever.

If you do that by accident, then most services have a way to reset your secret and get a new one.

Note that this means that you cannot put your secret into a public plugin or theme.

Step the Fourth

Authentication

The first thing you need to do with any API is to tell it who you are.

This is invariably the hardest thing to understand how to do.

OAuth is dead, long live OAuth 2

All major services now use OAuth2.

Most have no other way to do it anymore.

Learning OAuth

You’ll find images like these.
They rarely help.

OAuth is not complicated,
just confusing.

The OAuth Dance

OAuth – Step 1

Build a link to the service you want to authenticate through.

https://accounts.google.com/o/oauth2/auth
?response_type=code
&client_id=736121523937.apps.googleusercontent.com
&redirect_uri=http://ottopress.com/oauth2callback
&scope=https://www.googleapis.com/auth...
&approval_prompt=auto
&access_type=online
&state=popup_comments

(Docs: http://code.google.com/apis/accounts/docs/OAuth2Login.html)

OAuth – Step 2

Get the user to click on that link. (Bribes, trickery…)

  • They’ll be sent to the link in question, and it will redirect that user back to your “callback” with some parameters.
  • You intercept those parameters, and decode them using your “secret”.
  • When the secret decrypts the parameters correctly, you will have a valid “token”.

OAuth – Step 3

The token you get will be an “authorization” token.
Some people (Google) call it a “code”.

You have to convert the authorization token into an “access” token.

This means making a call on the back-end (not through the users browser).

Live on ottopress.com

When Google redirects the user back to http://ottopress.com/oauth2callback, they’ll pass me a code, like so:

http://ottopress.com/oauth2callback?code=1234567890

Code in Simple Google Connect to catch that callback using the rewrite engine

add_action('init','sgc_add_rewrite');
function sgc_add_rewrite() {
  global $wp;
  $wp->add_query_var('oauth2callback');
  add_rewrite_rule('oauth2callback?$', 'index.php?oauth2callback=1', 'top');
}

Code to do the token exchange (simplified)

function sgc_get_token($code) {
  $req['code'] = $code;
... other variables here ...
  $args['sslverify'] = false;
  $args['body'] = $req;
  $data = wp_remote_post('https://accounts.google.com/o/oauth2/token', $args);
  if ( is_wp_error( $data ) || 200 != wp_remote_retrieve_response_code( $data ) )
    return false;
  $resp = json_decode( wp_remote_retrieve_body( $data ), true );
  return $resp;
}

Basically, it makes a POST with the code and some other data to https://accounts.google.com/o/oauth2/token, which sends me back the access token (or an error if the token ain’t legit).

That token I get back I can then use in all my requests to the service from then on.

Store it somewhere appropriate.

Usermeta, options, whatever makes sense.

Step the Fifth

WordPress APIs

Stuff I just used

wp_remote_post
wp_remote_retrieve_response_code
wp_remote_retrieve_body

Lots more useful stuff in WP_HTTP

wp_remote_get
wp_remote_post
wp_remote_head
wp_remote_retrieve_headers
wp_remote_retrieve_header
wp_remote_retrieve_response_code
wp_remote_retrieve_response_message
wp_remote_retrieve_body
wp_http_supports

Fun with SSL

Most hosting systems have outdated Certificate Authority lists.

This breaks https requests on a lot of servers.

Workaround

$args['sslverify'] = false;
...
$data = wp_remote_post('https://accounts.google.com/o/oauth2/token', $args);

Less secure, but still encrypted and it works.

If security is of major importance, don’t use this.
Update the CA lists on your PHP installation instead.

Some systems just can’t talk SSL at all

Use something like this to see if they can:

if ( !wp_http_supports(array(), "https://example.com/") ) {
	// SSL is broken on this host
}

Step the Sixth

Integrating into WordPress

Login

Simple Facebook Connect
Simple Twitter Connect
Simple Google Connect

will all let you login to WordPress using credentials from other services.

The “authenticate” filter lets them do that

add_filter('authenticate','sfc_login_check',90);
function sfc_login_check($user) {
  if ( is_a($user, 'WP_User') ) return $user;
  $cookie = sfc_cookie_parse();
  $fbuid=$cookie['user_id'];
  if($fbuid) {
    ... bunch of code to get the user from the database based on info in the cookie ...
    $user = new WP_User($user_id);
  }
  return $user;
}

The authenticate filter should return a WP_User object if it can successfully identify the user, via whatever means.

WordPress handles the rest transparently.

SGC uses a different approach

add_action('sgc_state_popup_login', 'sgc_login_user_popup_handler');
function sgc_login_user_popup_handler($oauth) {
  if ( !empty( $oauth['token']['access_token'] ) ) {
    ... get user info from db based on the access info from google ...
    wp_set_auth_cookie($user_id, true);
    $redirect = admin_url();
  }?>
  ... output some HTML and javascript ...
  exit;
}

wp_set_auth_cookie for the validated user sets the proper cookies in the users browser, then Javascript code in the popup window redirects the main window and closes the popup window.

This approach is only used because of the homemade popup window, really.

Commenting

SFC/STC/SGC all allow commenting using credentials from those services.

How that works

add_filter('pre_comment_on_post', 'sfc_comm_fill_in_fields');
function sfc_comm_fill_in_fields($comment_post_ID) {
  ... code to get FB info ...
  $url = "https://graph.facebook.com/{$uid}/?fields=name,email&access_token={$token}";
  $data = wp_remote_get($url, array('sslverify'=>0));
  ...
  $_POST['author'] = FB INFO;
  $_POST['url'] = FB INFO;
  $_POST['email'] = FB INFO;
}

It fills in the $_POST variables, overriding the author/url/email fields, and then lets the normal comment process handle it.

It doesn’t try to process the comment itself, for example. If it did, it might miss something like spam filtering.

Try to add-on to the core code, not replace it.

Twitter Avatars

add_filter('get_avatar','stc_comm_avatar', 10, 5);
function stc_comm_avatar($avatar, $id_or_email, $size, $default, $alt) {
  ...
  $twuid = get_comment_meta($id_or_email->comment_ID, 'twuid', true);
  if ($twuid) {
    $avatar = "<img ... twitter avatar ... />";
  }
  return $avatar;
}

The Twitter comment code stores the twitter user ID with the comment when it’s made, as comment-meta. The avatar code simply checks for that and returns the proper HTML for a twitter avatar when it’s there.

Doing things with posts

There’s some simple filters like “publish_post” and such, but I prefer to have more control over when events occur with posts changing, so I almost always use “transition_post_status”.

add_action('transition_post_status', 'stc_publish_auto_check',10,3);
function stc_publish_auto_check($new, $old, $post) {
  if ($new == 'publish' && $old != 'publish') {
    $post_types = get_post_types( array('public' => true), 'objects' );
    foreach ( $post_types as $post_type ) {
      if ( $post->post_type == $post_type->name ) {
        stc_publish_automatic($post->ID, $post);
        break;
  } } }
}

Here, I only send a post to twitter when a post goes from “not-published” to “published” and it has a post type marked as “public”.

The function on this hook gets the new status, the old status, and the post object itself as parameters.

Thus, you can check to see what’s going on and act accordingly.

Keeps you sane

Bottom line: Figure out what you want to do, then find the least intrusive way to do it.

Benefits: Compatibility, sanity

Warning: Everybody else will still be doing-it-wrong regardless of what you do.

Questions?

Ask!

This presentation theme is based on the Shower presentation template:
https://github.com/pepelsbey/shower

css.php