Showing posts with label php. Show all posts
Showing posts with label php. Show all posts

Download a Google Doc using the PHP library


At the time of writing this tip, the Zend_Gdata_Docs component of the PHP library does not contain the export/download functionality of the DocList API. Here is an example of using AuthSub and file_get_contents() to download a document as a .txt file:

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>";

Geo-Tagging a Picasa Photo in PHP


Assuming 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.)
$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);

Constructing unsupported XML elements using the PHP client library


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.

The PHP client library accomplishes this using extension elements, represented as instances of Zend_Gdata_App_Extension_Element. 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 extensionElements array of the class representing the parent element. In order to send arbitrary XML, you can also construct a new Zend_Gdata_App_Extension_Element and place it in the extensionElements array.

As an example, Google Base added a publishing priority option, specified by adding a <gm:publishing_priority> 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.

<?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;

Of course, if you discover missing elements, please feel free to file an issue in the Zend Framework issue tracker 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 Zend Framework wiki.

2 Legged OAuth in PHP


Google Apps Premier/Education administrators can take advantage of 2 legged OAuth to communicate with the Google Data APIs. This sample makes use of the PHP OAuth library from oauth.net.
<?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);
}
?>

XML PHP Pretty Printer


<?
/** 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>';
?>

Force PHP to use a Google Account in ClientLogin


Sometimes 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:
$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);

Changing the default timeout for HTTP requests in PHP


If you are using the PHP client library to upload videos to YouTube, you may run into problems where your requests are timing out after ten seconds. The client library makes use of the Zend_Http_Client 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:
// 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);

Adding Multiple Video Responses to a Video Entry


Note that you need to use an authenticated YouTube client object (represented as $ytClient). You can only add responses that are your own videos and they may not show up immediately, depending on whether the owner of the video that you are responding to has enabled automatic acceptance of responses.
// Assuming we have just instantiated a working $httpClient object.
$ytClient = new Zend_Gdata_YouTube($httpClient, $applicationId, $clientId, $developerKey);

// The video ID that is to receive the responses.
$receiveResponses = 'ABC123';

// A few of your videos that you want to post as responses.
$videoResponses[] = 'defXYZ123';
$videoResponses[] = 'defXYZ124';
$videoResponses[] = 'defXYZ125';

function addVideoResponses($videoResponses, $receiveResponses) {
  global $ytClient;

  $receiveResponsesEntry = $yt->getVideoEntry($receiveResponses);

  foreach ($videoResponses as $videoResponse) {
    $videoResponseEntry = $yt->getVideoEntry($videoResponse);
    $responsesFeedUrl = $receiveResponsesEntry->getVideoResponsesLink()->getHref();

    try {
      $ytClient->insertEntry($videoResponseEntry, $responsesFeedUrl);
    } catch (Zend_Gdata_App_HttpException $httpException) {  
      echo 'ERROR: ' . $httpException->getRawResponseBody();
      
    }
  }
}

addVideoResponses($videoResponses, $receiveResponses);

Using the PHP Client Library through a Proxy connection


To access a Google Data API through a proxy connection you will need to use the Zend_Http_Client_Adapter_Proxy proxy adapter. In the snippet below, we are going to access our private Google Documents feed from the DocumentsList API through a proxy connection:
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata_App_HttpException');
Zend_Loader::loadClass('Zend_Gdata_ClientLogin');
Zend_Loader::loadClass('Zend_Gdata_Docs');

Zend_Loader::loadClass('Zend_Http_Client_Exception');
Zend_Loader::loadClass('Zend_Http_Client');
Zend_Loader::loadClass('Zend_Http_Client_Adapter_Proxy');


// Configure the proxy connection
$config = array(
    'adapter'    => 'Zend_Http_Client_Adapter_Proxy',
    'proxy_host' => 'your.proxy.server.net',
    'proxy_port' => 3128
);

// We are setting http://www.google.com:443 as the initial URL since we need to perform
// ClientLogin authentication first.
$proxiedHttpClient = new Zend_Http_Client('http://www.google.com:443', $config);

$username = 'foo@example.com';
$password = 'barbaz';
$service = Zend_Gdata_Docs::AUTH_SERVICE_NAME;

// Try to perform the ClientLogin authentication using our proxy client.
// If there is an error, we exit since it doesn't make sense to go on. You may want to 
// modify this according to the needs of your application.
try {
  $httpClient = Zend_Gdata_ClientLogin::getHttpClient($username, $password, $service,
    $proxiedHttpClient);
} catch (Zend_Gdata_App_HttpException $httpException) {
  exit("An error occurred trying to connect to the proxy server\n" .        
    $httpException->getMessage() . "\n");
}

// If that worked, proceed and retrieve the documents feed.
// Remember to set your application ID.
$docsClient = new Zend_Gdata_Docs($httpClient, $yourApplicationId);
$feed = $docsClient->getDocumentListFeed();

?>

Secure AuthSub using the Zend PHP library 1.6+


After uploading a public certificate to https://www.google.com/accounts/ManageDomains, here's how to use the Zend PHP 1.6+ library to work with secure AuthSub. This example uses the Google Health Data API
<?
function setupClient($singleUseToken = null) { 
  $client = null;  

  // Fetch a new AuthSub token?
  if (!$singleUseToken) {
    $next = 'http://' . $_SERVER['HTTP_HOST'] . $_SERVER['PHP_SELF'];
    $scope = 'https://www.google.com/health/feeds';
    $authSubHandler = 'https://www.google.com/health/authsub';    
    $secure = 1;
    $session = 1;
    $permission = 1;  // 1 - allows posting notices && allows reading profile data
    $authSubURL =  Zend_Gdata_AuthSub::getAuthSubTokenUri($next, $scope, $secure, $session, $authSubHandler);
    
    $authSubURL .= '&permission=' . $permission;
    
    echo '<a href="' . $authSubURL . '">Link your Google Health Account</a>';
  } else {
    $client = new Zend_Gdata_HttpClient();
    
    // This sets your private key to be used to sign subsequent requests
    $client->setAuthSubPrivateKeyFile('/path/to/myrsakey.pem', null, true);

    $sessionToken = Zend_Gdata_AuthSub::getAuthSubSessionToken(trim($singleUseToken), $client);
    // Set the long-lived session token for subsequent requests
    $client->setAuthSubToken($sessionToken);
  }
  return $client;
}
?>
Use this function like this:
$client = setupClient(@$_GET['token']);
if ($client) {
  // Query a feed
} else {
  exit(); // Just display the AuthSub link
}

Update:Read the new documentation on using AuthSub for PHP as well as all the other client libraries.

Finding the previous and next links using SimpleXml


If you find yourself in a situation where you need to page through a large feed of entries with SimpleXML, you can use the below snippet to retrieve the 'previous' and 'next' links:
// assuming you have read your XML string into the $sxml object
$links = $sxml->children('http://www.w3.org/2005/Atom'); 
foreach($links as $link) {
  foreach($link->attributes() as $key => $value) {
    if ($key == 'rel') {
      print "$key => $value\n";
    }
  }
}

Uploading a video to YouTube using an older version of the Zend Framework, prior to 1.5.3


We have recently added convenience methods that make uploading video entries to the YouTube API much easier than before. For those that are still using older versions of the PHP Client Library, prior to 1.5.3 of the Zend Framework, the snippet below explains how to upload a video entry. Note that this still works in version 1.5.3 and will continue to work in future revisions:
// assuming that $yt is a fully authenticated YouTube service object

// create a new Zend_Gdata_YouTube_VideoEntry object
$myVideoEntry = new Zend_Gdata_YouTube_VideoEntry();

// create a new Zend_Gdata_App_MediaFileSource object for your video file (if using direct upload)
$filesource = $yt->newMediaFileSource('/path/to/mytestmovie.mov');
$filesource->setContentType('video/quicktime');

// set slug header
$filesource->setSlug('mytestmovie.mov');

// add the filesource to the video entry
$myVideoEntry->setMediaSource($filesource);

// create a new Zend_Gdata_YouTube_Extension_MediaGroup object
$mediaGroup = $yt->newMediaGroup();
$mediaGroup->title = $yt->newMediaTitle()->setText('My Test Movie');
$mediaGroup->description = $yt->newMediaDescription()->setText('My description');

// the category must be a valid YouTube category
// optionally set some developer tags*
$mediaGroup->category = array(
$yt->newMediaCategory()->setText('Autos')->setScheme('http://gdata.youtube.com/schemas/2007/categories.cat'),
$yt->newMediaCategory()->setText('mydevelopertag')->setScheme('http://gdata.youtube.com/schemas/2007/developertags.cat'),
$yt->newMediaCategory()->setText('anotherdevelopertag')->setScheme('http://gdata.youtube.com/schemas/2007/developertags.cat')
);

// set keywords, please note that they cannot contain white-space
$mediaGroup->keywords = $yt->newMediaKeywords()->setText('cars, funny');
$myVideoEntry->mediaGroup = $mediaGroup;

// optionally set video location
$yt->registerPackage('Zend_Gdata_Geo');
$yt->registerPackage('Zend_Gdata_Geo_Extension');
$where = $yt->newGeoRssWhere();
$position = $yt->newGmlPos('37.0 -122.0');
$where->point = $yt->newGmlPoint($position);
$myVideoEntry->setWhere($where);


// upload URL for the currently authenticated user
$uploadUrl = 'http://uploads.gdata.youtube.com/feeds/users/default/uploads';

try {
$newEntry = $yt->insertEntry($myVideoEntry, $uploadUrl, 'Zend_Gdata_YouTube_VideoEntry');
} catch (Zend_Gdata_App_HttpException $httpException) {
echo $httpException->getRawResponseBody();
} catch (Zend_Gdata_App_Exception $e) {
echo $e->getMessage();
}
* Find out more about developer tags

Create a public album for Picasa Web Albums


This is how to do it in .NET:
AlbumEntry newEntry = new AlbumEntry();
newEntry.Title.Text = "My New album";
newEntry.Summary.Text = "This is an album";
AlbumAccessor ac = new AlbumAccessor(newEntry);
ac.Access = "public";
This is how to do it in PHP:
$entry = new Zend_Gdata_Photos_AlbumEntry();
$entry->setTitle($gp->newTitle('title'));
$entry->setGphotoAccess($gp->newAccess("public"));
$createdEntry = $gp->insertAlbumEntry($entry);

Link a Google Base item to your website


Here is how you can set the alternate link using the PHP client library. When someone clicks on your item in Google Base, he/she will be redirected to your site.
<?
$link = new Zend_Gdata_App_Extension_Link(); 
$link->setHref('http://www.example.com'); 
$link->setRel('alternate'); 
$link->setType('text/html');  
$linkArray[] = $link; 

$entry->setLink($linkArray); 
?>
Here is the underlying XML:
<link rel='alternate' type='text/html' href='http://www.example.com'/>

Catching Exceptions from the PHP Client Library


If you are using the PHP client library and see an error like "Fatal error: Uncaught exception 'Zend_Gdata_App_HttpException' with message 'Expected response code 200, got 400' in ..." What you should really do is surround your code in a try block and use a catch statement like this:
catch(Zend_Gdata_App_HttpException $exception) {
    echo "Error: " . $exception->getResponse()->getRawBody();
}
Usually this will print a more informative message describing the reason for failure.

Printing out Video thumbnails for a user's upload feed


A quick helper function to print thumbnails for all entries in a user's upload feed.
<php
require_once 'Zend/Loader.php';
Zend_Loader::loadClass('Zend_Gdata_YouTube');

function printThumbnails($username)
{
  $yt = new Zend_Gdata_YouTube();
  $userVideoFeed = $yt->getUserUploads($username);

  foreach($userVideoFeed as $videoEntry) {
    print "

Thumbnails for entry $videoEntry->title

"; $videoThumbnails = $videoEntry->getVideoThumbnails(); foreach($videoThumbnails as $videoThumbnail) { echo $videoThumbnail['time'] . ' -- ' . '<img src="' . $videoThumbnail['url']. '" height=' . $videoThumbnail['height'] . ' width=' . $videoThumbnail['width'] . ' />'; } } } ?>

Deleting Video Entrys based on Video ID and Developer Key


A quick helper function explaining how to delete Video Entry's from YouTube based on a Developer Tag and based on an array of Video ID's to delete. Please note that this example assumes the use of the PHP Client Library.
/**
 * Delete a video matching a developer tag and a video id.
 *
 * @param $youTubeService Zend_Gdata_YouTube An authenticated YouTube service object.
 * @param $developerTag string A developer tag for which to retrieve videos.
 * @param $videoIdsToDelete array An array of video ids to be deleted.
 * @return void
 */
function deleteVideosByDeveloperTag($youTubeService, $developerTag, $videoIdsToDelete)
{
 $devTagUrl = 'http://gdata.youtube.com/feeds/videos/-/%7B' .
   'http%3A%2F%2Fgdata.youtube.com%2Fschemas%2F2007%2F' .
   'developertags.cat%7D' . $developerTag;

  $devTagFeed = $youTubeService->getVideoFeed($devTagUrl);

  foreach($developerTagFeed as $videoEntry) {
    if ($entry instanceof Zend_Gdata_YouTube_VideoEntry) {
      $videoIdsToBeDeleted[] = $videoEntry->getVideoId();
    }
  }

  // Then use a read-write feed to actually perform the deletion(s)
  $myVideoFeed = $youTubeService->getUserUploads('default');

  foreach($myVideoFeed as $videoEntry) {
    $id = $videoEntry->getVideoId();
     if (in_array($id, $videoIdsToBeDeleted) {
       $youTubeService->delete($videoEntry);
    }
  }
}

Labeling Blogger Posts


When posting to Blogger, it's useful to organize your posts using using labels. When using the normal Blogger interface, this is done by adding a comma-separated list of labels to a text field at the bottom of the post. To achieve the same effect using the Blogger API, add an individual <atom:category> element for each label to your post's entry. Set the scheme to 'http://www.blogger.com/atom/ns#', and set the term to your desired label.

For example, the following code for the Zend_Gdata PHP client library would add the labels "foo" and "bar" to a post (stored in $entry):

$label1 = new Zend_Gdata_App_Extension_Category(
     'foo', 'http://www.blogger.com/atom/ns#');
$label2 = new Zend_Gdata_App_Extension_Category(
     'bar', 'http://www.blogger.com/atom/ns#');
$entry->setCategory(array($label1, $label2));

Secure AuthSub in PHP


A helper for sending a signed HTTP GET request in PHP.

// upgrade a single-use AuthSub token
$response = signedGET('https://www.google.com/accounts/AuthSubSessionToken', $singleUseToken);

// fetch Calendar data
$response = signedGET('http://www.google.com/calendar/feeds/default/allcalendars/full', $sessionToken);

<?php
function signedGET($requestURL, $token) { 
  $privKeyFilePath = "../myrsakey.pem";
  $timestamp = time();
  $nonce = md5(microtime() . mt_rand()); 
  $sigalg = 'rsa-sha1';
  
  // construct the data string
  $data = "GET $requestURL $timestamp $nonce";
  
  // get rsa private key
  $fp = fopen($privKeyFilePath, "r");  
  $priv_key = fread($fp, 8192);
  fclose($fp);                                

  // compute signature
  $privatekeyid = openssl_get_privatekey($priv_key);
  openssl_sign($data, $signature, $privatekeyid, OPENSSL_ALGO_SHA1);
  openssl_free_key($privatekeyid);

  $curl = curl_init($requestURL);
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
  curl_setopt($curl, CURLOPT_FAILONERROR, true);
  curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
  
  // Set Authorization header 
  $sig = base64_encode($signature);
  curl_setopt($curl, CURLOPT_HTTPHEADER, array(
      "Authorization: AuthSub token=\"$token\" data=\"$data\" sig=\"$sig\" sigalg=\"$sigalg\"")
  ); 
  
  $result = curl_exec($curl);
  curl_close($curl);

  return $result;
}
?>

Retrieving private Videos using the PHP Client Library


To retrieve private videos you will need to pass the full URI of the video entry as the $location parameter to the getVideoEntry method. Assuming we have a fully authenticated YouTube service object as $service:
$videoIdOfPrivateVideo = 'ABC123xyz';
$location = 'http://gdata.youtube.com/feeds/api/users/default/uploads/' . $videoIdOfPrivateVideo;
$privateEntry = $youTubeService->getVideoEntry(null, $location);
Update: Version 1.7.1 and higher of the PHP Client Library has a new method "getFullVideoEntry()" that should take care of this.