Uncategorized

“Working” from Home

Posted in Uncategorized on March 6th, 2013 by admin – 1 Comment

Telecommuting has been all the buzz this past week, with Yahoo eliminating work from home options for their fulltime telecommuters (but you can still wait for the cable guy!) and Best Buy following their lead soon after.  The articles covering these changes are quite interesting in their slant.

From Mashable, Best Buy’s announcement was spun this way:

Best Buy announced Monday that it has eliminated a flexible work program introduced in 2005 that gave corporate employees the freedom to work when and where they chose. Through the program, known as Results Only Work Environment, employees were assessed based solely on the quality of work they got done rather than whether or not they showed up to the office. Now, these employees will be expected to work a standard 40-hour workweek and come into the office “as much as possible.”

So let me get this straight.  The quality of work you do is less important to Best Buy than where you do it.  Seriously.

And in the New York Times article on Yahoo’s shift:

Parking lots and entire floors of cubicles were nearly empty because some employees were working as little as possible and leaving early.

Then there were the 200 or so people who had work-at-home arrangements. Although they collected Yahoo paychecks, some did little work for the company and a few had even begun their own start-ups on the side.

Does this strike anyone else as odd?  When you have employees who are not getting work done, you fire them.  Managers are supposed to be held responsible for the productivity of their groups.  If your managers can’t manage people except by counting the hours they’re in the office, then they should be fired as well.

I’ve been working for more than 20 years in an industry where I have to focus closely on my work sometimes, brainstorm sometimes, invent sometimes, and always be creative and alert.  Cube farms are soul sucking zombie cages where I just don’t produce nearly as much.  I am far more productive when working on my own schedule from where I want and I far exceed the output and effectiveness of most people I know.  I have a ransom note on top of my LinkedIn profile which somewhat arrogantly announces that I only want to work for people who want me to work for them.

I have fantastic skills, insight, and motivation.  I will do my best work and encourage and assist others to do the same.  I have experience in varied areas which allow me to complement those around me well.  Sitting in a seat in the office is not something I’m particularly good at, nor is it something I want to be measured on.

Best Buy and Yahoo are saying they need to rely on the same metrics used by retail establishments.  If you clock in and out on time and don’t annoy the customers, you’re doing well.  Your actual contributions to the company are not as important as your presence in the office.  How motivating is that? I can clock in, play scrabble, clock out and I should be fine.  Fortunately for my employer, that sounds horrendous to me.  I love contributing, fixing problems, helping people and getting things done.  Fortunately for me, that’s what they keep me around for.

A friend of mine pointed out how ironic it was that Marissa Meyer instituted this policy while spending the money and resources to have a nursery installed in her office at Yahoo.  Perhaps, he suggests, these struggling companies are simply trying to increase attrition to help fend off a future layoff.  If someone’s built their life structure around a particular work arrangement and has to move or find child care, they may just… leave.  And if they quit, you don’t have to fire them.  Win-win, I guess.

Review: Intermediate Perl

Posted in Uncategorized on September 11th, 2012 by admin – Be the first to comment

As a programmer, I’m always looking for ways to take my coding practices to the next level. As most developers have, I’ve attended numerous conferences and sat through countless tutorials, only to find that while the topic is engaging during the talk, it doesn’t “stick” when I leave the room. The biggest exception to this was Randal Schwartz’ “Packages, References, Objects and Modules” – I attended this talk upwards of ten years ago, and it still stands out in my head as the biggest stepping stone to improve my maturity as a programmer.

This book, “Intermediate Perl” is the newest version of that curriculum, and it’s a fantastic way for someone who has become comfortable with perl but who finds themselves stuck copying other people’s code or doing things by rote because they don’t understand all the choices available to them. The book covers Perl’s reference system, which feels like black magic if you don’t understand it; it addresses ways to work with complex data and file handles, and advanced use of regular expressions. There’s also an excellent section on Perl’s OO functionality (such as it is) and testing.

As someone who has worked with perl for almost 20 years, who went through this class several years ago, I still found the book to be incredibly useful. I love Perl Best Practices, but that’s more a primer on doing things in a sensible and efficient way. This book takes the time to walk you through these concepts which are so central to Perl, from simple examples to more complex. Best of all, there are exercises at the back of each chapter which really require thought in applying the concepts just discussed. For me, this is the best way to really internalize new information – give me something to play with, some way to use it, and I’m much more likely to start using it in my own work.  Each exercise has a solution in the back of the book – and these solutions are thoughtfully explained so you understand why it works the way it does.

In short, “Intermediate Perl” is an excellent next step after “Learning Perl.”  I’d go so far as to say that it’s a good workbook for long-time Perl programmers to use – go through the exercises at the back of each chapter and see if there’s some way you can improve your coding, or something you’re doing inefficiently.  In the overwhelming sea of blue Perl books, this one provides a great set of exercises to take your development skills to the next level.

 

 

Asking Questions Well

Posted in Uncategorized on August 5th, 2012 by admin – 3 Comments

One of the most frequent issues developers (and support people) have is that questions aren’t asked completely, and both people end up enmeshed in an iterative clarification cycle, unable to resolve the issue until both people understand what’s going on. There’s a decent FAQ on asking questions on StackOverflow, which does tell you to share details and context, but I’m going to go a little further and give you a template for asking questions which should help you get answers quickly.

What does a good problem statement look like?

I did X.
I expected Y to happen.
To my dismay, Z happened instead.

All three pieces are necessary, because frequently the problem is your expectation, not the system you’re trying to use. To clarify, let’s look at an example.

I jumped off a cliff.
I expected to sprout wings and fly across the ravine.
To my dismay, I plunged to my death instead.

Bummer. But the problem wasn’t really about gravity, it was that you expected something to magically happen to counteract it. Frequently users don’t understand how a system works, or what should be expected – this doesn’t make your question bad, by any stretch of the imagination. Part of supporting a product is making sure your users know what to expect, and the documentation may need updating. But the support person isn’t going to be able to help you if they don’t know why you think it’s broken. It may seem obvious to you what should happen, but trust me – more often than not including this piece will get you to your answer faster.

So, let’s go through this piece by piece…

I did X

When you’re explaining what you did, you need to include all the context you can.

  • What were you doing, exactly?  Can you reproduce the issue consistently?
  • Was it a web issue?  If so, which browser were you using?  Did you try other ones?
  • What exact URL or application were you using?  How can someone exactly recreate what you were doing?
  • If you’re coding, what language are you using?  Are you using a library?  Include an example of the specific code in question, with pointers to the library and examples you’re following (if applicable)
  • If you’re using a web service, what exactly did your request look like?  What were the headers and URL?  If you don’t know how to get those pieces, you need to know – read this article for some pointers.

I expected Y to happen

What did you hope to happen?  Lots of times with an error message or broken request, it’s obvious what you wanted to happen.  It’s still worth your while to include a quick statement.  But if you got a successful response that didn’t match what your expectation was, explain completely and clearly what you were expecting to get, and why.  Point to the documentation supporting your expectation (because it may well need an update or clarification).

To my dismay, Z happened instead

I try to always keep the “To my dismay…” part in there.  It amuses me, it amuses the person answering my question, and it acknowledges the truth of asking a question in this situation – I’m frustrated and sad that I’m stuck because something isn’t working how I want.

But just like with the “I tried X” part, you need to be very specific about what happened.

  • Did you get an error in a console or as a result? What did it say, exactly?
  • What did you try to get around the issue?
  • If you’re using a web service, what were the headers, status code, and/or body returned by the server
  • Note that if you are using a library that wraps a web service but asking for help from the API provider themselves, you likely need to dig down and see what the call itself looks like (as the API provider probably doesn’t support the third party libraries)

Following these guidelines will mean it takes a little longer for you to craft your question, but it will save you time, and most importantly, it will save people who might have the answer time. Posting incomplete questions wastes the time and energy of people who might otherwise help you (and they may not come back to see if you’ve added clarification). Showing them that you value their time and energy by completely explaining the issue will increase your chances of getting a complete answer – and if iteration cycles are needed they’ll be much more valuable.

LinkedIn Today SMS Notification System using LinkedIn, Twilio, Django and Heroku

Posted in Uncategorized on December 22nd, 2011 by admin – 3 Comments


At LinkedIn we enjoy a monthly Hackday, and I usually use the opportunity to figure out new ways to combine our platform with other systems – I love combining different APIs to create new functionality.

This month I decided to use my hackday time to create an SMS notification system for our LinkedIn today system. Since I’m not always on the website, I frequently miss the viral articles being discussed until they’ve passed into my history, and I’d like to be able to jump in on the conversation while it’s hot.  Using the LinkedIn Today API (currently only available internally), the system periodically checks each members feed ‘s see if there are any articles which are currently generating a huge amount of activity .  Once these “hot” articles are found, they are sent via SMS (using Twilio) to the user’s cell phone and recorded in the DB.  The member can reply to the SMS with “save” to add the article to their saved articles at LinkedIn, “share” the article with their LinkedIn network, change the notification level or cancel notifications entirely.
Welcome message Welcome message Welcome message
In order to build this system, I realized that I had to get several different systems to work together, and working through this process I realized that I had to solve several common problems.  This series of posts will be a tutorial on how to do the following:

These are building blocks that can be used for many similar projects, so I include them here to be an inspiration for future developers trying to get something working.  I enjoyed the resulting demo application enough that I ended up creating a web front-end to the system (so people could configure it) – you can get to the application at http://falling-summer-4605.herokuapp.com/

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.

Adding HTML5 Local Storage to Your Web App

Posted in Uncategorized on February 25th, 2011 by admin – 2 Comments

One of the things we’re working on in the Developer Relations group at LinkedIn is creating tools to help our developers work through issues and questions quickly and easily. The OAuth authentication piece is frequently tricky, so we put together an OAuth Test Console, based on a similar tool from JR Conlin.

It’s a handy tool, and certainly makes OAuth debugging much easier, but as a frequent user of the console myself, I found myself quickly getting tired of copying and pasting my consumer key, secret, oauth token, token secret into the fields every time I wanted to use the console. As a developer/user of the console, though, I certainly didn’t want to have that informations saved in a cookie which would go across the wire every time I used the tool, sharing my secret unencrypted keys.

HTML5 local storage to the rescue!  This, my friends, is just the kind of thing you can do with the new HTML5 local (browser) storage functionality.  Store the information in the user’s browser, and check for/use it when you find it there.  The functions are easy to use and implement, so I’ll go over the pieces I used to add this to our console.

Step 1: Don’t tease the IE6 users

All of the major browsers support this functionality in their newest release.  However, not everyone has a fancy, shiny new browser to work with, and telling them you have functionality they can’t have is just mean.  The first thing you should do is make sure that the buttons only show up if they’re going to actually do something.

So, I added a <div> with the store/clear buttons, but set the display to none by default:

<style>
   #storetokens { display: none; }
</style>
<div id="storetokens">
    <button id="store" onclick="storeTokens(); return false;">Store Token Info</button>
    <button id="store" onclick="clearTokens(); return false;">Clear Token Info</button>
</div>

Now that we’ve got that in place, let’s check to see if the browser knows how to store things:

<script>
   if (supportsHTML5Storage()) {$("#storetokens").show();}
</script>

Now we have buttons for storing and clearing the tokens from the browser storage, but only users who can use it will see it.

Step 2:  Store the Tokens

We’re calling a storeTokens() function when the button is clicked – let’s see what that should look like.

Once we ask the user to confirm the action, we just set the values in localStorage based on the values of the fields in the form.  Note that I’m using JQuery here to make it easier to grab the selectors, but this works just as well with raw Javascript.

function storeTokens() {
    var storeConfirm = confirm("Do you want to store your tokens and secrets in your browser's local storage?")
    if (!storeConfirm) { return; }

    localStorage["apiKey"] = $("#apiKey").val();
    localStorage["apiSecret"] = $("#apiSecret").val();
    localStorage["memberToken"] = $("#memberToken").val();
    localStorage["memberSecret"] = $("#memberSecret").val();
}

Step 3:  Clear the Tokens

Anytime you give the user the chance to store some sensitive information, you want to give them the ability to get it back out.  There are various reasons for this, but just trust me – it’s only polite.

So we have a clearTokens() function which is very similar to the storeTokens() function

function clearTokens() {
    var clearConfirm = confirm("Do you want to clear your tokens and secrets from your browser's local storage?")
    if (!clearConfirm) { return; }

   localStorage.removeItem("apiKey")
   localStorage.removeItem("apiSecret")
   localStorage.removeItem("memberToken")
   localStorage.removeItem("memberSecret")
}

Step 4: Retrieve the Stored Data

Great.  Now the user can store and clear the token information.  There’s something missing, though.  Until the form actually takes advantage of the stored data, we’re engaging in a purely academic exercise.  So, when the browser loads, we should populate the fields.

function populateFields() {
    if (localStorage.getItem("apiKey")) {
        $("#apiKey").val(localStorage.getItem("apiKey"))
  	$("#apiSecret").val(localStorage.getItem("apiSecret"));
	$("#memberToken").val(localStorage.getItem("memberToken"));
        $("#memberSecret").val(localStorage.getItem("memberSecret"));
    }
}

That’s It

That’s pretty much all there is to getting HTML5 storage working in modern browsers.  Storing secret information that a user never needs to send across the wire is a great reason to use this functionality.  For users who can use it, we’ve given them a way to save themselves some extra work when they want to use the tool. As you can see, the functionality is really quite easy to implement.  You can see the code in action (and take it to use for your own) - OAuth Test Console.

Watching Craigslist Feeds using Safari or Mac Mail

Posted in Uncategorized on January 11th, 2011 by admin – 2 Comments

Around June 28 of last year, Craigslist made a change to their feed structure which caused applications using Apple’s WebKit to be unable to load them. Safari doesn’t work (neither version 4 or 5), Mac Mail doesn’t work… This problem has been discussed on the Apple Support Forms here and here. While this problem is specific to Apple applications, it seems to only be occurring with a few different feed sources, one of them Craigslist.

There are a couple of ways to get around this problem, each of which uses the same general theory. If the structure of the RSS feed is not possible to read in your RSS reader, then use a different structure. What you need to do is pipe the feed through another feed aggregator, such as Yahoo! Pipes or Google Reader – and then add this feed to your reader.

Google Reader (requires a Google Reader Account)

  • Go to http://reader.google.com
  • Add the Craigslist feed to your Google Reader list
  • Select “Browse for stuff” in the navigation bar
  • Click “Create a bundle”
  • Drag the feed from your list into the bundle, and add it to your shared items
  • Save the bundle and then copy the link from the “Add a Link” link to use in your RSS reader

Yahoo! Pipes (requires a Yahoo! Account)

  • Go to http://pipes.yahoo.com
  • Click “Create a Pipe”
  • Choose “Fetch Feed”
  • Type the Craigslist feed URL in the Fetch Feed box
  • Connect the boxes by dragging from the “dot” at the bottom of the Fetch Feed box to the Pipe Output
  • Click “Save”
  • Add the “Pipe Web Address” for your pipe in your RSS reader (or in Safari)

I’m sure there are countless other ways to achieve the same goal, but these are relatively simple and use tools available to all of us for free. Someday perhaps Apple will fix the issue in WebKit, or the feeds at Craigslist will be adjusted so that they work, but in the meantime you can stay on top of the listings you want to see without having to constantly visit the site.

Another Chance to Get it Right

Posted in Uncategorized on January 1st, 2011 by admin – Be the first to comment

“Cheers to a new year, and another chance to get it right” — Oprah

Happy new year, to all and sundry.  I’ve apparently decided to start off the new year with a head cold, but that’ll pass, and I’ll have all of glorious 2011 in front of me to learn new things, have new experiences, cherish the things I have, and try to scootch ever closer to zen mastery. Last year was very trying, and I don’t think I made any progress on the zen scale – on the other hand, with the list of things that happened to us I’m glad not to have lost much ground.  I won’t bore you by including them all here, but I assure you that on any scale last year was… interesting times.

So, this year!  Resolutions are always fun and exciting.  I generally don’t see the first of the year as any magical day, but if there’s some change I’m planning to make I frequently put it off until the beginning of the new year, since my experience says that making serious habit changes during autumn rarely stick.

So here we are, in winter (brr), with a handy spot on the calendar for introducing changes into our lives.  What do I plan to do with it?

Food. I’m going to stop eating so many inflammatory foods.  My joints are screaming more often than not now, and along with going to the doctor I’m going to stop eating the foods that make my tissues more cranky.  So, no meat, lots less gluten, no soy, way less sugar.  I may add corn and dairy, or I might not.  Also, I’m going to cook most meals at home so we save money.

Exercise. I think perhaps that all of my superwoman punching/kicking/falling martial activities aren’t the best choice I could make.  I am going to focus, for now, on yoga (my goal is 3 or more times a week) and running, and leave it at that.  I have groupons for some bootcampy things, but until my elbows, hips, shoulders and knees are no longer in pain when I stay still, I don’t think I’ll stress them.  I’m going to start a new DirectLife plan on Monday to make sure I’m burning the calories I want to be burning.

Scheduling.  I sure do get into ruts.  And rather than comforting and calming me because of the predictability it makes me a little cranky because I never have really new experiences. So every month I’m going to do something that’s not a usual thing.  This month, the Edwardian Ball.  Next month (ok, I might be cheating a little bit here), Pantheacon.  My friends have awesome parties that I don’t usually go to but I’m going to go to them too.

Money.  We have a lot of stuff.  We really don’t need more.  I have started a new system of adding things I would normally just buy to my amazon wishlist, then reviewing that after a couple of weeks to see if it still seems like a good idea.  I’ve got a bunch of amazon gift money to spend, but it’s not like it’s going to go bad or anything.

Stuff.  Speaking of stuff, I have a lot of stuff I don’t use.  Clothes, knick knacks, books.  This month will be a month of organizing my things, one type at a time (clothes first!) and giving the stuff I don’t use to people who don’t have stuff.

Mindfulness.  Have some.

So that’s my list.  It probably looks a lot like your list, if you have one.  But these are things I try to (and frequently do) change in my life.  I’m just making a fresh run at them since the calendar makes it easy.

Enhancing your JQuery Website with the Facebook Graph

Posted in Uncategorized on November 11th, 2010 by admin – Be the first to comment

Hackday is here again at Netflix, so I’ve been working on a web version of the Intersect which uses JQuery. It occurred to me that adding social to the system would be some nice icing on the cake, so I set forth to use the Facebook Like Button to add things to the users’ graphs, and then the Graph API to pull them back out.

The functionality I wanted to implement was:

  • Show a like button
  • Show thumbnails for each of my friends who like the current movie
  • When the user’s thumbnail is clicked show all the movies liked by that friend in the app
  • When one of those movies is clicked, jump to that movie

Turns out this task was more challenging than I anticipated – partly because, as a hack, I hadn’t thought it out completely, and partly because some of the pieces were more difficult to implement than I’d expected.  You can see the resulting application here, although I will remind you that as a hack it’s not as stable as it might be, and it seems to be particularly sad in Firefox.

This post will cover the process I went through from start to finish.

  1. Preparing the application for Facebook interaction
  2. Adding the like button
  3. Getting users who like the page
  4. Getting all the items for a particular user

Preparing the application for Facebook interaction

If your application uses JQuery or any other Javascript framework, it’s likely that the URL doesn’t change as you move around in the application.  However, if you want the Facebook interaction to address specific entities in your application (in my case, movies) you will need to adjust the application to accept different URLs for different items (likely using query parameters).

For instance, the URL for my website was:

http://www.princesspolymath.com/webIntersect/index.html

But I wanted to be able to let people “like” particular movies, so I added an optional parameter “movie” to jump directly to a particular movie.

http://www.princesspolymath.com/webIntersect/index.html?movie=BVQHL

Once I’d added that piece of interaction, I needed to tackle the Open Graph metatags, so these liked items would correctly show up in the user’s Facebook graph as movies with the right metadata.  I tried inserting them using Javascript, but the Facebook linter does not accept dynamic content, so the metatags needed to be in place as soon as the page was loaded in order to be retrieved by the linter. What I needed to add to the headers was partly static content (app_id, site_name), but partly based on the movie being shown (title, picture, url)

So my JQuery based “html” page switched to a .php file so that it could get pre-processed to show the metadata before the page was loaded.

http://www.princesspolymath.com/webIntersect/index.php?movie=BVQHL

Which was a great idea, except that coming into the page from that link meant I didn’t have the metadata I needed in order to create the open graph metadata tags. The information I needed for the tags had been retrieved from a web service, so getting them back again before page load (without storing them on the server) was a challenge. I decided to store the information I needed *inside* of the URL.

http://www.princesspolymath.com/webIntersect/index.php?movie=BVQHL&name=Howl%27s+Moving+Castleℑ=http%3A%2F%2Fcdn-3.nflximg.com%2Fus%2Fboxshots%2Flarge%2F70028883.jpg

My app ignored the extra parameters, but the PHP parser was able to get what it needed to make the tags:
function curPageURL() {
$pageURL = 'http';
if ($_SERVER["HTTPS"] == "on") {
$pageURL .= "s";
}
$pageURL .= "://";
if ($_SERVER["SERVER_PORT"] != "80") {
$pageURL .= $_SERVER["SERVER_NAME"].":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"];
}
else {
$pageURL .= $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
}
return $pageURL;
}
function getName() {
return ($_GET["name"]);
}
function getImage() {
return ("http://api.freebase.com/api/trans/image_thumb" . $_GET["fbId"] );
}
?>

Adding the Like Button

Once I had the right URL, which had the right parameters to feed the Open Graph metatags back to the linter for proper graph inclusion, and the movie information so I knew what movie it refered to, I set to adding the Like Button.

My first thought was to use the dynamic one created by the Javascript SDK (since I needed it for other functionality) – giving my users the opportunity to comment and giving me the ability to listen for clicks… but Safari had some security complaints about the way it was fiddling with content so I decided to fall back to the iframe version.

I set it up using the URL I built above and checked to make sure that it was adding the correct movies to the graph (by directly querying graph.api.com to see whether there were new movies being added to my likes).

Note that in this case, since I was dynamically generating the content for the dialog, I needed to tell the FB SDK to parse the page again once the elements were finished rendering:

window.setTimeout(function() {																					   
    FB.XFBML.parse();
}, 500)

Getting Friends who Like the Page

Unfortunately, there’s no direct graph query for “Give me all of my friends who like this page” – I couldn’t even get a list of the users who like the page similar to how the like button with pictures did it.  However, using the Facebook Javascript SDK it was relatively easy to get all of my friends and their movies. Note that it would have been more accurate to search for the specific ID for this movie, but I didn’t have that available – all I had was the URL I’d built and the name of the movie, so I used the name.

FB.api("/me/friends?fields=movies,name,picture", function(friends) {
for (var friend in friends.data) {
	currentFriend = friends.data[friend]

	if (!currentFriend["movies"]) {
		continue
	}
	for (var friendmovie in currentFriend.movies.data) {
		thisFriendMovie = currentFriend.movies.data[friendmovie]
		if (thisMovieTitle == thisFriendMovie.name) {
			thisfriend = { "id":currentFriend.id,
			            		"name":currentFriend.name,
						"picture":currentFriend.picture
			}
			liking_friends.push(thisfriend)												
		}
	}
}

Getting Movies for a Particular Friend

I tagged the images of the user’s friends with their Freebase ID, so when the thumbnail was clicked, I grabbed that user’s movies from the graph:


FB.api("/" + id + "/movies?fields=website,picture", function(movies) {
	faves = ""
	for (var movieIndex in movies.data) {
		currentMovie = movies.data[movieIndex]
		if (!currentMovie.website || !
                          currentMovie.website.match(/princesspolymath.com/)) {
				continue;
		}
		params = currentMovie.website.split('=');
		tinyurl = params[1].split('&')[0]
		image = unescape(params[3])

		faves += ' '
	}
	$('#faves').html(faves)
}) 


I used the “website” information from the graph to get the id for the movie (the “movie” parameter) and the correct image to show in the application. When the user clicks on one of the movies for the user, the id is used to bring up a dialog with that movie, allowing people to navigate within the application using the graph information retrieved from Facebook.

Feel free to play around with the application and “like” some movies to see how it works.