Google OAuth and Contacts API - PHP Curl Examples

This code uses Zend Framework’s Zend_Json::decode method, however you can use json_decode if it is available on your version of PHP.

Create the authorization link
When this link is clicked, the site visitor is presented with a page from Google asking if they would like to allow your application access to their data. If they accept, Google will give them a code, which can then be used on the next request. This is run as an installed application, because there are times when data will be requested without user interaction.

$sAuthURL=AUTH_URL;
$aParms=array('response_type'=>RESPONSE_TYPE,
        'client_id'=>CLIENT_ID,
        'redirect_uri'=>REDIRECT_URI,
        'scope'=>SCOPE);
$sLink=$sAuthURL.'?'.http_build_query($aParms);

Request an access and refresh token

        require_once 'Zend/Json.php';
        $sTokenURL=TOKEN_URL;
        $aParms=array(
                'code'=>$_POST['code'],
                'client_id'=>CLIENT_ID,
                'client_secret'=>CLIENT_SECRET,
                'redirect_uri'=>REDIRECT_URI,
                'grant_type'=>AUTHORIZATION_CODE);
        $cURL=curl_init();
        curl_setopt($cURL,CURLOPT_URL,$sTokenURL);
        curl_setopt($cURL,CURLOPT_SSL_VERIFYPEER,TRUE);
        curl_setopt($cURL,CURLOPT_POST, TRUE);
        curl_setopt($cURL,CURLOPT_RETURNTRANSFER, TRUE);
        curl_setopt($cURL,CURLOPT_POSTFIELDS,$aParms);
        $cResponse=Zend_Json::decode(trim(curl_exec($cURL)));
        curl_close($cURL);                
        $sAccessToken=$cResponse['access_token'];
        $sRefreshToken=$cResponse['refresh_token'];

Use the refresh_token to request a new access_token

                                $sTokenURL=TOKEN_URL;
                                $aParms=array(
                                        'refresh_token'=>$sRefreshToken,
                                        'client_id'=>CLIENT_ID,
                                        'client_secret'=>CLIENT_SECRET,
                                        'grant_type'=>REFRESH_TOKEN);
                                $cURL=curl_init();
                                curl_setopt($cURL,CURLOPT_URL,$sTokenURL);
                                curl_setopt($cURL,CURLOPT_SSL_VERIFYPEER,TRUE);
                                curl_setopt($cURL,CURLOPT_POST, TRUE);
                                curl_setopt($cURL,CURLOPT_RETURNTRANSFER, TRUE);
                                curl_setopt($cURL,CURLOPT_POSTFIELDS,$aParms);
                                require_once 'Zend/Json.php';
                                $cResponse=Zend_Json::decode(trim(curl_exec($cURL)));
                                $sError=curl_error($cURL);
                                curl_close($cURL);
                                $sAccessToken=$cResponse['access_token'];



Request the contact data


                $sContactsURL=CONTACTS_URL.'?access_token='.$sAccessToken;
                $cURL=curl_init();
                curl_setopt($cURL,CURLOPT_URL,$sContactsURL);
                curl_setopt($cURL,CURLOPT_RETURNTRANSFER, TRUE);
                $cResponse=trim(curl_exec($cURL));
                $sError=curl_error($cURL);
                curl_close($cURL);

Parse the data from Google
This code also Zend_Gdata to handle the parsing.

$phone_only=false;

include 'Zend/Gdata.php';

$gdata=new Zend_Gdata();
$feed=$gdata->importString($cResponse);

foreach ($feed as $entry) {
        // Thanks to: http://www.ibm.com/developerworks/opensource/library/x-phpgooglecontact/index.html
        $xml = simplexml_load_string($entry->getXML());
        $obj = new stdClass;
        $obj->id = $entry->id->text;
        if (false===($iSlash=strpos($entry->title->text,'/')))
                $obj->name=$entry->title->text;
        else
                $obj->name = substr($entry->title->text,0,$iSlash);
        if (empty($obj->name)) continue;
        $name = explode(' ',$obj->name,3);
        $obj->first_name = $obj->middle_name = $obj->last_name = '';
        switch (count($name))
        {
                case 1: 
                        $obj->first_name = $obj->name;
                        break;
                case 2: 
                        $obj->first_name = $name[0];
                        $obj->last_name = $name[1];
                        break;
                case 3:
                        $obj->first_name = $name[0];
                        $obj->middle_name = $name[1];
                        $obj->last_name = $name[2];
                        break;
        }
        $obj->phoneNumber = array();
        $obj->phone = array();
        foreach ($xml->phoneNumber as $p) 
        {
                $type = preg_replace('/^[^#]+#(.*)$/','\1',(string) $p['rel']);
                $obj->phoneNumber[] = (string) $p.' ('.$type.')';
                $obj->phone[$type] = array ('phone' => preg_replace('/\D/','',(string) $p), 'type' => strtolower($type) );
        }
        if (empty($obj->phone) && $phone_only) continue;
        $results[$obj->last_name] = $obj;  
}
ksort($results);



This post courtesy of Lyrix, Inc. http://mobiso.com

Apache IE8 HTML entities filter

One of the pages in a web application displays text log file output in popup browser windows.

If that output includes this statement:

<?xml version="1.0" encoding="utf-8"?>

IE8 will try to parse the content as XML, and it will show an error:

The XML page cannot be displayed
Cannot view XML input using style sheet.
Please correct the error and then click the Refresh button, or try again later.
Invalid at the top level of the document. Error processing resource:

I didn’t want to add any scripting to the pages, since they’re text, and I didn’t want to make any coding changes. One solution is to use an Apache output filter to convert the text into HTML entities, and force the document type to text/html.

ExtFilterDefine htmlentities mode=output cmd="/usr/bin/php -R 'echo htmlentities(fgets(STDIN));'"

<FilesMatch "\.txt$">
  ForceType text/html
  SetOutputFilter htmlentities
</FilesMatch>

This is definitely a quick solution that may not be ideal for every situation, or could be refined.

The documents aren’t HTML, they are text. They don’t have any tags in them, and those that are there should not be treated as tags, but as text. Forcing the type to text/plain didn’t work.

Regardless, this is one way you can convert characters into HTML entities without modifying your code.

Different solutions:

  • Extend the filter to add the HTML tags necessary for a true text/html document
  • Modify the code to convert the document to HTML
  • Install recode (see link above)
  • Do something entirely different

Image - Round corners, add a credit

A script that uses ImageMagick to round the corners of an image, apply a credit, and optionally resize it. This script leaves the interim images, you could also delete them. The image credit is also written to a text file.

Original image

Credited Image

#!/bin/bash
if [ "$#" -lt 2 ]; then
        echo "usage: $0 <image file> <credit> [<resize>]"
else
ORIGINAL_IMAGE=$1
BASE_FILENAME=`basename $1 .jpg`
echo "$2" > $BASE_FILENAME.credit
convert $BASE_FILENAME.jpg \( +clone -alpha extract -draw 'fill black polygon 0,0 0,5 5,0 fill white circle 5,5, 5,0' \( +clone -flip \) \
    -compose Multiply -composite \( +clone -flop \) -compose Multiply -composite \) -alpha off  \
    -compose CopyOpacity -composite $BASE_FILENAME.rounded.png
convert -background white $BASE_FILENAME.rounded.png  +flatten $BASE_FILENAME.rounded.jpg
convert -background '#00000080' -pointsize 12 -fill white label:"$2" miff:- | \
    composite -gravity south -geometry +0+3 - \
    $BASE_FILENAME.rounded.jpg $BASE_FILENAME.credited.jpg
if [ "$#" -gt 2 ]; then
    mogrify -resize $3 $BASE_FILENAME.credited.jpg
fi
fi

Tested with:

convert -version
Version: ImageMagick 6.5.4-2 2009-07-08 Q16 OpenMP http://www.imagemagick.org
Copyright: Copyright © 1999-2009 ImageMagick Studio LLC

CentOS Firefox 10 HTML5 Audio

These tags allow you to specify that audio be played through Windows Media Player Plugin if it is available, and if it isn’t, use an HTML5 audio tag.


<object id="wmp_p" data="audio.wav" type="application/x-ms-wmp" width="175" height="75" >
      <param name="autostart" value="true" />
      <param name="volume" value="10" />
      <param name="uiMode" value="mini" />
	<audio autoplay="autoplay" controls="controls" style="height:75px;width:175px">
		<source src="audio.wav" type="audio/wav" />
		No audio player available, download <a href="audio.wav" title="Download audio">audio.wav</a>
	</audio>
    </object>

This was tested under CentOS 5.4, with Firefox 10.0.2, and it worked nicely. It may also work well with Chrome under Linux, as well as under Windows.

Notes

  • Different browser support different audio formats with the tag.
  • There may be subtle coding differences between the tags and attributes. Test thoroughly and carefully.
  • There may be layout issues across different browsers, again, test thoroughly and carefully.

Audio encoding that works well under CentOS/Firefox 10.0.2

sox -V input.wav -u -b -r 8000 -c 1 output.wav

sox: Writing Wave file: Microsoft PCM format, 1 channel, 8000 samp/sec
sox: 8000 byte/sec, 1 block align, 8 bits/samp

dojox.grid.DataGrid - Search for item

This is one way to search a dojox DataGrid for an item.

In this case, the id is used, but other attributes can be searched as well.

function already_in_the_grid(id)
{
         return dojo.some(member_store._getItemsArray(),(function(item){if (item.id==member_id) return true}));
}