This implementation is a more complex as it requires you to be re-directed to the authentication server, which then re-directs you back to your own site with the approved token. This allows LinkedIn to add additional permissions around certain content. You can authorize an application to have access to your full profile, or just your public profile.
You’ll need to register for a developer account and create a new application. You can do this here. Once you’re signed up, simply click on “Add New Application” and fill out the form.
AUTHENTICATION
GENERATING AN AUTHORIZATION CODE
The initial URL requires a some additional values along with the API keys. We first have to specify the response_type, since LinkedIn required the authentication flow this must be set to “code”. We also have to specify our client_id, this is the API key from above. Next up we have to specify the scope or level of permissions we are requesting, for this demo I’ll be using r_fullprofile. We also need to specify a random base64 code for security purposes. This is a one time use code to help mitigate CSRF vulnerabilities. I’m just using the time function and then encoding it. Finally we need our redirect URI, this is the url to our linkedin.php file that will accept the final token and store it for us. This block fits just under the submit button in add_linkedin_admin_page
<?php
$state = base64_encode(time());
$redirect = get_template_directory_uri() . '/linkedin.php';
$api_key = get_option( 'LINKEDIN_API_KEY' );
$api_secret = get_option( 'LINKEDIN_API_SECRET_KEY' );
$token = get_option( 'LINKEDIN_AUTHENTICATION_TOKEN' );
if($api_key && $api_secret && !$token) {
$api_url = "https://www.linkedin.com/uas/oauth2/authorization?response_type=code&client_id=$api_key&scope=r_fullprofile&state=$state&redirect_uri=$redirect";
?>
<a class="button-primary" type="button" href="<?php echo $api_url; ?>">Authenticate</a>
<?php
}
?>
Now when you click in the “Authenticate” button, you will be re-directed to LinkedIn and asked to confirm. (Don’t do this just yet, as we still have to add the logic to actually save the token!)
Once you’ve confirmed you wish to authenticate the application, you will be redirected to the linkedin.php file. We now have to make sure that when the linkedin.php file is requested, that it behaves appropriately and makes the final token request and saves it.
REQUESTING AN ACCESS TOKEN
In our constructor, we’re going to add a conditional section if the REQUEST_METHOD is get and the ‘code’ parameter is set. We’re also going to have to require some WordPress files so we have access to some basic functions. Since this file exists in our theme folder the require_once lines look like this
public function __construct() {
if ( $_SERVER["REQUEST_METHOD"] == "GET" && isset($_GET['code'])){
require_once('../../../wp-config.php');
require_once(ABSPATH . 'wp-includes/plugin.php');
require_once(ABSPATH . 'wp-includes/pluggable.php');
require_once(ABSPATH . 'wp-includes/general-template.php');
}
add_shortcode('linkedin', array($this, 'render_shortcode'));
add_action('admin_menu', array($this, 'add_linkedin_admin_menu'));
add_action('wp_footer', array($this, 'enqueue_linkedin_style'));
}
Now that we have access to some basic WordPress functions, we can get our current URI using get_template_directory_uri and our 2 keys using get_option. We then build up our parameter array for the final wp_remote_post. The redirect url here, needs to be the same one we specified before. If it’s not the same, the request will fail. This is what the updated constructor method should look like.
$redirect = get_template_directory_uri() . '/linkedin.php';
$api_key = get_option( 'LINKEDIN_API_KEY' );
$api_secret = get_option( 'LINKEDIN_API_SECRET_KEY' );
$args = array(
'method' => 'POST',
'httpversion' => '1.1',
'blocking' => true,
'body' => array(
'grant_type' => 'authorization_code',
'code' => $_GET['code'],
'redirect_uri' => $redirect,
'client_id' => $api_key,
'client_secret' => $api_secret
)
);
add_filter('https_ssl_verify', '__return_false');
$response = wp_remote_post( 'https://www.linkedin.com/uas/oauth2/accessToken', $args );
Now all we need to do is parse the response body, store our new token and re-direct ourselves back to the admin settings page.
public function __construct() {
if ( $_SERVER["REQUEST_METHOD"] == "GET" && isset($_GET['code'])){
require_once('../../../wp-config.php');
require_once(ABSPATH . 'wp-includes/plugin.php');
require_once(ABSPATH . 'wp-includes/pluggable.php');
require_once(ABSPATH . 'wp-includes/general-template.php');
$redirect = get_template_directory_uri() . '/linkedin.php';
$api_key = get_option( 'LINKEDIN_API_KEY' );
$api_secret = get_option( 'LINKEDIN_API_SECRET_KEY' );
$args = array(
'method' => 'POST',
'httpversion' => '1.1',
'blocking' => true,
'body' => array(
'grant_type' => 'authorization_code',
'code' => $_GET['code'],
'redirect_uri' => $redirect,
'client_id' => $api_key,
'client_secret' => $api_secret
)
);
add_filter('https_ssl_verify', '__return_false');
$response = wp_remote_post( 'https://www.linkedin.com/uas/oauth2/accessToken', $args );
$keys = json_decode($response['body']);
if($keys) {
update_option( 'LINKEDIN_AUTHENTICATION_TOKEN', $keys->{'access_token'} );
}
wp_redirect( get_bloginfo( 'url' ) . '/wp-admin/options-general.php?page=linkedin.php' );
exit;
}
add_shortcode('linkedin', array($this, 'render_shortcode'));
add_action('admin_menu', array($this, 'add_linkedin_admin_menu'));
add_action('wp_footer', array($this, 'enqueue_linkedin_style'));
}
GETTING OUR LINKEDIN PROFILE
Now that we have our authentication token, we can populate our render_shortcode method. The LinkedIn API is very well documented and is pretty easy to use. You simply specify the API URL and append the profile fields you require along with our authentication token
add_filter('https_ssl_verify', '__return_false');
$api_url = "https://api.linkedin.com/v1/people/~:(id,first-name,last-name,headline,industry,summary,positions,picture-url,skills,languages,educations,recommendations-received)?oauth2_access_token=$token&format=json";
$response = wp_remote_get( $api_url );
$json = json_decode( $response['body'] );
Requesting all of this data in one go results in quiet a large JSON result, but it’s very well constructed so it’s easy to parse. Since this shortcode is going to be a full CV, I’m going to break it down into sections. At the top I’ll have some quick links to jump down to each section.
SHORTCUTS
$return .= '<section class="shortcuts">';
$return .= '<h2><a href="#shortcuts">Quick links</a></h2>';
$return .= '<ul>';
$return .= '<li><a href="#skills">Skills</a></li>';
$return .= '<li><a href="#summary">Summary</a></li>';
$return .= '<li><a href="#positions">Positions</a></li>';
$return .= '<li><a href="#recommendations">Recommendations</a></li>';
$return .= '</ul>';
$return .= '</section>';
PROFILE DETAILS
This is where I’ll display my full name and my ‘headline’
$return .= '<section class="about">';
$return .= '<h2>' . $json->{'firstName'} . ' ' . $json->{'lastName'} . '</h2>';
$return .= '<p>' . $json->{'headline'} . '</p>';
$return .= '</section>';
SKILLS
This will be just a CSV formatted list of my skills. The skills object contains the total count of skills and the actual collection of skills in a values object. This is a repeated pattern within this JSON for anything that is a collection. Another thing to note is that the large blocks of text are served as just that, they are not HTML encoded blocks. I have chosen to display them in <pre%gt; tags, but you could replace ‘\n’ characters with <br /%gt; tags if you wanted.
$skills = $json->{'skills'}->{'values'};
$first = true;
$return .= '<section class="skills">';
$return .= '<h2><a href="#" name="skills">Skills</a></h2>';
$return .= '<pre style="font-size: smaller;">';
foreach($skills as $i => $skill) {
$return .= ( $first == false ? ', ' : '') . $skill->{'skill'}->{'name'};
$first = false;
}
$return .= '</pre>';
SUMMARY
The large block of ‘about me’ text
$return .= '<h2><a href="#" name="summary">Summary</a></h2>';
$return .= '<pre>' . $json->{'summary'} . '</pre>';
$return .= '</section>';
JOBS
This will be a list of my job history, once again the actual jobs are under the ‘values’ collection.
$jobs = $json->{'positions'}->{'values'};
$return .= '<section class="positions">';
$return .= '<h2><a href="#" name="positions">Positions - ' . $json->{'industry'} . '</a></h2>';
$return .= '<p>';
foreach($jobs as $i => $job) {
$return .= '<h2>' . $job->{'title'} . '</h2>';
$return .= '<h3>' . $job->{'company'}->{'name'};
$return .= ' ( ' . $job->{'startDate'}->{'year'} . ' - ';
if($job->{'isCurrent'} == "true"){
$return .= 'Current';
} else {
$return .= $job->{'endDate'}->{'year'};
}
$return .= ' )</h3>';
$return .= '<pre>' . $job->{'summary'} . '</pre>';
}
$return .= '</p>';
$return .= '</section>';
RECOMMENDATION
Finally I’ll list my recommendations from various employers or people I’ve worked with. I’ve chosen to display this in the <blockquote> tag.
$return .= '</p>';
$return .= '</section>';
$recommendations = $json->{'recommendationsReceived'}->{'values'};
$return .= '<section class="recommendations">';
$return .= '<h2><a href="#" name="recommendations">Recommendations</a></h2>';
foreach($recommendations as $i => $recommendation) {
$recommendedBy = $recommendation->{'recommender'};
$return .= '<h3>' . $recommendedBy->{'firstName'} . ' ' . $recommendedBy->{'lastName'} . '</h3>';
$return .= '<blockquote>';
$return .= $recommendation->{'recommendationText'};
$return .= '</blockquote>';
}
$return .= '</section>';
FINISHING UP
Now using the [linkedin] shortcode I can display my entire CV with in my site. I could also quite easily add another shortcode or widget to display one or more sections, such as the recommendations. I could then have them appear in the sidebar, or as parts of another post.
As always, the full source code is available below.
Post a Comment