Posts Tagged ‘javascript’

LinkedIn’s Faceted Search

Posted in Uncategorized on June 29th, 2011 by admin – Be the first to comment

On June 7, I gave a presentation to the Semantic Web Meetup in San Francisco on using LinkedIn’s Faceted Search.  The presentation talked about using the Faceted Search capabilities of LinkedIn’s Search API to create a semantic browsing experience within an application.  This topic is explored in much more depth in a previous post on this blog, which has all the code and logic needed to make it work for you.

More information on the LinkedIn APIs can be found on the LinkedIn Developer Portal.

Let me know if you have questions or thoughts, and I’ll make sure you get what you need!

Browsing your LinkedIn Connections by Industry

Posted in JSAPI, LinkedIn, Uncategorized on March 9th, 2011 by admin – 3 Comments

I presented at the Semantic Web Meetup in San Francisco about this topic, slides for that talk can be found here.

A friend of mine, who runs a rather cool website, asked me to help him understand how to create an industry-based query to see which of your connections are in a particular industry.  As this was a slightly trickier-than-usual problem, I put together an example for him to use, and I’ll go over how it works here.

But first, the obligatory working demo:

And the code. Note there are a couple of support files you’ll need to snag as well.

In order to keep this post manageable, I’ll mention that most of the pieces for this are included in my StreamIn’ tutorial. So if you’re interested in understanding how the basic framework works, take a look there. We’re basically going to break out on “Add Share Stream”.

Industries

One of the reasons that this example is somewhat tricky is that the API doesn’t currently have a way to give you a list of industries, and how they’re related, but we do have a web document (which is fairly static) with that information. So I grabbed the HTML from that page, fiddled with it in my editor, and created a JSON document with a hash of industries (and the groups they map to) and groups (and the industries they contain). You can see that file here. Loading that file as a javascript script gives the page access to hashes of this information.

Autocomplete

Hey, since we’ve made this handy hash, it’s a cinch to provide a jQuery autocomplete for the industries.

	var data = Object.keys(industry_names);
	$("input#Industry").autocomplete({
	   source: data,
	   select: function() { getConnections() }
	});

Titles is not really a bounded set of this type, so I’ll just let the user input that freestyle.

Getting the connections

The logic I wanted to use was… find all my 1st or 2nd degree connections in the industry who match the title. If there are fewer than 25 of those, do another search for all of the industries in the industry group. You can see how that stacks in the code, so I’ll just talk through the actual call for the first search.

	$("#connections").html("");
	var industrynum = industry_names[$("#Industry").val()]["code"]
	var title = $("#Title").val();
	path = "/people-search:(people:(id,first-name,last-name,public-profile-url,
                  three-current-positions:(title,company:(name,industry)),distance,picture-url))?
                  sort=distance&facet=industry," + industrynum + "&title=" + title + "&current-
                  title=true&facet=network,F,S&count=25"

So, clear the div we’re gong to use. Grab the code from the industry hash based on what’s in the Industry user input field. Pull the title from the Title input field.

In this case we’re using the People Search resource with some field selectors to request specific profile fields for the users who are returned. That’s this part:

people-search:(people:(
                     id,first-name,last-name,public-profile-url,
                     three-current-positions:(title,company:(name,industry)),
                     distance,picture-url))

Why three-current-positions? Because I can’t just ask for one current position, and this list will be smaller and faster than all the positions.

We also need some very specific filters on our search, so we’re using facets (also covered on the People Search page) to get us only the members in our network who have the right title and industry. Let’s go over those things individually:

  • sort=distance : distance from me, i.e. degree (first or second)
  • facet=industry,xx : I want only users who are working in this industry (like Entertainment or Internet)
  • title=yyy : This is a fuzzy string match. Doesn’t have to match exactly.
  • current_title=true : Don’t tell me about their past positions. I want people doing this right now
  • facet=network,F,S : First and Second degree connections only.
  • count=25 : I don’t have all day and I’m only going to show 25 total results in any case.

All right, now that that’s settled, let’s make the call. The Raw API call we’re making reaches directly through to the back end REST API.

	IN.API.Raw(path)
	  .result(function(result) {
	    var count = result.people.values.length;
	    var html = $("#connections").html();
	  	for (var index in result.people.values) {
	  		var person = result.people.values[index]
	  		html += "<div class=\"connectionitem\">";
	  		if (person["pictureUrl"]) {
		  		html += "<img src= \"" + person["pictureUrl"] +
                                              "\"  width=40 align=right/>";
			}
			var distance = (person["distance"] === 1 ? '1st' : '2nd')
	  		html += "<a href=\"" + person["publicProfileUrl"] + "\"><h3>" +
                                      person["firstName"] + " " + person["lastName"] +
                                      " (" + distance + ")</h3></a>";
	  		html += person["threeCurrentPositions"]["values"][0]["title"] +
                                    ", " + person["threeCurrentPositions"]["values"][0]["company"]["name"] +
                                   " (" + person["threeCurrentPositions"]["values"][0]["company"]["industry"] +
                                   ") </div>";
	  	}
	  	$("#connections").html(html)
	  });

Just as in the other tutorials, the JSAPI method (in this case IN.API.Raw()) is called with chained methods indicating how it should be executed. In this case, we just want to perform a GET on the resource, and process the results, so .results is the only thing we’re setting.

Just like in the StreamIn’ tutorial, the results are iterated over person by person and a collection of connectionitems is gathered together, made pretty, and plopped in the “connections” div.

Creating an Application using LinkedIn Platform in 3 Easy Steps!

Posted in API, JSAPI, LinkedIn on February 17th, 2011 by admin – 2 Comments

The LinkedIn API has all sorts of juicy goodness to it, but the authentication and structure take some learning. LinkedIn has created a JS API called Connect to help you get around those things, so that you can focus on the logic and presentation of your application without spending too much time considering the back end.

I made an internal presentation covering this material at LinkedIn on 2/18, which is available in PDF format. This tutorial has also been integrated into the general JSAPI tutorials on the developer portal. That version has a lot less narration and is just about getting the code written and working.

Everyone loves activity streams, so to illustrate the application development process I’m going to walk through the steps needed to create an interactive LinkedIn application. StreamIn’ has a profile badge for the logged in member, a stream of the status updates from members in their network, and the ability to like/unlike particular items.

A note on this tutorial – there is detailed documentation for the JSAPI on the LinkedIn Developer Portal – there are links in the text appropriate, but detailed options aren’t covered here to keep the tutorial reasonably sized. It’s a lot more fun to get started in a framework if you’ve built something from start to finish, and that’s what we’ll do here.

Getting Started

To get started on our exercise, we need a shell for the application.  Here we have a basic layout, with places for the badge and stream to be inserted.

<div id="header"><h1>StreamIN'</h1></div>
  <div id="wrapper">
    <div id="profile">
	    <div id="badge">
	    Profile info goes here!
	    </div>
    </div>
    <div id="stream">
        Stream stuff goes here!
    </div>
    <br clear="both">
    <div id="footer">
    <div id="nav"> [ << ] <a href="Step1.html">[ >> ]</a></div>
</div>

We’ll start with this and build the application in stages.

Step 0: Check out the Console

The Connect framework does all of the authentication work for you, and loads the things you need in order to get things working in your application.  There are various ways to present login options for the member, and you can explore them using the new JSAPI Console.  At the very least you’ll want to explore the Login options (Login Button, Login Button with Events, Login Button Label). The console is an incredibly useful tool!  Take a few moments now to look around and see what it has to offer.  You can even edit the Code section and click “Run” to see how the Results change.

Step 1: Get the Profile

In order to show the user’s profile, we’ll need to do a few things.

  • Import the framework
  • Add a login button
  • Add an API call to get the profile data and display it

I know, that seems like a lot of sub-steps, but it’s really very easy.

Import the Framework

The first thing we need to do when using Connect is get the framework in our page.  In the <head> of the document, we’ll add a small script to grab the framework.  To do this you need to get a LinkedIn API key – if you don’t have one, go ahead and get one and then come back – it doesn’t take long and it’s much more fun to play along than just read about what I did.

Here’s your code. Use your own api_key. “authorize:true” tells the framework to authorize the member if they’ve visited your application before, rather than logging in each time.

<script type="text/javascript" src="http://platform.linkedin.com/in.js">
   api_key: API_KEY
   authorize: true
</script>

Add a Login Button

Let’s add the button at the bottom of the page.  Here’s the code.  No really, this is all there is!  The data-onAuth variable tells the framework to fire the loadData function when the member has been authorized.

<script type="IN/Login" data-onAuth="loadData"></script>

Load the User’s Profile

Here’s the meat of the step – now the framework is in place, so it’s possible to make calls on behalf of the member. When the member has been authorized the loadData() function will be called. This function calls the Profile method. We need a couple of extra fields from that call, so we use .fields to tell the call what to request.  And the .result sets a callback to perform as soon as the call returns.  You can put an anonymous function there (as I have here) or give it the name of another function to process your returned data.

The callback in this case just pulls the user’s information from the result and builds a profile badge, inserting it in the badge <div>.

function loadData() {
// we pass field selectors as a single parameter (array of strings)
IN.API.Profile("me")
   .fields(["id", "firstName", "lastName", "pictureUrl","headline"])
   .result(function(result) {
      profile = result.values[0];
      profHTML = "<p><a href=\"" + profile.publicProfileUrl + "\">";
      profHTML +=  "<img align=\"left\" src=\"" + profile.pictureUrl + "\"></a>";
      profHTML +=  "<a href=\"" + profile.publicProfileUrl + "\">";
      profHTML +=  "<h2>" + profile.firstName + " " + profile.lastName + "</a> </h2>";
      profHTML += "<span>" + profile.headline + "</span>";
$("#badge").html(profHTML);
});
}

This is how that renders – and you can look at the code for this step.

Step 2: Add Share Stream

The member is authorized and we can see their information. The next thing the application needs to do is grab all of the status updates in their network stream and insert the information into the application.

This can be done using the NetworkUpdates function. To restrict the type of update to “STAT” (Status) updates, .params is used. For simplicity we’re using another inline function, which iterates over the updates and builds individual stream items for each one, then injects the html into the stream <div>.

There also needs to be a call to getUpdateStream() in the loadData() function so it gets called after the profile is loaded.

function getUpdateStream() {  
	IN.API.NetworkUpdates()
	.params({type:"STAT"})
	.result(function(result) {
	    var streamHTML = "";
		for (var update in result.values) {
			var thisupdate = result.values[update]
		
			// Build each individual stream update item
			person = thisupdate.updateContent.person
			var thisHTML = "<div class=streamitem>";
			
			// Person's picture,  linked name, and status
			thisHTML += "<img align=\"left\" class=img_border height=\"50\" src=\"" + person.pictureUrl + "\"></a>"; 
			thisHTML += "<a href=\"" + person.publicProfileUrl + "\">";
			thisHTML += "<span class=updater>" + person.firstName + " " + person.lastName + "</span></a>";			
			thisHTML += "<p class=update>" + activateLinks(person.currentStatus) + "</p>"
			thisHTML += "</div>";
			streamHTML += thisHTML
		}
		$("#stream").html(streamHTML);
	});
}

And now we have this. Code is here.

Step 3: Adding Interaction

Reading from the API is all well and good, but it’s pretty hard to make a compelling application if the member can’t do anything with it. Status updates can always be “liked”, so let’s add some like/unlike action to StreamIn’.

This part is a little more complicated. getUpdateStream() needs to be extended to add Like/Unlike buttons, and those buttons have to perform the appropriate action when pressed.

The Connect calls for these buttons are a little trickier. Since there is no Like/Unlike convenience method in Connect, we’ll pass through a raw call to the backend API. For this call, the method needs to be “PUT”, and the body is simply “true” or “false”. There’s an “alert” here to tell the user something happened, and then the stream is reloaded and redisplayed.

Here’s getUpdateStream again, with the newly added code in bold. The first section simply adds a button to the stream item – if the user has not yet liked the item, a Like button is shown. Otherwise, there’s an Unlike button.
The other two functions are event handlers for clicks on these buttons.


function getUpdateStream() {  
	IN.API.NetworkUpdates()
	.params({type:"SHAR"})
	.result(function(result) {
	    var streamHTML = "";
		for (var update in result.values) {
			var thisupdate = result.values[update]
		
			// Build each individual stream update item
			person = thisupdate.updateContent.person
			var thisHTML = "<div class=streamitem>";
			
			// Person's picture,  linked name, and status
			thisHTML += "<div class=updateperson>" ;
			thisHTML += "<img class=img_border align=\"left\" height=\"50\" src=\"" + person.pictureUrl + "\"></a>"; 
			thisHTML += "<a href=\"" + person.publicProfileUrl + "\">";
			thisHTML += "<span class=updater>" + person.firstName + " " + person.lastName + "</a></span>";	
			thisHTML += "<p class=update>" + activateLinks(person.currentShare.comment) + "</p></div>";
						
			// Present a like button
			if (! thisupdate.isLiked) {
				thisHTML += "<div id=button><button class=\"likebutton ui-corner-all\" id=\"" + 
                                thisupdate.updateKey + "\"><img src=\"Thumbs_up.png\"> Like</button></div>"
			} else {
				thisHTML += "<div id=button><button class=\"unlikebutton ui-corner-all\" id=\"" +
                                thisupdate.updateKey + "\"><img src=\"Thumbs_down.png\"> Unlike</button></div>"
			}	
			thisHTML += "</div>";
			
			// Slap this onto the HTML we're building
			streamHTML += thisHTML;
		}
		$("#stream").html(streamHTML);
	});
	
	$( ".likebutton" ).live("click", function() {
		   likeURL = "/people/~/network/updates/key=" + $(this).attr("id") + "/is-liked"
		   IN.API.Raw(likeURL)
			.method("PUT")
			.body("true")
			.result(function(result) {
				alert ("Liked");
				getUpdateStream();
			})
	});	
	
	$( ".unlikebutton" ).live("click", function() {
		   likeURL = "/people/~/network/updates/key=" + $(this).attr("id") + "/is-liked"
		   IN.API.Raw(likeURL)
			.method("PUT")
			.body("false")
			.result(function(result) {
				alert ("Unliked");
				getUpdateStream();
			})
	});	
}

So that’s it. The code for Streamin’ code is here. Now that you’ve gotten an app up and running, check out the docs and see what you can build!