My favorites | English | Sign in

Faster apps faster - GWT 2.0 with Speed Tracer New!

Google Friend Connect APIs (Labs)

Visitor Interests

Friend Connect allows you to ask community members questions about their interests. Their answers will appear on their profile just for your site. For example, if you run a music site, you may want to ask members to choose their favorite genre of music. Information collected helps visitors learn more about each other, and can be used freely by you to provide interest based newsletters, content, ads, and more.

Google Friend Connect also provides APIs to use this community data as hints for ad targeting, content filtering and searching, etc. This may allow you to monetize your site better. An AdSense implementation is included to enable AdSense to use both the content of the site and profile information from your users to serve more relevant ads.

The social profile of a user is built based upon their response to profile questions either at the time they first join the site or via a poll gadget. The Google Friend Connect APIs can be used to retrieve the user's responses. The aggregate information from all GFC users on the site can also be retrieved and used to target ads or other content for users who are not signed in with Google Friend Connect. The interests example should demonstrate how this works.

Contents

  1. Getting Started
  2. Accessing Interests Data
    1. Using JavaScript
    2. Using REST or PRC
  3. Adding Interests Data Programmatically
  4. Retrieving Hints
  5. Using Hints with AdSense

Getting Started

There are several important steps to get started using visitor interests and it all begins on the GFC admin site at www.google.com/friendconnect.

  1. Configure your poll questions by choosing the "Interests" option in the Friend Connect admin site.
  2. For each question, you can choose between displaying the question to users at the time they join the site, via a poll gadget embedded in your site or via your own code.
  3. If you choose to use the poll gadget, set the colors and styles and copy/paste the generated code into your site.
  4. To monetize your site with AdSense, visit the "AdSense" option in the admin site, create an AdSense account on that page (if you don't already have one), and copy/paste the generated code. It's that simple.

Many sites will only need to follow the steps above to poll their users, populate user profiles and boost their monetization potential. However, developers using the APIs can build additional solutions for analysis and use of the interests data.

Here are some potential integrations:

  • Allow a site admin to see what hints are being passed for AdSense monetization.
  • Pass hints to third-party ad systems, search engines, or content management systems.
  • Add additional profile information acquired through custom code to a user's profile (with their confirmation, of course)

Accessing Interests Data

Interests data is stored as part of the OpenSocial Person object and accessable using either the JavaScript code or the REST/RPC protocols.

Accessing Interests Data for Individual Users

The interests data for an individual user is available in the OpenSocial Person object. To get the data representing the current VIEWER of the site, see the JavaScript example below. The data is available under the profileExtensions field. Since the field is not returned by default, it must be explicitly requested when making a request for the data via JavaScript, REST or RPC.

Here's an example user profile with interests data:

   "entry":{
      "profileUrl":"http://www.example.com/canvas.html?site=11402851587185854919&profileId=05095913696217258577",
      "aboutMe":"Geek and Developer Advocate on OpenSocial at Google. I also tweet lots about my new dog- digit, a cuddly goldendoodle.",
      "id":"05095913696217258577",
      "profileExtensions":{
         "1":{
            "id":"1",
            "title":"What is your favorite amplifier?",
            "entryType":"RADIO",
            "choice":[
               {
                  "id":"0",
                  "label":"Peavey Amp"
               },
               {
                  "id":"1",
                  "label":"Roland Amp"
               },
               {
                  "id":"2",
                  "label":"Marshall Amp"
               },
               {
                  "id":"3",
                  "label":"Fender Amp"
               }
            ],
            "value":{
               "choice":[
                  {
                     "id":"0",
                     "label":"Peavey Amp"
                  }
               ]
            },
            "ordinal":1,
            "helpText":"Amp",
            "required":false
         },
         "0":{
            "id":"0",
            "title":"What is your favorite guitar?",
            "entryType":"RADIO",
            "choice":[
               {
                  "id":"0",
                  "label":"Gibson"
               },
               {
                  "id":"1",
                  "label":"Ibanez"
               },
               {
                  "id":"2",
                  "label":"Ovation"
               },
               {
                  "id":"3",
                  "label":"Fender"
               }
            ],
            "value":{
               "choice":[
                  {
                     "id":"0",
                     "label":"Gibson"
                  }
               ]
            },
            "ordinal":0,
            "helpText":"Guitar",
            "required":false
         },
         "1111":{
            "id":"1111",
            "title":"Top two guitarsts",
            "entryType":"CHECKBOX",
            "choice":[
               {
                  "id":"0",
                  "label":"Beck"
               },
               {
                  "id":"1",
                  "label":"Hendrix"
               },
               {
                  "id":"2",
                  "label":"Doc Watson"
               },
               {
                  "id":"3",
                  "label":"Clapton"
               }
            ],
            "value":{
               "choice":[
                  {
                     "id":"0",
                     "label":"Beck"
                  },
                  {
                     "id":"2",
                     "label":"Doc Watson"
                  }
               ]
            },
            "ordinal":4,
            "helpText":"Top guitarist",
            "required":false
         }
      },
      "thumbnailUrl":"http://www.google.com/friendconnect/profile/picture/RkBSp-UUgqQiUMytFJS6M-REB9LTuVao0iHlD00D6iFumUUt9A6H6EqesafABZ6Zrsx7q5L0pXlIew8nlJrmx2o0OLmKKacsvLaWKkHfYhEDsBFOjbCVjIbTOgMFm_hrdcGG9ij3ltXTML4UdaS4ZK0Kgn4k4I2L8NOyXZPXM8EUxBHIaKreaG98nqFdoQEW",
      "urls":[
         {
            "linkText":"ryan boyd@TWITTER",
            "value":"http://twitter.com/ryguyrg",
            "type":"externalProfile"
         },
         {
            "linkText":"",
            "value":"http://www.google.com/profiles/113895932679661040760",
            "type":"OpenId"
         },
         {
            "value":"http://www.example.com/canvas.html?site=11402851587185854919&profileId=05095913696217258577",
            "type":"profile"
         }
      ],
      "photos":[
         {
            "value":"http://www.google.com/friendconnect/profile/picture/RkBSp-UUgqQiUMytFJS6M-REB9LTuVao0iHlD00D6iFumUUt9A6H6EqesafABZ6Zrsx7q5L0pXlIew8nlJrmx2o0OLmKKacsvLaWKkHfYhEDsBFOjbCVjIbTOgMFm_hrdcGG9ij3ltXTML4UdaS4ZK0Kgn4k4I2L8NOyXZPXM8EUxBHIaKreaG98nqFdoQEW",
            "type":"thumbnail"
         }
      ],
      "displayName":"ryan boyd"
   }
}

Accessing Aggregate Interests Data

The aggregate interests data is available in the OpenSocial Person object representing the OWNER of the site, under the profileExtensions field. Since profileExtensions is not a default field, you must explicitly request it. To get the aggregate data, you must also explicitly request the profileAggregates field.

Here's an example:

{
   "entry":{
      "profileUrl":"http://www.example.com/",
      "id":"11402851587185854919",
      "profileExtensions":{
         "1":{
            "id":"1",
            "aggregate":{
               "response":[
                  {
                     "count":7,
                     "choice":{
                        "id":"3",
                        "label":"Fender Amp"
                     }
                  },
                  {
                     "count":6,
                     "choice":{
                        "id":"1",
                        "label":"Roland Amp"
                     }
                  },
                  {
                     "count":5,
                     "choice":{
                        "id":"2",
                        "label":"Marshall Amp"
                     }
                  },
                  {
                     "count":4,
                     "choice":{
                        "id":"0",
                        "label":"Peavey Amp"
                     }
                  }
               ]
            },
            "title":"What is your favorite amplifier?",
            "entryType":"RADIO",
            "choice":[
               {
                  "id":"0",
                  "label":"Peavey Amp"
               },
               {
                  "id":"1",
                  "label":"Roland Amp"
               },
               {
                  "id":"2",
                  "label":"Marshall Amp"
               },
               {
                  "id":"3",
                  "label":"Fender Amp"
               }
            ],
            "ordinal":1,
            "helpText":"Amp",
            "required":false
         },
         "0":{
            "id":"0",
            "aggregate":{
               "response":[
                  {
                     "count":10,
                     "choice":{
                        "id":"0",
                        "label":"Gibson"
                     }
                  },
                  {
                     "count":7,
                     "choice":{
                        "id":"2",
                        "label":"Ovation"
                     }
                  },
                  {
                     "count":5,
                     "choice":{
                        "id":"3",
                        "label":"Fender"
                     }
                  },
                  {
                     "count":3,
                     "choice":{
                        "id":"1",
                        "label":"Ibanez"
                     }
                  }
               ]
            },
            "title":"What is your favorite guitar?",
            "entryType":"RADIO",
            "choice":[
               {
                  "id":"0",
                  "label":"Gibson"
               },
               {
                  "id":"1",
                  "label":"Ibanez"
               },
               {
                  "id":"2",
                  "label":"Ovation"
               },
               {
                  "id":"3",
                  "label":"Fender"
               }
            ],
            "ordinal":0,
            "helpText":"Guitar",
            "required":false
         },
         "111":{
            "id":"111",
            "aggregate":{
               "response":[
                  {
                     "count":4,
                     "choice":{
                        "id":"0",
                        "label":"Bonnaroo"
                     }
                  },
                  {
                     "count":3,
                     "choice":{
                        "id":"1",
                        "label":"Grey Fox"
                     }
                  },
                  {
                     "count":2,
                     "choice":{
                        "id":"2",
                        "label":"Telluride"
                     }
                  }
               ]
            },
            "title":"Favorite Festival",
            "entryType":"RADIO",
            "choice":[
               {
                  "id":"0",
                  "label":"Bonnaroo"
               },
               {
                  "id":"1",
                  "label":"Grey Fox"
               },
               {
                  "id":"2",
                  "label":"Telluride"
               }
            ],
            "ordinal":3,
            "helpText":"Festival",
            "required":false
         },
         "1111":{
            "id":"1111",
            "aggregate":{
               "response":[
                  {
                     "count":6,
                     "choice":{
                        "id":"1",
                        "label":"Hendrix"
                     }
                  },
                  {
                     "count":5,
                     "choice":{
                        "id":"0",
                        "label":"Beck"
                     }
                  },
                  {
                     "count":5,
                     "choice":{
                        "id":"2",
                        "label":"Doc Watson"
                     }
                  },
                  {
                     "count":4,
                     "choice":{
                        "id":"3",
                        "label":"Clapton"
                     }
                  }
               ]
            },
            "title":"Top two guitarsts",
            "entryType":"CHECKBOX",
            "choice":[
               {
                  "id":"0",
                  "label":"Beck"
               },
               {
                  "id":"1",
                  "label":"Hendrix"
               },
               {
                  "id":"2",
                  "label":"Doc Watson"
               },
               {
                  "id":"3",
                  "label":"Clapton"
               }
            ],
            "ordinal":4,
            "helpText":"Top guitarist",
            "required":false
         },
         "11":{
            "id":"11",
            "aggregate":{
               "response":[
                  {
                     "count":6,
                     "choice":{
                        "id":"0",
                        "label":"Electric guitar"
                     }
                  },
                  {
                     "count":5,
                     "choice":{
                        "id":"2",
                        "label":"Banjo"
                     }
                  },
                  {
                     "count":4,
                     "choice":{
                        "id":"1",
                        "label":"Acoustic guitar"
                     }
                  },
                  {
                     "count":4,
                     "choice":{
                        "id":"3",
                        "label":"Mandolin"
                     }
                  }
               ]
            },
            "title":"What 's your main stringed instrument?",
            "entryType":"RADIO",
            "choice":[
               {
                  "id":"0",
                  "label":"Electric guitar"
               },
               {
                  "id":"1",
                  "label":"Acoustic guitar"
               },
               {
                  "id":"2",
                  "label":"Banjo"
               },
               {
                  "id":"3",
                  "label":"Mandolin"
               }
            ],
            "ordinal":2,
            "helpText":"instrument type",
            "required":false
         }
      },
      "name":{
         "formatted":"example",
         "honorificPrefix":"",
         "additionalName":"",
         "familyName":"",
         "givenName":"example",
         "honorificSuffix":""
      },
      "urls":[
         {
            "value":"http://www.example.com/",
            "type":"profile"
         }
      ],
      "displayName":"example"
   }
}

JavaScript Example

The following example shows retrieving the interests data for the VIEWER and popping up alert dialogs showing each question which the user answered followed by their corresponding answer. In order to run this code, you must first setup your site to use the inline Friend Connect library.

var params = {};
params[opensocial.DataRequest.PeopleRequestFields.PROFILE_DETAILS] =
  ['profileExtensions'];
 
var req = opensocial.newDataRequest();
req.add(req.newFetchPersonRequest(opensocial.IdSpec.PersonId.VIEWER, params), 'viewer');
req.send(fetchPersonHandler);

function fetchPersonHandler(resp) {
  // Get the viewer data from the response
  var viewerResp = resp.get('viewer'); 
  if (!viewerResp.hadError()) {
    var viewer = viewerResp.getData();
 
    // Use the opensocial.Person class' getField() method to access specially requested fields
    var extensions = viewer.getField("profileExtensions");
 
    // Loop through each object in the extensions, ignoring properties from the prototype
    for (var key in extensions) {
      if (extensions.hasOwnProperty(key)) {
        // Alert with the title of the profile question
        alert('Question: ' + extensions[key].title);
 
        if (extensions[key].entryType == 'SHORT_TEXT') {
          alert('Answer: ' + extensions[key].value.text);
        } else if (extensions[key].entryType == 'RADIO') {
          alert('Answer: ' + extensions[key].value.choice[0].label);
        } else if (extensions[key].entryType == 'CHECKBOX') {
          // multiple answers could exist, but show only first
          alert('Answer: ' + extensions[key].value.choice[0].label);
        }
      }
    }
  }
};

REST and RPC Examples

The REST and RPC protocols can be used with the HTTP libraries and language of your choice, though there are OpenSocial Client Libraries to make interaction with the services easier.

To retrieve the interests data for all members of a particular site via the REST protocol, you only need the site ID. After you have the ID, make a HTTP request like the following, replacing the bolded text with your site ID found in the URL of the Friend Connect admin site.

http://www.google.com/friendconnect/api/people/(site id)/@friends?id=(site id)&fields=profileExtensions

The RPC protocol is a bit different. Here's what the curl request looks like for doing a HTTP POST to make a RPC request for the same data:

curl -d '{"method" : "people.get", "id" : "siteinfo", "params" : {"userId" : "(site id)", "groupId" : "@friends", fields : "profileExtensions"}}' -H 'Content-Type: application/json' http://www.google.com/friendconnect/api/rpc?id=(site id)

Note that both of the requests above are specifically sending the fields param in order to ensure that the profileExtensions data is returned. This is because it is not returned by default.

Adding Interests Data Programmatically

Although Google Friend Connect provides profile data collection at the time a user logs into a site and via the poll gadget, some sites may wish to build information about visitor interests in other ways.

Examples:

  • Play a series of videos and ask users to choose their favorite.
  • Show several pictures of animals and ask users to choose the cutest.
  • Present polls inside of a Flash app, but save the data to a user's profile using JavaScript

In order to add information to a user's profile, you need to define the questions in advance via the Google Friend Connect admin site. After the questions are defined, you just need to make a simple JavaScript call to store profile information. This will create a popup div (modal dialog) which asks the user to confirm that they want to add the given information to their public profile.

Here's an example call which stores a user's favorite color choice:

var choiceId;
switch (color) {
  case 'red':
    choiceId = '0';
    break;
  case 'blue':
    choiceId = '1';
    break;
  case 'green':
    choiceId = '2';
    break;
}

var preferenceText = 'I choose which colors I like based on those that make me happy and glow inside.';

var exts = {"2703852437578438542":{"choiceId":[choiceId]},
            "1403375950924197018":{"text":preferenceText}};
google.friendconnect.updateProfileExtensions(exts);

The two long numbers above (27038...) represent the Question IDs and can be found below the list of questions in the "Interests" section of the admin site, if you click on the 'Show question IDs' link.

Responses for multiple choice, checkboxes and choose from a list questions are set using choiceId which takes an array of the 0-based choice index values (first choice is 0, second is 1, etc). It always takes an array, even if there's only one response as indicated in the example above.

Reponses for text and paragraph text questions are set using "text" instead of "choiceId", with the value passed as a string rather than an array.

Multiple questions can be saved to the visitor's profile at once by passing multiple Question IDs and values, so you can avoid overly prompting the user.

Retrieving Hints

Hints are calculated based on the current user's response to poll questions, or the aggregate response if no user is logged in. These hints can be passed onto content management systems, ad networks or other systems.

Hints are retrieved using the standard OpenSocial DataRequest class. Each request to retrieve a hint results in a random single response to one of the visitor interests questions. Here's an example:

var req = opensocial.newDataRequest();
req.add(google.friendconnect.newFetchInterestHintRequest(), 'hint');
req.send(fetchHints);

function fetchHints(resp) {
 var hintResp = resp.get('hint');

 if (!hintResp.hadError()) {
   var hintPacket = hintResp.getData();
   var google_ad_client = hintPacket['publisherId'];
   var gfc_hints = hintPacket['hint'];
   var google_signature = hintPacket['signature'];
   var isPersonal = !!hintPacket['personal'];
   alert("hints: " + gfc_hints);
  }
}

The Featured Content gadget available under "Social gadgets" in the Google Friend Connect admin panel provides content based on the visitor's interests. Here's a complete code example to show you the basics of a similar implementation. You could use the same technique to search your own database, product catalog, knowledge base, etc. based on interests data.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>Search results based on visitor interests</title>
    <script src="http://www.google.com/jsapi?key=(AJAX API key)" type="text/javascript"></script>
    <script type="text/javascript">
    //<![CDATA[
    siteId = "(site id)";

    google.load('friendconnect', '0.8');
    google.load('search', '1');

    /**
     * Upon loading of all requested Google libraries (search+gfc),
     * initiate the OpenSocial API to enable in-page use of OpenSocial
     * JavaScript requests
     */
    function onLoad() {
      google.friendconnect.container.initOpenSocialApi({
        site: siteId,
        onload: function(securityToken) { onOpenSocialLoaded() }    });
    }

    /**
     * After OpenSocial is loaded, create a DataRequest to fetch hints
     * based on answers to visitor interest questions.
     */
    function onOpenSocialLoaded() {
      var req = opensocial.newDataRequest();
      req.add(google.friendconnect.newFetchInterestHintRequest(), 'hint');
      req.send(onReceiveHint);
    }

    /**
     * Callback function called when the DataRequest for a hint is returned.
     * Calls renderSearchControl with the current hint.
     */
    function onReceiveHint(resp) {
     var hintResp = resp.get('hint');
    
     if (!hintResp.hadError()) {
       var hintPacket = hintResp.getData();
       var googleAdClient = hintPacket['publisherId'];
       var gfcHint = hintPacket['hint'];
       renderSearchControl(gfcHint);
      }
    }

    /**
     * Renders an AJAX search control, defaulting to a serch based on the
     * hint passed.  Includes general web, local, blog searchers in addition
     * to a site restricted search for the site URL passed
     */
    function renderSearchControl(gfcHint, siteUrl) {
      var options = new google.search.SearcherOptions();
      options.setExpandMode(google.search.SearchControl.EXPAND_MODE_PARTIAL);

      // Create a search control
      var searchControl = new google.search.SearchControl();

      // Add a site restricted search
      var siteSearch = new google.search.WebSearch();
      siteSearch.setUserDefinedLabel(siteUrl);
      siteSearch.setUserDefinedClassSuffix("siteSearch");
      siteSearch.setSiteRestriction(siteUrl);
      searchControl.addSearcher(siteSearch, options);

      // Add in a full set of searchers
      searchControl.addSearcher(new google.search.LocalSearch(), options);
      searchControl.addSearcher(new google.search.WebSearch(), options);
      searchControl.addSearcher(new google.search.VideoSearch());
      searchControl.addSearcher(new google.search.BlogSearch());

      // Tell the searcher to draw itself and tell it where to attach
      searchControl.draw(document.getElementById("searchcontrol"));

      // Execute an inital search
      searchControl.execute(gfcHint);
    }
    google.setOnLoadCallback(onLoad);

    //]]>
    </script>
  </head>
  <body>
    <div id="searchcontrol">Loading...</div>
  </body>
</html>

Using Hints with AdSense

Using hints with AdSense is fairly simple. Instead of using the normal block of AdSense code for generating ads, you use the block of code provided in the Google Friend Connect admin site on the page labeled "AdSense." This block of code will automatically generate an AdSense ad inside of a gadget using the chosen format.

Here's an example call to renderAdsGadget which places a 468x60 banner ad in the HTML div with an id of adBlock:

google.friendconnect.container.renderAdsGadget(
 { id: 'adBlock',
   height: 60,
   site: '(site id)',
   'prefs':{"google_ad_format":"468x60",
            "google_ad_host":"pub-6518359383560662",
            "google_ad_client":"(pub id)"}
 });