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.
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.
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:
Interests data is stored as part of the OpenSocial
Person object and accessable using either the
JavaScript code or the
REST/RPC protocols.
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"
}
}
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"
}
}
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);
}
}
}
}
};
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.
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:
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.
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 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)"}
});