Develop PHP applications with Picasa Web Albums

Integrate data from Picasa Web Albums into a custom PHP application

Picasa Web Albums offers Web application developers a REST-based Data API to manipulate the photos and albums stored on its servers. PHP's SimpleXML extension and Zend's GData Library are ideal to process the XML feeds generated by this API so you can customize PHP photo management and photo sharing applications. In this article, meet the Picasa Web Albums Data API and see how you can use it to retrieve photos and photo metadata; add, modify and delete photos; and perform keyword searches of Picasa's user-generated content.

Share:

Introduction

Frequently used acronyms

  • API: application programming interface
  • HTTP: Hypertext Transfer Protocol
  • MIME: Multipurpose Internet Mail Extensions
  • PHP: PHP Hypertext Preprocessor
  • REST: REpresentational State Transfer
  • URL: Uniform Resource Locator
  • XML: Extensible Markup Language

As someone who takes a lot of digital photos, I'm no stranger to disk clutter—my computer has hundreds of megabytes of old photos, most with sketchy or non-existent labels. Only a few months ago, when I finally tried out Google's free Picasa photo management software, some method began to emerge from the madness.

But Picasa is more than just a tool you can download off the Web—it also exists online, as a free photo-sharing site called Picasa Web Albums. At the Web site, users can upload photos and videos, categorize them into albums, tag them with keywords for easy searching, and share them with others. The best part, though, is behind the scenes: Like many other Google tools, Picasa Web Albums exposes a Data API which allows developers to build customized applications around the images stored in user photo albums. You can access this API, which follows the REST model, through any XML-capable development toolkit. The API already has client libraries for many common programming languages...including the one I use most often, PHP.

This article will introduce you to the Google Picasa Web Albums Data API, and show you how to integrate and use your Picasa albums and photos with PHP applications. Its examples include how to retrieve albums and photos, upload photos through a customized Web interface, and search for photos by keyword. So come on in, and get started!


Understanding the Picasa Web Albums Data API

Before you dive into the PHP code, a few words about the Picasa Web Albums Data API are in order. As with all REST-based services, things start with an HTTP request to a designated resource. This HTTP request contains a query with one or more input parameters; the server replies to the query with an Atom- or RSS-formatted response (or feed), suitable for parsing in any XML-aware client.

A typical Picasa feed contains a lot of information. To see an example, try popping the URL http://picasaweb.google.com/data/feed/api/user/userid?kind=photo into your Web browser (after replacing userid with your Google Account username) and, assuming you already have some photos and albums saved in Picasa Web Albums, you should see something similar to Listing 1.

Take a quick glance through this output to familiarize yourself with its main elements:

  • The Picasa Web Albums Data API responds to a REST request with a feed of the requested data. Various feeds are available: album feeds, photo feeds, user feeds, user contact feeds, and so on. In most cases, therefore, the XML response contains a <feed> element as the root element. The <feed> element contains <link> elements, which contain URLs for the current, next, and previous pages of the result set, and <openSearch:> elements, which contain summary statistics for the search.
  • The outermost <feed> element encloses one or more <entry> elements, each representing a photo matching the query. Each <entry> contains further information on the photo it represents, including the caption, file name, publication date, and author. Each <entry> also contains <link> elements, which provide URL links to view and edit the photo.
  • A set of <gphoto:> elements within each <entry> contain detailed information on the photo: its size, dimensions, album, comment count, and so on.
  • A <media:group> element within each <entry> contains photo keywords and thumbnail links.

Retrieving photos with SimpleXML

Now you'll proceed to an example of parsing a Picasa Web Albums feed with PHP and SimpleXML. Listing 2 takes the feed from Listing 1 and uses SimpleXML to extract relevant fragments of data from it and format it into a Web page:

Listing 2: Retrieving photo listings with SimpleXML
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Listing album contents</title>
    <style>
    body {
      font-family: Verdana;      
    }
    h2 {
      color: red; 
      text-decoration: none;  
    }
    span.attr {
      font-weight: bolder;  
    }
    img {
      float: left;  
    }
    </style>    
  </head>
  <body>
    <?php
    $userid = 'userid%40googlemail.com';

    // build feed URL
    $feedURL = "http://picasaweb.google.com/data/feed/api/user/$userid?kind=photo";
    
    // read feed into SimpleXML object
    $sxml = simplexml_load_file($feedURL);
    
    // get album name and number of photos
    $counts = $sxml->children('http://a9.com/-/spec/opensearchrss/1.0/');
    $total = $counts->totalResults; 
    ?>
    <h1><?php echo $sxml->title; ?></h1>
    <?php echo $total; ?> photo(s) found.
    <p/>

    <?php    
    // iterate over entries in album
    // print each entry's title, size, dimensions, tags, and thumbnail image
    foreach ($sxml->entry as $entry) {
      $title = $entry->title;
      $summary = $entry->summary;
      
      $gphoto = $entry->children('http://schemas.google.com/photos/2007');
      $size = $gphoto->size;
      $height = $gphoto->height;
      $width = $gphoto->width;
      
      $media = $entry->children('http://search.yahoo.com/mrss/');
      $thumbnail = $media->group->thumbnail[1];
      $tags = $media->group->keywords;
      
      echo "<h2>$summary</h2>\n";
      echo "<table><tr><td><img src=\"" . 
      $thumbnail->attributes()->{'url'} . "\"/></td>\n";
      echo "<td><span class=\"attr\">File</span>: $title 
      <br />\n";
      echo "<span class=\"attr\">Size</span>: $size bytes 
      ($height x $width) <br />\n";
      echo "<span class=\"attr\">Tags</span>: $tags 
      </td></tr></table>\n";
    }
    ?>
    
  </body>
</html>

Figure 1 demonstrates the output you might see:

Figure 1. A Web page listing photos
A Web page listing photos

Listing 2 begins by using the simplexml_load_file() object to send a request to the feed URL and convert the response into a SimpleXML object. It then iterates over the <entry> elements in the response, processing each one using a foreach() loop and retrieving the information shown in Figure 1. Child nodes under each <entry> are represented as SimpleXML object properties—for example, the <title> node is represented by $entry->title, the <summary> node by $entry->summary, and so on. Keywords and a thumbnail image are obtained from the <media:group> element under each <entry>.


Working with user and album feeds

If you want to obtain a listing of all the albums created by a particular user, this too is easily managed. You use the feed URL in Listing 2, but alter the kind parameter to list albums instead of photos. Listing 3 demonstrates how to obtain and parse such a feed:

Listing 3: Retrieving album listings with SimpleXML
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Listing albums</title>
    <style>
    body {
      font-family: Verdana;      
    }
    </style>    
  </head>
  <body>
    <h1>Album Listing</h2>
    <?php
    $userid = 'userid%40googlemail.com';
    
    // build feed URL
    $feedURL = "http://picasaweb.google.com/data/feed/api/user/$userid?kind=album";
    
    // read feed into SimpleXML object
    $sxml = simplexml_load_file($feedURL);
    
    // get album names and number of photos in each
    echo "<ul>";
    foreach ($sxml->entry as $entry) {      
      $title = $entry->title;
      $gphoto = $entry->children('http://schemas.google.com/photos/2007');
      $numphotos = $gphoto->numphotos;      
      echo "<li>$title - $numphotos photo(s)</li>\n";
    }
    echo "</ul>";
    ?>
    
  </body>
</html>

Figure 2 demonstrates the revised output:

Figure 2. A Web page listing albums
A Web page listing albums

To obtain an album feed, which lists only the photos in a specific album, specify the album name in the feed URL. Listing 4 demonstrates how to obtain and parse such a feed:

Listing 4: Retrieving photos in an album with SimpleXML
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Listing album contents</title>
    <style>
    body {
      font-family: Verdana;      
    }
    h2 {
      color: red; 
      text-decoration: none;  
    }
    span.attr {
      font-weight: bolder;  
    }
    img {
      float: left;  
    }
    </style>    
  </head>
  <body>
    <?php
    $userid = 'userid%40googlemail.com';
    $album = 'France2008';
    
    // build feed URL
    $feedURL = "http://picasaweb.google.com/data/feed/api/user/$userid/album/$album";
    
    // read feed into SimpleXML object
    $sxml = simplexml_load_file($feedURL);
    
    // get album name and number of photos
    $counts = $sxml->children('http://a9.com/-/spec/opensearchrss/1.0/');
    $total = $counts->totalResults; 
    ?>
    <h1><?php echo $sxml->title; ?></h1>
    <?php echo $total; ?> photo(s) found.
    <p/>

    <?php    
    // iterate over entries in album
    // print each entry's title, size, dimensions, tags, and thumbnail image
    foreach ($sxml->entry as $entry) {
      $title = $entry->title;
      $summary = $entry->summary;
      
      $gphoto = $entry->children('http://schemas.google.com/photos/2007');
      $size = $gphoto->size;
      $height = $gphoto->height;
      $width = $gphoto->width;
      
      $media = $entry->children('http://search.yahoo.com/mrss/');
      $thumbnail = $media->group->thumbnail[1];
      $tags = $media->group->keywords;
      
      echo "<h2>$summary</h2>\n";
      echo "<table><tr><td><img src=\"" . 
      $thumbnail->attributes()->{'url'} . "\"/></td>\n";
      echo "<td><span class=\"attr\">File</span>: $title 
      <br />\n";
      echo "<span class=\"attr\">Size</span>: $size bytes 
      ($height x $width) <br />\n";
      echo "<span class=\"attr\">Tags</span>: $tags 
      </td></tr></table>\n";
    }
    ?>
    
  </body>
</html>

And Figure 3 demonstrates what the output looks like:

Figure 3. A Web page listing photos in an album
A Web page listing photos in an album

Retrieving photos with the Zend GData Client Library

The previous examples have demonstrated how easy it is to read and use data from Picasa Web Albums using PHP. However, read-only access has its limitations. To perform more sophisticated operations that require write access, such as adding new photos or albums, you need to add user authentication to your application using one of the two Google-approved authentication methods: AuthSub or ClientLogin.

To perform this type of authentication manually is a fairly messy task, and requires a fair bit of code to account for the various scenarios that might crop up during a typical authentication transaction. Fortunately, you don't have to worry too much about this: Zend's GData Client Library, designed specifically for developers trying to integrate PHP applications with the Google Data API, handles all the details for you. This library, which you can download separately (see Resources for a link), provides a convenient, object-oriented interface to the Google Data API, and encapsulates most common tasks (including authentication), thus leaving you free to focus on the core functions of your application.

Listing 5 demonstrates, by replicating the functionality of Listing 4 with the Zend GData Client Library:

Listing 5: Retrieving photos in an album with the Zend GData Client Library
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Listing album contents</title>
    <style>
    body {
      font-family: Verdana;      
    }
    h2 {
      color: red; 
      text-decoration: none;  
    }
    span.attr {
      font-weight: bolder;  
    }
    img {
      float: left;  
    }
    </style>    
  </head>
  <body>
    <?php
    // load library
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
    Zend_Loader::loadClass('Zend_Gdata_Photos');
    Zend_Loader::loadClass('Zend_Http_Client');
    
    // create authenticated HTTP client for Picasa service
    $svc = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
    $user = "userid@gmail.com";
    $pass = "secret";
    $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $svc);
    $gphoto = new Zend_Gdata_Photos($client);
    
    // generate query to get album feed
    $query = $gphoto->newAlbumQuery();    
    $query->setUser("default");
    $query->setAlbumName("France2008");
    
    // get and parse album feed
    // get album name and number of photos
    try {
      $feed = $gphoto->getAlbumFeed($query);
    } catch (Zend_Gdata_App_Exception $e) {
      echo "Error: " . $e->getResponse();
    }
    ?>
    <h1><?php echo $feed->getTitle(); ?></h1>
    <?php echo $feed->getTotalResults(); ?> photo(s) found.
    <p/>

    <?php        
    // process each photo entry
    // print each entry's title, size, dimensions, tags, and thumbnail image
    foreach ($feed as $entry) {
      $title = $entry->getTitle();
      $summary = $entry->getSummary();
      $thumbnail = $entry->getMediaGroup()->getThumbnail();
      $tags = $entry->getMediaGroup()->getKeywords();
      $size = $entry->getGphotoSize();
      $height = $entry->getGphotoHeight();
      $width = $entry->getGphotoWidth();
      
      echo "<h2>$summary</h2>\n";
      echo "<table><tr><td><img src=\"" . 
      $thumbnail[1]->url . "\"/></td>\n";
      echo "<td><span class=\"attr\">File</span>: $title 
      <br />\n";
      echo "<span class=\"attr\">Size</span>: $size bytes 
      ($height x $width) <br />\n";
      echo "<span class=\"attr\">Tags</span>: $tags 
      </td></tr></table>\n";
    }
    ?>

  </body>
</html>

Listing 5 starts by loading the Zend class libraries, and then initializes an instance of the Zend_Http_Client class. This client is provided with the necessary user authentication information, and opens an authenticated connection to the Picasa service. Once an authenticated connection is opened, the getAlbumFeed() method is used to retrieve the album feed. This method accepts an AlbumQuery object, which is configured with two parameters:

  • The owner's Picasa user name
  • The album name

The response to the getAlbumFeed() API call is an XML feed similar to that in Listing 1, which is then parsed and converted into a PHP object. This object exposes getter and setter magic methods, making it easy to retrieve specific pieces of information from the nodes in the XML feed through object/property notation. Thus, for example, the getSummary() method retrieves the photo summary, while the getGphotoSize() method retrieves the photo size in bytes.

Figure 4 demonstrates the output you might see:

Figure 4. A Web page listing photos in an album
A Web page listing photos in an album

Adding new photos

Now that you can list photos, how about adding new photos? This is actually not as complicated as it might seem.

To add a new photo, simply POST the image file to the album feed URL, together with the associated metadata, as a MIME-encoded block. One part of the block should contain the XML-encoded <entry>; the other, the binary data. Listing 6 has an example of one such block:

Listing 6: An example POST packet for adding a new photo with metadata
--=_00fb9c5957c74e05312cbc2f9d1d09b8
Content-Type: application/atom+xml
<atom:entry xmlns:atom="http://www.w3.org/2005/Atom">
  <atom:category term="http://schemas.google.com/photos/2007#photo" 
  scheme="http://schemas.google.com/g/2005#kind"/>
  <atom:summary type="text">Notre Dame, Paris</atom:summary>
  <media:group xmlns:media="http://search.yahoo.com/mrss/">
    <media:keywords>church, paris</media:keywords>
  </media:group>
</atom:entry>

--=_00fb9c5957c74e05312cbc2f9d1d09b8
Content-Type: image/jpeg

[binary data]

If you use the Zend library, things are even simpler. You only need to call the insertPhotoEntry() method, which creates the POST packet in Listing 6 and then posts it to the album feed. Listing 7 demonstrates, as it builds a Web form that asks the user for the photo file, title, and tags, and then uses the Zend library to construct the corresponding POST packet and save the data to an album:

Listing 7: Adding a new photo
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Adding photos to an album</title>
    <style>
    body {
      font-family: Verdana;      
    }
    li {
      border-bottom: solid black 1px;      
      margin: 10px; 
      padding: 2px; 
      width: auto;
      padding-bottom: 20px;
    }
    h2 {
      color: red; 
      text-decoration: none;  
    }
    span.attr {
      font-weight: bolder;  
    }
    </style>    
  </head>
  <body>
    <h1>Add Photo</h1>
    <?php if (!isset($_POST['submit'])) { ?>
    <form method="post" action="<?php 
     echo htmlentities($_SERVER['PHP_SELF']); ?>" enctype="multipart/form-data">
      Title: <br/>
      <input name="title" type="text" size="25" /><p/>
      File to upload: <br/>
      <input name="photofile" type="file" /><p/>      
      Tags: <br/>
      <input name="tags" type="text" size="25" /><p/>
      <input name="submit" type="submit" value="Save" />
    </form>
    <?php
    } else {
      // load classes
      require_once 'Zend/Loader.php';
      Zend_Loader::loadClass('Zend_Gdata');
      Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
      Zend_Loader::loadClass('Zend_Gdata_Photos');
      Zend_Loader::loadClass('Zend_Http_Client');
      
      // connect to service
      $svc = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
      $user = "userid@gmail.com";
      $pass = "secret";
      $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $svc);
      $gphoto = new Zend_Gdata_Photos($client);
      
      // validate input
      if (empty($_POST['title'])) {
        die('ERROR: Missing title');
      } 
      
      // sanitize input
      $title = htmlentities($_POST['title']);
      $tags = htmlentities($_POST['tags']);

      // set album name
      $albumName = "France2008";

      // construct photo object
      // save to server      
      try {        
        $photo = $gphoto->newPhotoEntry();
        
        // set file
        $file = $gphoto->newMediaFileSource($_FILES['photofile']['tmp_name']);
        $file->setContentType("image/jpeg");
        $photo->setMediaSource($file);
        
        // set title
        $photo->setSummary($gphoto->newSummary($title));
        
        // set tags
        $photo->mediaGroup = new Zend_Gdata_Media_Extension_MediaGroup();
        $keywords = new Zend_Gdata_Media_Extension_MediaKeywords();
        $keywords->setText($tags);
        $photo->mediaGroup->keywords = $keywords;
        
        // link to album
        $album = $gphoto->newAlbumQuery();        
        $album->setUser($user);
        $album->setAlbumName($albumName);
        
        // save photo
        $gphoto->insertPhotoEntry($photo, $album->getQueryUrl()); 
      } catch (Zend_Gdata_App_Exception $e) {
        echo "Error: " . $e->getResponse();
      }
      echo 'Photo successfully added!';      
    }
    ?>
  </body>
</html>

Listing 7 actually consists of two parts:

  • A Web form
  • The PHP code that processes the input submitted through the form

Figure 5 illustrates what this form looks like:

Figure 5. A Web form to add a new photo
A Web form to add a new photo

Once the user selects an image file for upload, enters a title and optional tags into this form and submits it, the second half of the script swings into action. First, an HTTP client is initialized and opens an authenticated connection to the Picasa Web Albums Data API. Next, the input entered into the Web form is validated, and a new PhotoEntry() object is initialized, to hold the data for the new photo.

The keywords and title entered into the form are then attached to this object through its mediaGroup property and setSummary() method, respectively. The special file upload array in PHP, $_FILES, obtains the file name of the uploaded file, and the setMediaSource() method attaches this file to the object. Finally, the setAlbumName() method specifies the album to which the photo is linked (set the album value to default to place the image in the default album, or drop box).

Once you configure these various object parameters, the insertPhotoEntry() method actually saves the photo and metadata to the Google servers. After the photo is added, you should immediately see it in the corresponding album.

Figure 6 illustrates the output after successfully adding a new photo:

Figure 6. The result of adding a new photo
The result of adding a new photo

Deleting and modifying photos

To delete a photo, send a DELETE request to the unique URL of the photo. In the Zend library context, you construct the photo URL using the photo and album IDs, obtain a reference to the photo (as an object) using the getPhotoEntry() method, and then call the delete() method of that object. Listing 8 illustrates the process:

Listing 8: Deleting a photo
<?php
// load classes
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Photos');
Zend_Loader::loadClass('Zend_Http_Client');

// connect to service
$svc = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$user = "userid@gmail.com";
$pass = "secret";
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $svc);
$gphoto = new Zend_Gdata_Photos($client);

// get photo entry
// delete entry
try {
  $photo = $gphoto->getPhotoEntry( 'http://picasaweb.google.com/data/entry/api/
  user/xxxxx/albumid/yyyyy/photoid/zzzzz');
  $photo->delete();
} catch (Zend_Gdata_App_Exception $e) {
  echo "Error: " . $e->getResponse();
}        
echo 'Photo successfully deleted!';  
?>

To edit a photo, use the getPhotoEntry() method again. This time, set new values for the various object properties and then call the save() method of the object to save the changes back to the server. Listing 9 illustrates the process:

Listing 9: Updating a photo
<?php
// load classes
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Photos');
Zend_Loader::loadClass('Zend_Http_Client');

// connect to service
$svc = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$user = "userid@gmail.com";
$pass = "secret";
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $svc);
$gphoto = new Zend_Gdata_Photos($client);

// get photo entry
// delete entry
try {
  $photo = $gphoto->getPhotoEntry('http://picasaweb.google.com/
  data/entry/api/user/xxxxx/albumid/yyyyy/photoid/zzzzz');

  // set new title
  $photo->setSummary($gphoto->newSummary('New title'));
  
  // set new tags
  $photo->mediaGroup = new Zend_Gdata_Media_Extension_MediaGroup();
  $keywords = new Zend_Gdata_Media_Extension_MediaKeywords();
  $keywords->setText('new,tags');
  $photo->mediaGroup->keywords = $keywords;
  
  $photo->save();
} catch (Zend_Gdata_App_Exception $e) {
  echo "Error: " . $e->getResponse();
}        
echo 'Photo successfully modified!';  
?>

Filtering feed results

The Picasa Web Albums Data API also lets you customize feeds. You can either restrict the number of entries in the feed, or retrieve only those entries that match search criteria. For example, as Listing 10 illustrates, if you add the 'max-results' parameter to the feed URL, you can limit the number of entries returned in the feed response:

Listing 10: Restricting feed results
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Restricting feed results</title>
    <style>
    body {
      font-family: Verdana;      
    }
    h2 {
      color: red; 
      text-decoration: none;  
    }
    span.attr {
      font-weight: bolder;  
    }
    img {
      float: left;  
    }
    </style>    
  </head>
  <body>
    <?php
    $userid = 'userid%40googlemail.com';
    $album = 'France2008';
    
    // build feed URL
    $feedURL = "http://picasaweb.google.com/data/feed/api/
    user/$userid/album/$album?max-results=3";
    
    // read feed into SimpleXML object
    $sxml = simplexml_load_file($feedURL);
    
    // get album name and number of photos
    $counts = $sxml->children('http://a9.com/-/spec/opensearchrss/1.0/');
    $total = $counts->totalResults; 
    ?>
    <h1><?php echo $sxml->title; ?></h1>
    <?php echo $total; ?> photo(s) found.
    <p/>

    <?php    
    // iterate over entries in album
    // print each entry's title, size, dimensions, tags, and thumbnail image
    foreach ($sxml->entry as $entry) {
      $title = $entry->title;
      $summary = $entry->summary;
      
      $gphoto = $entry->children('http://schemas.google.com/photos/2007');
      $size = $gphoto->size;
      $height = $gphoto->height;
      $width = $gphoto->width;
      
      $media = $entry->children('http://search.yahoo.com/mrss/');
      $thumbnail = $media->group->thumbnail[1];
      $tags = $media->group->keywords;
      
      echo "<h2>$summary</h2>\n";
      echo "<table><tr><td><img src=\"" . 
      $thumbnail->attributes()->{'url'} . "\"/></td>\n";
      echo "<td><span class=\"attr\">File</span>: $title 
      <br />\n";
      echo "<span class=\"attr\">Size</span>: $size 
      ($height x $width) <br />\n";
      echo "<span class=\"attr\">Tags</span>: $tags 
      </td></tr></table>\n";
    }
    ?>
    
  </body>
</html>

Alternatively, you can retrieve only those entries that match a particular query string, when you use the q parameter. Picasa will search the title, caption, and tags of each photo for matches to the query string. Listing 11 illustrates by displaying all photos with the tag 'church':

Listing 11: Searching for photos by keyword
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Searching photos by tag</title>
    <style>
    body {
      font-family: Verdana;      
    }
    h2 {
      color: red; 
      text-decoration: none;  
    }
    span.attr {
      font-weight: bolder;  
    }
    img {
      float: left;  
    }
    </style>    
  </head>
  <body>
    <?php
    $userid = 'userid%40googlemail.com';
    $album = 'France2008';
    
    // build feed URL
    $feedURL = "http://picasaweb.google.com/data/feed/api/user/
    $userid/album/$album?tag=church";
    
    // read feed into SimpleXML object
    $sxml = simplexml_load_file($feedURL);
    
    // get album name and number of photos
    $counts = $sxml->children('http://a9.com/-/spec/opensearchrss/1.0/');
    $total = $counts->totalResults; 
    ?>
    <h1><?php echo $sxml->title; ?></h1>
    <?php echo $total; ?> photo(s) found.
    <p/>

    <?php    
    // iterate over entries in album
    // print each entry's title, size, dimensions, tags and thumbnail image
    foreach ($sxml->entry as $entry) {
      $title = $entry->title;
      $summary = $entry->summary;
      
      $gphoto = $entry->children('http://schemas.google.com/photos/2007');
      $size = $gphoto->size;
      $height = $gphoto->height;
      $width = $gphoto->width;
      
      $media = $entry->children('http://search.yahoo.com/mrss/');
      $thumbnail = $media->group->thumbnail[1];
      $tags = $media->group->keywords;
      
      echo "<h2>$summary</h2>\n";
      echo "<table><tr><td><img src=\"" . 
      $thumbnail->attributes()->{'url'} . "\"/></td>\n";
      echo "<td><span class=\"attr\">File</span>: $title 
      <br />\n";
      echo "<span class=\"attr\">Size</span>: $size 
      ($height x $width) <br />\n";
      echo "<span class=\"attr\">Tags</span>: $tags 
      </td></tr></table>\n";
    }
    ?>
    
  </body>
</html>

Figure 7 illustrates the likely output:

Figure 7. Searching for photos by keyword
Searching for photos by keyword

Integrating Picasa actions

You're ready to put all this information to practical use, and build a simple PHP-based application that allows you to view, delete, add, and search for photos in a given album, piggy-backing on the features offered by the Picasa Web Albums Data API. This prototype application has 3 scripts (see in Download):

  • 'list.php'—Lists photos in an album, and provides links to search, add, and delete photos
  • 'add.php'—Displays a form to add a new photo and processes the submission
  • 'delete.php'—Deletes a specified photo

Begin with 'list.php' (Listing 12), which updates Listing 4 so that each photo is now accompanied by a delete link. The page also contains an additional search form:

Listing 12: Listing photos
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Listing album contents</title>
    <style>
    body {
      font-family: Verdana;      
    }
    h2 {
      color: red; 
      text-decoration: none;  
    }
    span.attr {
      font-weight: bolder;  
    }
    img {
      float: left;  
    }
    </style>    
  </head>
  <body>
    <?php
    // load library
    require_once 'Zend/Loader.php';
    Zend_Loader::loadClass('Zend_Gdata');
    Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
    Zend_Loader::loadClass('Zend_Gdata_Photos');
    Zend_Loader::loadClass('Zend_Http_Client');
    
    // create authenticated HTTP client for Picasa service
    $svc = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
    $user = "userid@gmail.com";
    $pass = "secret";
    $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $svc);
    $gphoto = new Zend_Gdata_Photos($client);
    
    // generate query to get album feed
    $query = $gphoto->newAlbumQuery();    
    $query->setUser("default");
    $query->setAlbumName("France2008");
    
    // filter by search term
    if(isset($_GET['q'])) {
      $query->setQuery($_GET['q']);      
    }
    
    // get and parse album feed
    // get album name and number of photos
    try {
      $feed = $gphoto->getAlbumFeed($query);
    } catch (Zend_Gdata_App_Exception $e) {
      echo "Error: " . $e->getResponse();
    }
    ?>
    <h1><?php echo $feed->getTitle(); ?></h1>
    <?php echo $feed->getTotalResults(); ?> photo(s) found.
    <p/>

    <?php        
    // process each photo entry
    // print each entry's title, size, dimensions, tags, and thumbnail image
    foreach ($feed as $entry) {
      $title = $entry->getTitle();
      $summary = $entry->getSummary();
      $thumbnail = $entry->getMediaGroup()->getThumbnail();
      $tags = $entry->getMediaGroup()->getKeywords();
      $size = $entry->getGphotoSize();
      $height = $entry->getGphotoHeight();
      $width = $entry->getGphotoWidth();
      $photoid = $entry->getGphotoId();
      $albumid = $entry->getGphotoAlbumId();
      
      echo "<h2>$summary</h2>\n";
      echo "<table><tr><td><img src=\"" . 
      $thumbnail[1]->url . "\"/></td>\n";
      echo "<td><span class=\"attr\">File</span>: $title 
      <br />\n";
      echo "<span class=\"attr\">Size</span>: $size bytes 
      ($height x $width) <br />\n";
      echo "<span class=\"attr\">Tags</span>: $tags 
      <br />\n";
      echo "<a href=\"delete.php?aid=$albumid&pid=$photoid\">
       delete photo</a></td></tr></table>\n";
    }
    ?>
    
    <a href="add.php">Add a new photo</a>
    <p/>
    
    <form action="<?php echo $_SERVER['PHP_SELF']; ?>" method="get">
      Search for photos containing:<br/>
      <input type="text" name="q" size="10"/><p/>
      <input type="submit" name="submit" value="Search"/>
    </form>

  </body>
</html>

Figure 8 illustrates what you might see after you run this script:

Figure 8. A Web page listing photos in an album
A Web page listing photos in an album

Notice from Listing 12 that the search form, on submission, simply re-invokes list.php and attaches the user's query string to the requested feed, through the setQuery() method of the query object.

The add.php script adds new photos, as illustrated in Listing 13:

Listing 13: Adding photos
<!DOCTYPE html 
  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>Adding photos to an album</title>
    <style>
    body {
      font-family: Verdana;      
    }
    li {
      border-bottom: solid black 1px;      
      margin: 10px; 
      padding: 2px; 
      width: auto;
      padding-bottom: 20px;
    }
    h2 {
      color: red; 
      text-decoration: none;  
    }
    span.attr {
      font-weight: bolder;  
    }
    </style>    
  </head>
  <body>
    <h1>Add Photo</h1>
    <?php if (!isset($_POST['submit'])) { ?>
    <form method="post" action="<?php 
    echo htmlentities($_SERVER['PHP_SELF']); ?>" enctype="multipart/form-data">
      Title: <br/>
      <input name="title" type="text" size="25" /><p/>
      File to upload: <br/>
      <input name="photofile" type="file" /><p/>      
      Tags: <br/>
      <input name="tags" type="text" size="25" /><p/>
      <input name="submit" type="submit" value="Save" />
    </form>
    <?php
    } else {
      // load classes
      require_once 'Zend/Loader.php';
      Zend_Loader::loadClass('Zend_Gdata');
      Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
      Zend_Loader::loadClass('Zend_Gdata_Photos');
      Zend_Loader::loadClass('Zend_Http_Client');
      
      // connect to service
      $svc = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
      $user = "userid@gmail.com";
      $pass = "secret";
      $client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $svc);
      $gphoto = new Zend_Gdata_Photos($client);
      
      // validate input
      if (empty($_POST['title'])) {
        die('ERROR: Missing title');
      } 
      
      // sanitize input
      $title = htmlentities($_POST['title']);
      $tags = htmlentities($_POST['tags']);

      // set album name
      $albumName = "France2008";

      // construct photo object
      // save to server      
      try {        
        $photo = $gphoto->newPhotoEntry();
        
        // set file
        $file = $gphoto->newMediaFileSource($_FILES['photofile']['tmp_name']);
        $file->setContentType("image/jpeg");
        $photo->setMediaSource($file);
        
        // set title
        $photo->setSummary($gphoto->newSummary($title));
        
        // set tags
        $photo->mediaGroup = new Zend_Gdata_Media_Extension_MediaGroup();
        $keywords = new Zend_Gdata_Media_Extension_MediaKeywords();
        $keywords->setText($tags);
        $photo->mediaGroup->keywords = $keywords;
        
        // link to album
        $album = $gphoto->newAlbumQuery();        
        $album->setUser($user);
        $album->setAlbumName($albumName);
        
        // save photo
        $gphoto->insertPhotoEntry($photo, $album->getQueryUrl()); 
      } catch (Zend_Gdata_App_Exception $e) {
        echo "Error: " . $e->getResponse();
      }
      echo 'Photo successfully added!';      
    }
    ?>
  </body>
</html>

Figure 9 illustrates this form:

Figure 9. A Web form to add new photos
A Web form to add new photos

You delete photos with the delete.php script, which accepts the unique album and photo IDs from list.php using the GET method. The delete.php script uses this information to locate and delete the photo entry. Listing 14 shows the code:

Listing 14: Deleting photos
<?php
// check for album and photo ID 
if (empty($_GET['aid']) || empty($_GET['pid'])) {
  die('ERROR: Missing input parameters');
} else{
  $aid = htmlentities($_GET['aid']);  
  $pid = htmlentities($_GET['pid']);  
}

// load classes
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Photos');
Zend_Loader::loadClass('Zend_Http_Client');

// connect to service
$svc = Zend_Gdata_Photos::AUTH_SERVICE_NAME;
$user = "userid@gmail.com";
$pass = "secret";
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $svc);
$gphoto = new Zend_Gdata_Photos($client);

// get photo entry
// delete entry
try {
  $photo = $gphoto->getPhotoEntry('http://picasaweb.google.com/data/
  entry/api/user/userid/albumid/' . $aid . '/photoid/' . $pid);
  $photo->delete();
} catch (Zend_Gdata_App_Exception $e) {
  echo "Error: " . $e->getResponse();
}        
echo 'Photo successfully deleted!';  
?>

Summary

Over the last few pages, you walked through a crash course in how to integrate data from the Picasa Web Albums service into a PHP application using both SimpleXML and the Zend client library. The examples in this article introduced you to the Picasa feed format showed you how to:

  • Retrieve album and photo listings
  • Filter these listings by search terms; add, modify and delete photos; and build a customized interface to the Picasa service
  • Add, modify and delete photos
  • Build a customized interface to the Picasa service

As these examples illustrate, the Picasa Web Albums Data API is a powerful and flexible tool for developers who want to build creative new applications around photo management and storage. Play with it sometime, and see what you think!


Download

DescriptionNameSize
Sample code for articlex-picasalbum-example.zip4KB

Resources

Learn

Get products and technologies

  • the Zend GData Client Library: Download the Zend_Gdata PHP 5 client interfaces for the Google Data APIs.
  • IBM trial software for product evaluation: Build your next project with trial software available for download directly from developerWorks, including application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into XML on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=XML, Open source, Web development
ArticleID=336685
ArticleTitle=Develop PHP applications with Picasa Web Albums
publish-date=09162008