tag:blogger.com,1999:blog-19820516755754792142024-03-18T12:29:58.237-07:00Google Data API TipsRandom code snippets for the Google Data APIs.Stephanie Liuhttp://www.blogger.com/profile/08457117592361473057noreply@blogger.comBlogger63125tag:blogger.com,1999:blog-1982051675575479214.post-67363501026725066322010-03-15T14:55:00.000-07:002010-03-15T15:00:50.316-07:00Updating a Google Site Listpage/Listitem<p>Here is a quick and dirty (by fully functional) sample of updating a <a href="http://www.google.com/support/sites/bin/answer.py?hl=en&answer=98361">listpage</a>/listitem in Google Sites using the Data API.</p>
<pre name="code" class="python">
#!/usr/bin/python
import getpass
import gdata.sites.client
import gdata.sites.data
USER = 'test@example.com'
SITE = 'YOUR_SITE_NAME'
DOMAIN = 'YOUR_DOMAIN' # or 'site' if you're not using a Google Apps domain.
# Setup our client.
client = gdata.sites.client.SitesClient(source='google-SitesListeItemUpdateTest', site=SITE, domain=DOMAIN)
client.ClientLogin(USER, getpass.getpass(), client.source)
client.ssl = True
client.http_client.debug = False
# Only fetch listpages.
feed = client.GetContentFeed(uri=client.MakeContentFeedUri() + '?kind=listpage')
# Work with first listpage we found.
lp = feed.GetListPages()[0]
print 'Listpage columns:'
for col in lp.data.column:
print 'index: %s, name: %s' % (col.index, col.name)
# Query the listpage's listems and work with first row found.
li = client.GetContentFeed(uri=lp.feed_link.href).entry[0]
print 'Row contents:'
for field in li.field:
print 'index: %s, name: %s, value: %s' % (field.index, field.name, field.text)
# Update the first fields/column's value.
li.field[0].text = 'Someone else'
entry = client.Update(li)
# Update the listpage's column heading.
#lp.data.column[0].name = 'New Heading'
#entry2 = client.Update(lp)
</pre>Eric (Google)http://www.blogger.com/profile/13033421744122011068noreply@blogger.com106tag:blogger.com,1999:blog-1982051675575479214.post-83996005347888363062010-01-13T13:18:00.000-08:002010-01-13T13:33:04.734-08:00Download your DocList arbitrary file uploads in .NET<p>The .NET client's <a href="http://code.google.com/p/google-gdata/source/browse/trunk/clients/cs/src/gdocuments/docrequest.cs#594">Download() method</a> currently handles exporting documents, presentations, and spreadsheets. However, it cannot download PDFs or arbitrary file types. Those types cannot be exported to different formats. Instead, you need to override the Download() method in docsrequest.cs to hit the content src link:</p>
<pre name="code" class="c#">
public Stream Download(Document document) {
string queryUri = "";
Service s = this.Service;
string downloadUrl = document.DocumentEntry.Content.Src.ToString();
Uri target = new Uri(downloadUrl);
return s.Query(target);
}
</pre>Eric (Google)http://www.blogger.com/profile/13033421744122011068noreply@blogger.com22tag:blogger.com,1999:blog-1982051675575479214.post-89440487618195332982009-12-30T12:09:00.000-08:002009-12-30T12:16:58.224-08:00Making a Calendar public via the ACL feed<p>Google Calendar allows individual calendars to be marked as public, meaning that they can be accessed either via the Calendar UI, embeded calendars, or the Calendar Data API without requiring a password.</p>
<p>Normally, this option is enabled from within the Calendar UI. However, it can be enabled using the Calendar Data API as well. To do so, you'd send the following HTTP request:</p>
<pre name="code" class="xml">
POST /calendar/feeds/default/acl/full
Host: www.google.com
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gAcl="http://schemas.google.com/acl/2007">
<category scheme="http://schemas.google.com/g/2005#kind" term="http://schemas.google.com/acl/2007#accessRule"></category>
<gAcl:role value="http://schemas.google.com/gCal/2005#read"></gAcl:role>
<gAcl:scope type="default"></gAcl:scope>
</entry>
</pre>
<p>In this case, this will make the default calendar as public. If you want to make a secondary calendar public, replace <code>default</code> in the request's location with the desired calendar ID.</p>Trevor Johnshttp://www.blogger.com/profile/16057630924335188659noreply@blogger.com48tag:blogger.com,1999:blog-1982051675575479214.post-55060341109792492452009-08-24T18:55:00.000-07:002009-08-31T03:48:31.376-07:00Update a document's content in .NET<pre name="code" class="c#">
class Demo {
private DocumentsService service = null;
public Demo() {
service = new DocumentsService("google-DocUpdateTip-v1");
service.setUserCredentials("username@gmail.com", "pa$$word");
GDataGAuthRequestFactory reqFactory = (GDataGAuthRequestFactory)service.RequestFactory;
reqFactory.ProtocolMajor = 3;
}
static void Main(string[] args) {
Demo demo = new Demo();
// Fetch only documents
DocumentsListQuery query = new DocumentsListQuery("http://docs.google.com/feeds/default/private/full/-/document");
DocumentsFeed doclistFeed = demo.service.Query(query);
// Update first document found
DocumentEntry entry = (DocumentEntry)doclistFeed.Entries[0];
Console.WriteLine("Updating " + entry.Title.Text + "...");
DocumentEntry updatedEntry = demo.UpdateDocContents(entry, "C:/Documents and Settings/replacement.doc");
Console.WriteLine(entry.Title.Text + " Updated!, view at " + entry.AlternateUri.ToString());
}
public DocumentEntry UpdateDocContents(DocumentEntry entryToUpdate, String replacementFileName) {
FileInfo fileInfo = new FileInfo(replacementFileName);
FileStream stream = fileInfo.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
DocumentEntry entry = null;
try {
// Convert the extension to caps and strip the "." off the front
String ext = fileInfo.Extension.ToUpper().Substring(1);
String contentType = (String)DocumentsService.GDocumentsAllowedTypes[ext];
if (contentType == null) {
throw new ArgumentException("File extension '" + ext + "' is not recognized as valid.");
}
// Set ETag because we're making an update
GDataRequestFactory factory = (GDataRequestFactory)service.RequestFactory;
factory.CustomHeaders.Add("If-Match: " + entryToUpdate.Etag);
Uri mediaUri = new Uri(entryToUpdate.MediaUri.ToString());
entry = service.Update(mediaUri, stream, contentType, entryToUpdate.Title.Text) as DocumentEntry;
} finally {
stream.Close();
}
return entry;
}
}
</pre>Eric (Google)http://www.blogger.com/profile/13033421744122011068noreply@blogger.com21tag:blogger.com,1999:blog-1982051675575479214.post-10235578544569614222009-07-27T15:01:00.000-07:002009-07-27T15:35:11.668-07:00Create a new Google Docs Spreadsheet from content using AJAXThis example shows how one could use jQuery to make an AJAX request that creates new Google Docs Spreadsheet from existing text/csv content. This particular examples uses <a href="http://code.google.com/apis/gdata/auth.html#ClientLogin">ClientLogin</a>
<pre name="code" class="javascript">
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js"
type="text/javascript"></script>
<script type="text/javascript">
var token = 'YOUR_CLIENTLOGIN_AUTH_TOKEN';
function constructContentBody(docTitle, docType, contentBody, contentType) {
var atom = ["<?xml version='1.0' encoding='UTF-8'?>",
'<entry xmlns="http://www.w3.org/2005/Atom">',
'<category scheme="http://schemas.google.com/g/2005#kind"',
' term="http://schemas.google.com/docs/2007#', docType, '"/>',
'<title>', docTitle, '</title>',
'</entry>'].join('');
var body = ['--END_OF_PART\r\n',
'Content-Type: application/atom+xml;\r\n\r\n',
atom, '\r\n',
'--END_OF_PART\r\n',
'Content-Type: ', contentType, '\r\n\r\n',
contentBody, '\r\n',
'--END_OF_PART--\r\n'].join('');
return body;
}
function createSpreadsheetFromContent(title, content, contentType) {
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
$.ajax({
type: 'POST',
url: 'http://docs.google.com/feeds/documents/private/full',
contentType: 'multipart/related; boundary=END_OF_PART',
data: constructContentBody(title, 'spreadsheet', content, contentType),
dataType: 'xml',
beforeSend: function(xhr) {
$('#data').html('uploading...');
xhr.setRequestHeader('Authorization', 'GoogleLogin auth=' + token);
},
success: function(resp) {
$('#data').html('Spreadsheet created!');
}
});
}
</script>
</head>
<body>
<div id="data"></div>
<button onclick="createSpreadsheetFromContent('ANewTitle', '1,2,3,4', 'text/csv')">Create spreadsheet w/ content</button>
</body>
</html>
</pre>Eric (Google)http://www.blogger.com/profile/13033421744122011068noreply@blogger.com35tag:blogger.com,1999:blog-1982051675575479214.post-9855045199475284732009-07-15T13:15:00.000-07:002009-08-31T03:29:39.348-07:00Download a Google Doc using the PHP library<p>At the time of writing this tip, the <code>Zend_Gdata_Docs</code> component of the PHP library does not contain the export/download functionality of the <a href="http://code.google.com/apis/documents/docs/3.0/developers_guide_protocol.html#DownloadingDocs">DocList API</a>. Here is an example of using AuthSub and <code>file_get_contents()</code> to download a document as a <code>.txt</code> file:</p>
<pre name="code" class="php">
function download($client, $url, $format=null) {
$sessionToken = $client->getHttpClient()->getAuthSubToken();
$opts = array(
'http' => array(
'method' => 'GET',
'header' => "GData-Version: 3.0\r\n".
"Authorization: AuthSub token=\"$sessionToken\"\r\n"
)
);
if ($url != null) {
$url = $url . "&exportFormat=$format";
}
return file_get_contents($url, false, stream_context_create($opts));
}
// TODO 1: setup a Zend_Gdata_Docs client in $docs_client
// TODO 2: fetch a $feed or $entry
$contentLink = $feed->entries[0]->content->getSrc();
$fileContents = download($docs_client, $contentLink, 'txt');
echo 'Contents of document "' . $feed->entries[0]->title . '":<hr>';
echo "<pre>$fileContents</pre>";
</pre>Eric (Google)http://www.blogger.com/profile/13033421744122011068noreply@blogger.com120tag:blogger.com,1999:blog-1982051675575479214.post-75638159595379397882009-04-06T16:46:00.000-07:002009-06-08T15:38:08.635-07:00OAuth in Google App Engine<p>This sample demonstrates a basic structure that you can use to perform <a href="http://code.google.com/apis/gdata/articles/oauth.html">3-legged OAuth</a> using the <a href="http://code.google.com/apis/gdata/articles/python_client_lib.html">Google Data Python client library</a> in <a href="http://code.google.com/appengine/">Google App Engine</a>. This particular example talks to the <a href="http://code.google.com/apis/documents/">Documents List Data API</a>.</p>
<p><a href="http://code.google.com/p/gdata-python-client/source/browse/#svn/trunk/samples/oauth/oauth_on_appengine">App Engine (Python) + OAuth sample</a></p>
Note: The sample is available in two versions, one that signs requests with RSA-SHA1 and another that signs with HMAC-SHA1.Eric (Google)http://www.blogger.com/profile/13033421744122011068noreply@blogger.com71tag:blogger.com,1999:blog-1982051675575479214.post-22190521692579994382009-01-05T22:00:00.000-08:002009-01-06T11:06:02.881-08:00Retrieving a document's content URL in JavaIf you have a published document, its contents can be viewed by fetching the DocumentListEntry object's <content> src URL:
<pre class="java" name="code">
List<DocumentListEntry> entries = resultFeed.getEntries();
for (DocumentListEntry entry : entries) {
MediaContent content = (MediaContent)entry.getContent();
System.out.println("View '" + entry.getTitle().getPlainText() + "' at " + content.getUri());
}
</pre>Eric (Google)http://www.blogger.com/profile/13033421744122011068noreply@blogger.com15tag:blogger.com,1999:blog-1982051675575479214.post-60438651286496569972008-12-09T12:46:00.000-08:002008-12-09T12:49:37.674-08:00Geo-Tagging a Picasa Photo in PHPAssuming that $gp is a Zend_Gdata_Photos object, this will tag the $photoEntry with the specified latitude and longitude. (Note you still have to insert or update the $photoEntry.)
<pre name="code" class="php">
$gp->registerPackage('Zend_Gdata_Geo');
$gp->registerPackage('Zend_Gdata_Geo_Extension');
$where = $gp->newGeoRssWhere();
$position = $gp->newGmlPos('37.0 -122.0');
$where->point = $gp->newGmlPoint($position);
$photoEntry->setGeoRssWhere($where);
</pre>Unknownnoreply@blogger.com7tag:blogger.com,1999:blog-1982051675575479214.post-58675702397687845422008-12-05T16:13:00.000-08:002008-12-05T16:41:03.186-08:00Constructing unsupported XML elements using the PHP client library<p>When a new XML element is introduced, client library support needs to be added. At times, the services move a bit faster than the client libraries. This means that (as per the AtomPub spec) clients must store all unknown elements returned from the server. Clients may also wish to be able to use new functionality which isn't explicitly supported yet in the libraries, so clients can allow you to create arbitrary XML elements and attach them to entries.</p>
<p>The PHP client library accomplishes this using extension elements, represented as instances of <code>Zend_Gdata_App_Extension_Element</code>. Each time the server returns an element that is unknown to the client library, the client creates an instance of that class and stores it in the <code>extensionElements</code> array of the class representing the parent element. In order to send arbitrary XML, you can also construct a new <code>Zend_Gdata_App_Extension_Element</code> and place it in the <code>extensionElements</code> array.</p>
<p>As an example, Google Base added a <a href="http://code.google.com/apis/base/starting-out.html#PubPriority">publishing priority</a> option, specified by adding a <code><gm:publishing_priority></code> element. At the time of this post, the element isn't supported by a class in the PHP client library, so here's how you can add it.</p>
<pre name="code" class="php">
<?php
require_once 'Zend/Gdata/Gbase.php';
require_once 'Zend/Gdata/App/Extension/Element.php';
$base = new Zend_Gdata_Gbase();
$entry = $base->newItemEntry();
// Constructing a Zend_Gdata_App_Extension_Control
$control = $base->newControl();
// Constructing the extension element, and placing it into the array
// of extension owned by the class representing the parent app:control element.
// Arguments:
// element name (including prefix)
// namespace prefix
// namespace URI
// text node of the new element
$control->extensionElements = array(
new Zend_Gdata_App_Extension_Element('gm:publishing_priority', 'gm', 'http://base.google.com/ns-metadata/1.0', 'high')
);
$entry->control = $control;
</pre>
<p>Of course, if you discover missing elements, please feel free to file an issue in the <a href="http://framework.zend.com/issues/">Zend Framework issue tracker</a> and, since it's open source, you can also contribute a fix back to the project. More information on contributing can be found on the <a href="http://framework.zend.com/wiki/display/ZFDEV/Contributing+to+Zend+Framework">Zend Framework wiki</a>.api.rboydhttp://www.blogger.com/profile/08405436280227503401noreply@blogger.com16tag:blogger.com,1999:blog-1982051675575479214.post-92096316861328630092008-12-03T21:34:00.000-08:002008-12-07T11:34:54.585-08:00Using the JavaScript Client Library w/ non-supported ServicesThe <a href="http://code.google.com/apis/gdata/client-js.html">JavaScript client library</a> has helper methods for Calendar, Contacts, Blogger, and Google Finance. However, you can use it with just about any Google Data API to access authenticated/private feeds.
This example uses the <a href="http://code.google.com/apis/documents/overview.html">DocList API</a>.
<pre name="code" class="javascript">
<script src="http://www.google.com/jsapi" type="text/javascript"></script>
<script type="text/javascript">
google.load('gdata', '1.x');
google.setOnLoadCallback(initialize);
function initialize() {
var scope = 'http://docs.google.com/feeds/';
if (google.accounts.user.checkLogin(scope)) {
var service = new google.gdata.client.GoogleService('writely', 'DocList-App-v1.0');
service.getFeed(scope + 'documents/private/full/', handleFeed, handleError);
} else {
var token = google.accounts.user.login(scope); // can ignore returned token
}
};
var handleFeed = function(response) {
var entries = response.feed.entry;
if (!entries.length) {
alert('You have no entries!');
return;
}
var html = [];
for (var i = 0, entry; entry = entries[i]; i++) {
var title = entry.title.$t;
html.push('<li>' + title + '</li>');
}
document.getElementById('data').innerHTML = html.join('');
};
var handleError = function(e) {
alert('Error: ' + e.cause ? e.cause.statusText : e.message);
};
</script>
<div id="data"><!-- dynamically filled --></div>
</pre>Eric (Google)http://www.blogger.com/profile/13033421744122011068noreply@blogger.com469tag:blogger.com,1999:blog-1982051675575479214.post-16679816009820723522008-11-23T10:05:00.000-08:002009-06-09T14:33:20.864-07:002 Legged OAuth in PythonGoogle Apps Premier/Education administrators can take advantage of <a href="http://code.google.com/apis/accounts/docs/OAuth.html#GoogleAppsOAuth">2 legged OAuth</a> to communicate with the Google Data APIs.
If you're using the <a href="http://code.google.com/p/gdata-python-client/">Google Data Python client library</a> 1.2.3+:
<strong>An updated sample is here available <a href="http://code.google.com/p/gdata-python-client/source/browse/trunk/samples/oauth/2_legged_oauth.py">here</a></strong>.
Otherwise, if you're stuck with an older version of the library (< 1.2.3), you can use the
<a href="http://code.google.com/p/oauth/">Python OAuth library from oauth.net</a>.
<pre name="code" class="python">
import urllib
import oauth
import gdata.contacts
import gdata.contacts.service
CONSUMER_KEY = 'yourdomain.com'
CONSUMER_SECRET = 'YOUR_CONSUMER_SECRET'
CONTACTS_URL = 'http://www.google.com/m8/feeds/contacts/default/full'
# Setup 2 legged OAuth consumer based on our admin "credentials"
consumer = oauth.OAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET)
user = 'any.user@yourdomain.com'
params = {'max-results': 50, 'xoauth_requestor_id': user}
# Construct the request manually and sign it using HMAC-SHA1
# Note: The params dictionary needs to be passed in separately from the base URL
request = oauth.OAuthRequest.from_consumer_and_token(
consumer, http_method='GET', http_url=CONTACTS_URL, parameters=params)
request.sign_request(oauth.OAuthSignatureMethod_HMAC_SHA1(), consumer, None)
# See patch @ http://code.google.com/p/oauth/issues/detail?id=31
headers = request.to_header()
client = gdata.contacts.service.ContactsService()
# Query the user's contacts and print their name & email
uri = '%s?%s' % (request.http_url, urllib.urlencode(params))
feed = client.GetFeed(uri, extra_headers=headers, converter=gdata.contacts.ContactsFeedFromString)
for entry in feed.entry:
print '%s, %s' % (entry.title.text, entry.email[0].address)
</pre>Eric (Google)http://www.blogger.com/profile/13033421744122011068noreply@blogger.com9tag:blogger.com,1999:blog-1982051675575479214.post-33939629298139181362008-11-21T11:17:00.000-08:002008-11-21T11:19:03.671-08:00Paging Through Feeds in Python<p>If you have a feed object you use</p>
<pre name="code" class="python">
feed.GetNextLink().href
</pre>
<p>to find the URL of the next page of results. Then you can use that to
query for more like so:</p>
<pre name="code" class="python">
nextPageFeed = service.Query(feed.GetNextLink().href)
</pre>Unknownnoreply@blogger.com3tag:blogger.com,1999:blog-1982051675575479214.post-30510711759687754702008-11-20T13:32:00.000-08:002008-11-24T12:43:22.430-08:00Dealing with YouTube videos that you can't embed<p>If you are using the YouTube Player APIs you will notice that some videos will refuse to load on your site. This is because the owner of that video has requested that embedding be disabled. You can tell on the video watch page because it will say "Embedding disabled by request" in place of the standard embed code:</p>
<p><a href="http://www.youtube.com/watch?v=juPxfHO50oo">http://www.youtube.com/watch?v=juPxfHO50oo</a></p>
<p>If you are retrieving videos to display using the Data API you should use the <a href="http://code.google.com/apis/youtube/2.0/reference.html#formatsp">format parameter</a> set to 5 in order to filter out non-embeddable videos:</p>
<pre>http://gdata.youtube.com/feeds/api/videos?v=2&format=5</pre>
<p>The Data API also has the <a href="http://code.google.com/apis/youtube/2.0/reference.html#youtube_data_api_tag_yt:noembed"><code><yt:noembed></code></a> tag included in the video entry when the video cannot be embedded. A video entry can be retrieved using:</p>
<pre>http://gdata.youtube.com/feeds/api/videos/VIDEO_ID</pre>
<p>If you are using the Player JavaScript or AS2 API you should also handle the <a href="http://code.google.com/apis/youtube/js_api_reference.html#Events">onError event</a>.</p>Unknownnoreply@blogger.com13tag:blogger.com,1999:blog-1982051675575479214.post-22356684760026278662008-11-20T09:33:00.000-08:002008-12-10T13:46:47.043-08:002 Legged OAuth in PHPGoogle Apps Premier/Education administrators can take advantage of <a href="http://code.google.com/apis/accounts/docs/OAuth.html#GoogleAppsOAuth">2 legged OAuth</a> to communicate with the Google Data APIs. This sample makes use of the <a href="http://oauth.googlecode.com/svn/code/php/OAuth.php">PHP OAuth library</a> from oauth.net.
<pre name="code" class="php">
<?php
require_once('OAuth.php');
// Establish an OAuth consumer based on our admin 'credentials'
$CONSUMER_KEY = 'yourdomain.com';
$CONSUMER_SECRET = 'YOUR_CONSUMER_SECRET';
$consumer = new OAuthConsumer($CONSUMER_KEY, $CONSUMER_SECRET, NULL);
// Setup OAuth request based our previous credentials and query
$user= 'any.user@yourdomain.com';
$base_feed = 'http://www.google.com/m8/feeds/contacts/default/full/';
$params = array('max-results' => 10, 'xoauth_requestor_id' => $user);
$request = OAuthRequest::from_consumer_and_token($consumer, NULL, 'GET', $base_feed, $params);
// Sign the constructed OAuth request using HMAC-SHA1
$request->sign_request(new OAuthSignatureMethod_HMAC_SHA1(), $consumer, NULL);
// Make signed OAuth request to the Contacts API server
$url = $base_feed . '?' . implode_assoc('=', '&', $params);
echo send_request($request->get_normalized_http_method(), $url, $request->to_header());
/**
* Makes an HTTP request to the specified URL
* @param string $http_method The HTTP method (GET, POST, PUT, DELETE)
* @param string $url Full URL of the resource to access
* @param string $auth_header (optional) Authorization header
* @param string $postData (optional) POST/PUT request body
* @return string Response body from the server
*/
function send_request($http_method, $url, $auth_header=null, $postData=null) {
$curl = curl_init($url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_FAILONERROR, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
switch($http_method) {
case 'GET':
if ($auth_header) {
curl_setopt($curl, CURLOPT_HTTPHEADER, array($auth_header));
}
break;
case 'POST':
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/atom+xml',
$auth_header));
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
break;
case 'PUT':
curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/atom+xml',
$auth_header));
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $http_method);
curl_setopt($curl, CURLOPT_POSTFIELDS, $postData);
break;
case 'DELETE':
curl_setopt($curl, CURLOPT_HTTPHEADER, array($auth_header));
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $http_method);
break;
}
$response = curl_exec($curl);
if (!$response) {
$response = curl_error($curl);
}
curl_close($curl);
return $response;
}
/**
* Joins key:value pairs by inner_glue and each pair together by outer_glue
* @param string $inner_glue The HTTP method (GET, POST, PUT, DELETE)
* @param string $outer_glue Full URL of the resource to access
* @param array $array Associative array of query parameters
* @return string Urlencoded string of query parameters
*/
function implode_assoc($inner_glue, $outer_glue, $array) {
$output = array();
foreach($array as $key => $item) {
$output[] = $key . $inner_glue . urlencode($item);
}
return implode($outer_glue, $output);
}
?>
</pre>Eric (Google)http://www.blogger.com/profile/13033421744122011068noreply@blogger.com248tag:blogger.com,1999:blog-1982051675575479214.post-32897594830404529452008-11-20T07:45:00.000-08:002008-11-20T07:56:58.434-08:00XML PHP Pretty Printer<pre name="code" class="php">
<?
/** Prettifies an XML string into a human-readable and indented work of art
* @param string $xml The XML as a string
* @param boolean $html_output True if the output should be escaped (for use in HTML)
*/
function xmlpp($xml, $html_output=false) {
$xml_obj = new SimpleXMLElement($xml);
$level = 4;
$indent = 0; // current indentation level
$pretty = array();
// get an array containing each XML element
$xml = explode("\n", preg_replace('/>\s*</', ">\n<", $xml_obj->asXML()));
// shift off opening XML tag if present
if (count($xml) && preg_match('/^<\?\s*xml/', $xml[0])) {
$pretty[] = array_shift($xml);
}
foreach ($xml as $el) {
if (preg_match('/^<([\w])+[^>\/]*>$/U', $el)) {
// opening tag, increase indent
$pretty[] = str_repeat(' ', $indent) . $el;
$indent += $level;
} else {
if (preg_match('/^<\/.+>$/', $el)) {
$indent -= $level; // closing tag, decrease indent
}
if ($indent < 0) {
$indent += $level;
}
$pretty[] = str_repeat(' ', $indent) . $el;
}
}
$xml = implode("\n", $pretty);
return ($html_output) ? htmlentities($xml) : $xml;
}
echo '<pre>' . xmlpp($xml, true) . '</pre>';
?>
</pre>Eric (Google)http://www.blogger.com/profile/13033421744122011068noreply@blogger.com42tag:blogger.com,1999:blog-1982051675575479214.post-34570374715551195982008-11-14T15:20:00.000-08:002008-11-21T01:24:47.056-08:00Accessing a feed using ClientLogin & RubyExample of accessing the DocList API using ClientLogin in Ruby:
<pre name="code" class="ruby">
require 'net/http'
require 'net/https'
require 'cgi'
require 'rubygems'
require 'xmlsimple'
require 'pp'
def get_feed(uri, headers=nil)
uri = URI.parse(uri)
Net::HTTP.start(uri.host, uri.port) do |http|
return http.get(uri.path, headers)
end
end
email = 'user@gmail.com'
password = CGI::escape('pa$$word')
service = 'writely'
source = 'MyCompany-MyApp-0.1'
path = '/accounts/ClientLogin'
data = ["accountType=HOSTED_OR_GOOGLE",
"Email=#{email}",
"Passwd=#{password}",
"service=#{service}",
"source=#{source}"].join('&')
http = Net::HTTP.new(host='www.google.com', port=443)
http.use_ssl = true
http.start
headers = {'Content-Type' => 'application/x-www-form-urlencoded'}
resp, data = http.post(path, data, headers)
token = data[/Auth=(.*)/, 1] # parse out the Auth token
headers['Authorization'] = "GoogleLogin auth=#{token}"
resp = get_feed('http://docs.google.com/feeds/documents/private/full', headers)
doc = XmlSimple.xml_in(resp.body, 'KeyAttr' => 'name')
pp doc
</pre>Eric (Google)http://www.blogger.com/profile/13033421744122011068noreply@blogger.com8tag:blogger.com,1999:blog-1982051675575479214.post-2572044810450038782008-10-31T11:00:00.000-07:002008-10-31T11:14:15.503-07:00Force PHP to use a Google Account in ClientLoginSometimes you have a Google Apps account that is also registered as a Google Account and you want to make sure you are logging in as the Google Account, especially when you want to use a service like Picasa Web Albums that does not have an Apps version.
To do this using ClientLogin you need to do something like:
<pre name="code" class="php">
$serviceName = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$username = "user@gmail.com";
$pass = "password";
$accountType = "GOOGLE";
$client = Zend_Gdata_ClientLogin::getHttpClient($username, $pass, $serviceName, null, Zend_Gdata_ClientLogin::DEFAULT_SOURCE, null, null, Zend_Gdata_ClientLogin::CLIENTLOGIN_URI, $accountType);
</pre>Unknownnoreply@blogger.com4tag:blogger.com,1999:blog-1982051675575479214.post-9961591339018097582008-10-23T11:24:00.000-07:002008-10-23T11:27:51.282-07:00Using AuthSub on App EngineFor a full explanation, see this article: <a href="http://code.google.com/appengine/articles/gdata.html">Retrieving Authenticated Google Data Feeds with Google App Engine</a>
<pre name="code" class="python"> # Initialize a client to talk to Google Data API services.
client = gdata.service.GDataService()
gdata.alt.appengine.run_on_appengine(client)
session_token = None
# Find the AuthSub token and upgrade it to a session token.
auth_token = gdata.auth.extract_auth_sub_token_from_url(self.request.uri)
if auth_token:
# Upgrade the single-use AuthSub token to a multi-use session token.
session_token = client.upgrade_to_session_token(auth_token)
if session_token and users.get_current_user():
# If there is a current user, store the token in the datastore and
# associate it with the current user. Since we told the client to
# run_on_appengine, the add_token call will automatically store the
# session token if there is a current_user.
client.token_store.add_token(session_token)
elif session_token:
# Since there is no current user, we will put the session token
# in a property of the client. We will not store the token in the
# datastore, since we wouldn't know which user it belongs to.
# Since a new client object is created with each get call, we don't
# need to worry about the anonymous token being used by other users.
client.current_token = session_token</pre>Jeff Scudderhttp://www.blogger.com/profile/10171738514445498413noreply@blogger.com2tag:blogger.com,1999:blog-1982051675575479214.post-60979704512612791632008-10-10T11:50:00.000-07:002008-10-10T11:51:45.671-07:00Uploading a private YouTube video in .NET<pre name="code" class="CSharp">
newEntry.Media.ExtensionElements.Add(new Google.GData.YouTube.Private());
</pre>Unknownnoreply@blogger.com7tag:blogger.com,1999:blog-1982051675575479214.post-20917932805278124322008-10-09T14:23:00.000-07:002008-10-09T14:29:38.568-07:00Changing the default timeout for HTTP requests in PHPIf you are using the PHP client library to <a href="http://code.google.com/apis/youtube/developers_guide_php.html">upload videos to YouTube</a>, you may run into problems where your requests are timing out after ten seconds. The client library makes use of the <a href="http://framework.zend.com/apidoc/core/Zend_Http/Client/Zend_Http_Client.html">Zend_Http_Client</a> component to dispatch requests to the API server. By default the client object is initialized to time out any requests after 10 seconds. The snippet below shows how you may increase the timeout to 30 seconds:
<pre name="code" class="php">
// assuming your Zend_Http_Client already exists as $httpClient
// and that you want to change the timeout from the 10 second default to 30 seconds
$config = array('timeout' => 30);
$httpClient->setConfig($config);
</pre>Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-1982051675575479214.post-43145585654933721052008-10-03T11:19:00.000-07:002008-10-03T11:21:08.964-07:00Saving a Feed or Entry as XML with the .NET client library<p>The SaveToXml() method takes either a Stream or a XmlWriter.</p>
<pre name="code" class="C#">
FileStream file = new FileStream("C:\\testxml.txt", FileMode.Create);
feed.SaveToXml(file);
file.Close();
</pre>Unknownnoreply@blogger.com4tag:blogger.com,1999:blog-1982051675575479214.post-2690111550231726262008-10-03T10:12:00.001-07:002008-10-03T10:17:28.963-07:00Getting RTSP Streams From the YouTube API Without Redirects<p>So if you want RTSP streams from the YouTube API you probably looked at our <a href="http://code.google.com/apis/youtube/reference.html#formatsp">documentation for the format parameter</a> and made a request like</p>
<pre>
http://gdata.youtube.com/feeds/api/videos?format=1
</pre>
<p>or</p>
<pre>http://gdata.youtube.com/feeds/api/videos?format=6</pre>
<p>or even</p>
<pre>http://gdata.youtube.com/feeds/api/videos?format=1,6</pre>
<p>But you may have noticed you get a lot of redirects, which may upset your RTSP player. What you should be doing is requesting the <strong>mobile</strong> projection. So your request should look like</p>
<pre>http://gdata.youtube.com/feeds/mobile/videos?format=1</pre>Unknownnoreply@blogger.com6tag:blogger.com,1999:blog-1982051675575479214.post-16670511965729085062008-09-24T10:15:00.000-07:002008-09-24T10:16:47.230-07:00Retrieving a user profile image in YouTube using Python<pre name="code" class="python">
import gdata.youtube
import gdata.youtube.service
yt_service = gdata.youtube.service.YouTubeService()
user_entry = yt_service.GetYouTubeUserEntry(username='GoogleDevelopers')
print user_entry.thumbnail.url
</pre>Unknownnoreply@blogger.com9tag:blogger.com,1999:blog-1982051675575479214.post-7365206718944843492008-09-21T20:11:00.000-07:002008-09-21T20:17:39.075-07:00Catching Exceptions from the Java Client Library<p>Just like our other client libraries, if you're using the Java client library and encounter a server error, an exception will be generated. This will cause Java to stop in its tracks and print out the contents of the exception. While this is useful for debugging, in a production application you'll probably want your application to fail gracefully, perhaps displaying a nice error message to your users.</p>
<p>You can do this by wrapping your code in a <code>try</code> block and using an <code>catch</code> statement:</p>
<pre name="code" class="java">
try {
// Do something that accesses the network
} catch (IOError e) {
// Insert custom error handling code here.
System.out.println(e.toString());
} catch (ServiceException e) {
// Insert custom error handling code here.
System.out.println(e.toString());
}</pre>
<p>Most errors will be instances of either <code><a href="http://java.sun.com/javase/6/docs/api/java/io/IOError.html">IOError</a></code> or <code><a href="http://code.google.com/apis/gdata/javadoc/com/google/gdata/util/ServiceException.html">ServiceException</a></code>, depending on the cause.</p>Trevor Johnshttp://www.blogger.com/profile/04465221509532799738noreply@blogger.com1