Saturday, October 30, 2010

A free and customizable full width blogger template

Here my first design of a full width blogger template. The background that shows up on the sides of the template is blue and the content area is white. The layout of the blogspot theme has the articles on the left and the space for widgets on the right side.

Also most of the text and the links are dark blue or black as you can see in the screenshot. The picture in the header doesn't come with the template, but you are able to upload your own picture and display the blog title and a description in the header area as displayed further below.

Terms of Use

Using this blogspot theme is free of charge. Anybody can use or change the template as long as the copyright link stays in the footer of the template.

Download the template

Here is the download link of the full width free customizable blogger template:

How to install the blogger template and backup your old template?

1. Go to blogger.com when you are logged in to your blogger account.
2. In the Dashboard under manage blogs find your according blog (in case you have more than one).
3. Click on "Design"
4. Click on "Edit HTML"
5. Click on "Download Full Template" to backup your current template.
6. Click on "Choose File" to select the full-width-template.xml file on your hard drive.
7. Click on "Upload" to upload the template.
8. In case you have widgets on the blog that are missing in this template a Warning will show up. Click "Keep Widgets" to add the missing widgets.

And you are finished.

Upload a header image and add blog title and description to the heading.

To upload a header image to the template you can repeat steps 1. to 3. above and then click on "Page Elements".

Here you see the layout of the template. Click at the header on "Edit". A window pops up that lets you upload an image. If you have already an image there and you want to changes, click on "Remove Image".

If your image contains all the Text you want in the header area select under Placement "Instead of title and description". If you want to add a title and or description select "Behind title and description". Then make sure you have enter the text you want to show up later in the header area above in the "Title" and "Description" field.

Customize text, link and background colors of the full width blogger template

Repeat step 1. to 4. and scroll down in the "Edit Template" area to the "Variable definitions" section. Here you can change the value parameter of variables like the background color or links. After you have saved the changes you can review them on the blog.

Thursday, October 21, 2010

Configuring Apache2.2 with Tomcat 6.0.x on Linux with mod_proxy_ajp

This article should describe how to use the HTTPD Server Apache 2.2 to handle all request on port 80 and in certain cases (depending on the requested url) pass on the request to the Apache Tomcat 6 Server (6.0.x). So your Apache can handle requests for your static pages and for example php projects and your tomcat handles java web applications in for example .war-files.

How to let Apache 2 forward requests for different URLs to different folders is described in a former article.

These setting were used and tested in Linux Debian 5 Lenny. They should also work in other distributions like RedHad, Suse and Ubuntu, as well as Windows and Mac OS systems.

Make sure mod_proxy_ajp is installed

Look into /etc/apache2/mods-available for proxy.load and proxy_ajp.load to make sure the according mods are available.
Look into /etc/apache2/mods-enabled for proxy.load and proxy_ajp.load to make sure both mods are enabled.

From Apache2.2 this should normally be all set already, if not read here on how to enable mod_proxy_ajp.

Apache2 configuration

As described in a former post I use virtual host to handle requests for different URLs and forward them to certain folders in the /var/www folder. To forward requests to a certain URL to tomcat can also be done by creating a virtual host.

ProxyPass and ProxyPassReverse are classic reverse proxy directives used to forward the stream to another location. ajp://… is the AJP connector location (your tomcat’s server host/port)

The changes have to be done in the sites-enabled/000-default file in the apache2 home folder. Use for example nano or vim to edit the file and at below block.
nano /etc/apache2/sites-enabled/000-default
#Virtual Host setup
<VirtualHost *:80>
   ServerName yourapp.com
   ServerAlias *.yourapp.com
   DocumentRoot "/usr/local/tomcat/webapps/yourapp"
   DirectoryIndex index.html
   <Proxy *>
      AddDefaultCharset Off
      Order deny,allow
      Allow from All
   </Proxy>
   ProxyPass / ajp://localhost:8009/
   ProxyPassReverse / ajp://localhost:8009/
</VirtualHost>

Tomcat configuration

In Tomcat 6 we have to do some changes as well. All changes are done in the server.xml file.
nano /usr/local/tomcat/conf/server.xml
The first line might already be in your file. Please check this and in case only add the missing parameters, like enableLookups="false". This line will enable the AJP connections to the 8009 port of your tomcat 6 server.
<Connector enableLookups="false" port="8009" protocol="AJP/1.3" redirectPort="8443" />
Add this inbetween the tags:

<Host name="yourapp.com" debug="0" appBase="/usr/local/tomcat/webapps"
 unpackWARs="true" autoDeploy="true">
   <Alias>wiki</Alias>
   <Context path= "" docBase="/usr/local/tomcat/webapps/yourapp" debug=”1″/>
   <Valve className=”org.apache.catalina.valves.AccessLogValve”
    directory=”logs” prefix=”home_access_log.” suffix=”.txt”
    pattern=”common” resolveHosts=”false”/>
</Host>

Now if you point your domain(yourdomain.com) to the IP of the server Apache should do the rest and Tomcat should serve you the web app.

Restart Apache and Tomcat

Restart Apache:
/etc/init.d/apache2 restart
Restart Tomcat:
/etc/init.d/tomcat restart

Tuesday, October 19, 2010

Symfony - extract locale from url or user (sf_culture)

If your symfony supports different cultures like en, fr, ru, de, uk etc. you can set in the routing.yml the prefix_path: /:sf_culture/module

To get the value from the URL

In an action you can use:
$locale = $request->getParameter('sf_culture');
If you are not in an action, maybe you want to put this in a helper class or Filter.
$locale = $this->getContext()->getRequest()->getParameter('sf_culture');

To get this value from the user object instead

In an action you can use:
$locale = $this->getUser()->getCulture();
Outside of an action use:
$locale = $this->getContext()->getUser()->getCulture();
Furthermore, here are some more explanations about the culture support from Symfony explained with an example.

Friday, October 15, 2010

JavaScript find form fields type=file and remove from form if empty when submit

 Following situation. You have a form that should upload files to your server. The user should be able to set a file in the input field file or not. If the file is not set you don't want the form to send the parameter.

This text describes how you can check these file input fields with JavaScript and in case remove them from the form before it gets send.

The form HTML code:
<form action="myurl.com" enctype="multipart/form-data" id="form1" method="post">
<input name="text1" type="text" />
   <input name="text2" type="text" />

   <input name="field1" type="file" />


   <input name="field2" type="file" />
   <input name="field3" type="file" />
   <input onclick="checkFileFields();" type="submit" value="Execute" />
</form>

Put this JavaScript funtion in between the <head></head>-tags:
<script language="javascript">
function checkFileFields()
{
   //get the form with the id form1
   var form = document.getElementById("form1");
   //get all elements in the form, e.g. the input fields
   var formElements = form.elements;
   //initialize an array that stores all the elements that later get deleted, don't delete when you find the element, otherwise you change the formElements array size and miss another field
   var toDeleteElements = [];
   
   for (x in formElements)
   {
      //each element that has type='file' and value is 0 is what we want to delete later
      if(formElements[x].type=='file'&&formElements[x].value.length==0)
      {
         toDeleteElements.push(formElements[x]);
 
      }
   }
   
   for (x in toDeleteElements)
   {
      //remove element from its own parent
      toDeleteElements[x].parentNode.removeChild(toDeleteElements[x]);
   }
}
</script>

Thursday, October 14, 2010

Setting up a FTP server on Linux Debian 5

A simple to use and very popular FTP server is proftpd basic. How to install this FTP server, create an user that has only access via FTP and give him access to a desired directory like /var/www is the context of this article. Although this might work for any kind of Linux like RedHead, Ubuntu, SuSe etc. the instructions are tested for the Linux distribution Debian 5 Lenny.

Install proftpd

Install the proftpd package

To install it on your Debian 5 server the following command from the command line:
sodu apt-get install proftpd-basic

Error: Couldn't find package

If you run into this error message:
Reading package lists... Done
Building dependency tree
Reading state information... Done
E: Couldn't find package proftpd-basic
Make sure your sources.list file contains all the necessary source locations.
The sources.list can be found in Debian 5 at /etc/apt/sources.list.

Here are the entries my sources.list contains:
## main & security repositories
deb http://ftp.us.debian.org/debian/ lenny main contrib non-free
deb-src http://ftp.us.debian.org/debian/ lenny main contrib non-free
deb http://security.debian.org/ lenny/updates main contrib non-free
deb-src http://security.debian.org/ lenny/updates main contrib non-free

Here is a great source list creater tool to fix or update your sources.list in case you don't have the original one anymore.

Important!
After saving any changes to the sources.list run the:
apt-get update
command to update the package list. Now run the install command again.

Run as standalone

When the proftpd installation started successfully you get asked if you want to run the ftp-server under inetd or standalone. Selecting standalone that seems to cause the fewest problem to get the FTP server actually running. You can change this setting later in the proftpd.conf file mentioned below under ServerType.



Create a virtual FTP user and give him access to /var/www

Create a folder for the virtual user

sudo mkdir -p /home/webadmin

Set proftpd user as owner of folder

The proftpd's default user is named proftpd if you didn't changed. Make him the owner of the folder:
sudo chown -R proftpd:nogroup /home/webadmin

Get user proftpd's uid and pid

sudo grep ftp /etc/passwd
Looks like:
proftpd:x:106:65534::/var/run/proftpd:/bin/false

Create the virtual FTP user

sudo ftpasswd --passwd --name=webadmin --uid=106 --gid=65534 --home=/home/webadmin --shell=/bin/false --file=/etc/proftpd/passwd
Response in the CLI:
ftpasswd: using alternate file: /etc/proftpd/passwd
ftpasswd: creating passwd entry for user webadmin

ftpasswd: /bin/false is not among the valid system shells.  Use of
ftpasswd: "RequireValidShell off" may be required, and the PAM
ftpasswd: module configuration may need to be adjusted.

Password:
Re-type password:
After the password was re-typed it says:
ftpasswd: entry created

Install PAM

Then I installed PAM but I am not sure if this is really necessary:
apt-get install libpam-pwdfile

Setup the config file

Now we have to change the proftpd config file:
nano /etc/proftpd/proftpd.conf
Following stuff should be in the file:
DefaultRoot                  ~
RequireValidShell            off
AuthUserFile                 /etc/proftpd/passwd

# VALID LOGINS
<Limit LOGIN>
   AllowUser webadmin
   DenyALL
</Limit>

<Directory /home/webadmin>
   <Limit ALL>
      DenyAll
   </Limit>
   <Limit DIRS READ WRITE>
      AllowUser webadmin
   </Limit>
</Directory>

Don't forget to restart the proftpd - daemon

/etc/init.d/proftpd restart
If you didn't change anything that was in there after the fresh install, this should be sufficient.

Give the FTP user access to /var/www directory

If you want to give this user access to any other directory like /var/www where normally the websites of the Apache2 server are located. You can mount the folder into the /home/webadmin folder.

These are the steps.
Create a new folder:
mkdir /home/webadmin/www
Mount the desired folder to the new folder:
sudo mount --bind -r /var/www /home/webadmin/www
Now you can login to the FTP server from any client computer by using a FTP client like FileZilla for example.

Regulate the access to read access, rather than write access

With this setup a user has read and write access to anything under /home/webadmin. To only give read permission you could change the setting in the config file to:

<Directory /home/webadmin>
   <Limit ALL>
      DenyAll
   </Limit>
   <Limit DIRS READ>
      AllowUser webadmin
   </Limit>
</Directory>

How to find the RSS feed address of a Twitter account

In the old Twitter you could go on a Twitter user's profile and on the bottom right there was a link that says: RSS feed of "XYZ's" tweets.

With the new Twitter these links don't exist anymore. 
There is a way to get this link though. Therefore you have to log out from Twitter and go to your or another twitter account by typing the URL: http://www.twitter.com/anyUser. Now you should be able to see the RSS feed and use it.

For example in PHP to embed the feed in your webside as described here.

Symfony arrays from actions turn into sfOutputEscaperArrayDecorator objects in template.

In symfony a common problem is that arrays that are passed on from the action to the template are turned into sfOutputEscaperArrayDecorator. Then functions like is_array($testArray) won't work anymore.
this->testArray = array('1'=>'a', '2'=>'b', '3'=>'c');


Turning output escaping off

This happens when you have output escaping enabled in Symfony.
If you don't need it you can turn it off in:
myapp/apps/frontend/config/settings.yml
Find the entry and set it to false.
all:
   escaping_strategy:    false


Solution with keeping output escaping enabled

In the action.class.php:
this->testArray = array('1'=>'a', '2'=>'b', '3'=>'c');
In the template.php:
$myArray = $sf_data->getRaw('testArray');

Wednesday, October 13, 2010

Example using OAuth-PHP to retrieve data from Twitter API

Similar to the OAuth-PHP example for Google Contacts we can retrieve data from Twitter in a similar way. The code is based on the examples from the OAuth-PHP page.

The example describes how to integrate the OAuth authentication with PHP in Symfony, when you take the code of the action class it self it can be used in any php file.

Get a twitter consumer key and consumer secret

Before you start coding you need to get a consumer key and consumer secret that allows your application to communicate with twitter.
To do so sign up for an account at Twitter and login.
Go to the Twitter API section and select Register a new application.

Make sure the callback URL is set up properly you need it later in the php code.

In the action.class.php:

public function executeSyncWithTwitter(sfWebRequest $request)
    {
        include_once sfConfig::get('sf_lib_dir')."/oauth/OAuthStore.php";
        include_once sfConfig::get('sf_lib_dir')."/oauth/OAuthRequester.php";

        define("TWITTER_CONSUMER_KEY",  "your_consumer_secret_here");
        define("TWITTER_CONSUMER_SECRET", "your_consumer_key_here");

        define("TWITTER_OAUTH_HOST", "https://twitter.com");
        define("TWITTER_REQUEST_TOKEN_URL", "https://api.twitter.com/oauth/request_token");
        define("TWITTER_AUTHORIZE_URL", "https://twitter.com/oauth/authorize");
        define("TWITTER_ACCESS_TOKEN_URL", "https://api.twitter.com/oauth/access_token");

        define('OAUTH_TMP_DIR', function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : realpath($_ENV["TMP"]));

        //  Init the OAuthStore
        $options = array(
                'consumer_key' => TWITTER_CONSUMER_KEY,
                'consumer_secret' => TWITTER_CONSUMER_SECRET,
                'server_uri' => TWITTER_OAUTH_HOST,
                'request_token_uri' => TWITTER_REQUEST_TOKEN_URL,
                'authorize_uri' => TWITTER_AUTHORIZE_URL,
                'access_token_uri' => TWITTER_ACCESS_TOKEN_URL
        );

        // Note: do not use "Session" storage in production. Prefer a database
        // storage, such as MySQL.
        OAuthStore::instance("Session", $options);

        try
        {
                //  STEP 1:  If we do not have an OAuth token yet, go get one
                if (empty($_GET["oauth_token"]))
                {
                        $getAuthTokenParams = array('oauth_callback' => 'your_domain.com/twitter/syncWithTwitter.php');

                        // get a request token
                        $tokenResultParams = OAuthRequester::requestRequestToken(TWITTER_CONSUMER_KEY, 0, $getAuthTokenParams);

                        //  redirect to the google authorization page, they will redirect back
                        $this->redirect(TWITTER_AUTHORIZE_URL . "?oauth_token=" . $tokenResultParams['token']);
                }
                else {
                        //  STEP 2:  Get an access token
                        $oauthToken = $_GET["oauth_token"];

                        $tokenResultParams = $_GET;

                        try {
                            OAuthRequester::requestAccessToken(TWITTER_CONSUMER_KEY, $oauthToken, 0, 'POST', $_GET);

                        }
                        catch (OAuthException2 $e)
                        {
                            //if there is any error send the user back to the homepage of your app
                            $this->redirect('@home');
                        }
                }
               
                //get the users information (see below for details about the return)
                $userContainerRequest = new OAuthRequester("https://twitter.com/account/verify_credentials.xml", 'GET', $tokenResultParams);
                $userContainer = $userContainerRequest->doRequest(0);
               
                if ($userContainer['code'] == 200) {
                    $this->user = new SimpleXMLElement($userContainer['body']);
                   }
        }
        catch(OAuthException2 $e) {
            //if there is any error send the user back to the homepage of your app
            $this->redirect('@home');
        }
        return sfView::SUCCESS;
    }

In the template syncWithTwitter.php:

All possible values of the twitter user object can be seen in the Twitter docs. The values from the SimpleXMLElement can be accessed as described below:

<h3>
Show Twitter user</h3>
<?php
echo $user->id; // e.g. 12345678
echo $user->name; // e.g. Peter Parker
echo $user->screen_name; // e.g. Spiderman
?>

In the routing.yml:

syncWithTwitter:
url:    /twitter/syncWithTwitter/*.
param:  { module: twitter, action: syncWithTwitter }

Friday, October 8, 2010

Get certain system path in a Symfony project

Sometimes you need to include a php file from certain system path in Symfony within a action.class.php.
The code to do that looks like be:
include_once sfConfig::get("sf_app_dir")."/lib/Helper.php";
Symfony has different keys that resolve to a certain directory in the project. The base directory in the below example is example_app in the Apache2 default folder /var/www.


sfConfig::get("sf_root_dir");          # /var/www/example_app
sfConfig::get("sf_apps_dir");          # /var/www/example_app/apps
sfConfig::get("sf_app_dir");           # /var/www/example_app/apps/frontend 
sfConfig::get("sf_app_config_dir");    # /var/www/example_app/apps/config
sfConfig::get("sf_app_i18n_dir");      # /var/www/example_app/apps/i18n
sfConfig::get("sf_app_lib_dir");       # /var/www/example_app/apps/lib
sfConfig::get("sf_app_module_dir");    # /var/www/example_app/apps/modules
sfConfig::get("sf_app_template_dir");  # /var/www/example_app/apps/templates
sfConfig::get("sf_cache_dir");         # /var/www/example_app/cache
sfConfig::get("sf_app_base_cache_dir");# /var/www/example_app/cache/frontend
sfConfig::get("sf_app_cache_dir");     # /var/www/example_app/cache/frontend/dev
sfConfig::get("sf_template_cache_dir");# /var/www/example_app/cache/frontend/dev/templates
sfConfig::get("sf_i18n_cache_dir");    # /var/www/example_app/cache/frontend/dev/i18n
sfConfig::get("sf_config_cache_dir");  # /var/www/example_app/cache/frontend/dev/config
sfConfig::get("sf_test_cache_dir");    # /var/www/example_app/cache/frontend/dev/test
sfConfig::get("sf_module_cache_dir");  # /var/www/example_app/cache/frontend/dev/modules
sfConfig::get("sf_config_dir");        # /var/www/example_app/config
sfConfig::get("sf_data_dir");          # /var/www/example_app/data
sfConfig::get("sf_doc_dir");           # /var/www/example_app/doc
sfConfig::get("sf_lib_dir");           # /var/www/example_app/lib
sfConfig::get("sf_log_dir");           # /var/www/example_app/log
sfConfig::get("sf_test_dir");          # /var/www/example_app/test
sfConfig::get("sf_plugins_dir");       # /var/www/example_app/plugins
sfConfig::get("sf_web_dir");           # /var/www/example_app/web
sfConfig::get("sf_upload_dir");        # /var/www/example_app/uploads

Joomla 1.5.20 - "Warning! - Failed to move file"

When you try to install any extension like  modules, components, plugins, languages or templates in Joomla in version 1.5.x an error could occur that says: "Warning! - Failed to move file".

That is because Joomla doesn't have the writing rights after a fresh install for the affected folders.
Following folders should be write enabled for any extension you upload:

joomla_base/tmp
joomla_base/language
(sometimes: joomla_base/logs)

Then depending on if you upload a administrator(backend) or a user(frontend) extension the according folder needs to be write enabled. That could be:

joomla_base/modules
joomla_base/components
joomla_base/plugins
joomla_base/templates
or
joomla_base/administrator/modules
joomla_base/administrator/components
joomla_base/administrator/language
joomla_base/administrator/templates

In Unix/Linux systems the CLI command would be:

chmod -R 777 language

Where -R stands for recursive all subfolders and files of the folder "language".

Warning: Failed to move file! in Joomla Media Manager

When you try to upload a file in Joomla 1.5 via the Media Manager of the Backend you could run into a similar error as described above.

Warning: Failed to move file!
Error. Unable to upload file.

To solve this problem you need to write enable the images folder in the joomla base path. Not the media folder!
chmod -R 777 images

Thursday, October 7, 2010

Forward certain URL(Domain) to specific folder on the Server with Apache2 Virtual Host and Debian5

If you have a Apache2 Web server that hosts different websites you might have them in different folders under /var/www (standard root folder in Apache2 on Debian5). For each project you have your own domain. Each domain should now point to a different folder.

For example you have mydomain.com, my2nddomain.com.
mydomain.com should hit /var/www/folder1/index.html and my2nddomain.com should display the index.html of /var/www/folder2/index.php (Lets assume folder2 contains a php application like Joomla).

First of all make sure that both domains point to the IP of your server. Normally Apache2 is running under port 80 so you don't need to specify the port.

Now open for example with vim or nano the Apache2 according Apache config file:

nano /etc/apache2/sites-enabled/000-default

Here you should add two entries at the very end of the file:



<virtualhost *:80>
ServerName mydomain.com
ServerAlias *.mydomain.com
DocumentRoot "/var/www/folder2"
DirectoryIndex index.html
<directory "/var/www/folder">
AllowOverride All
Allow from All
</Directory>
</VirtualHost>

<virtualhost *:80>
ServerName my2nddomain.com
ServerAlias *.my2nddomain.com
DocumentRoot "/var/www/folder2"
DirectoryIndex index.php
<directory "/var/www/folder2">
AllowOverride All
Allow from All
</Directory>
</VirtualHost>

Now don't forget to restart the Apache2 server with:

/etc/init.d/apache2 restart

And you are good to go.

Monday, October 4, 2010

JMeter - random boolean value in HTTP Request with JavaScript

If you are writing a load test with JMeter you often want to send in an random Boolean value with a HTTP request.

Following JavaScript code snippet can do that:
${__javaScript(Math.round(Math.random())==1 ? "true" : "false";)} 

What the code does in detail:

The surrounding $-sign with curly brackets

${...}
This signalizes to JMeter that a variable is used.

The javaScript tag

__javaScript()
This signalizes to JMeter to interpret the containing code as JavaScript.

The JavaScript Random function

Math.random()
Returns a random decimal value between 0 and 1.

The JavaScript Round function

Math.round()
Rounds values from .5 up to 1 and values below down to 0. So in 50% of the cases you get 0 and in the other 50% the value 1.


The IF shortcut

x==1 ? "true" : "false"
This expression stands for IF(?) x equals(==) 1 then return true else(:) false.

Other Purpose:

This code could also be used to send in a value randomly with the HTTP request or leave the value blank.
${__javaScript(Math.round(Math.random())==1 ? "12345" : "";)} 

Friday, October 1, 2010

Using url_for() in an Action - Symfony

In an symfony template you can get the base url of the application with:
url_for('@homepage', true);

Using this code in an action causes:
Fatal error: Call to undefined function url_for() in ...

In an action this code should be used instead:
$baseUrl = $this->getController()->genUrl('@homepage', true);

Using Oauth-php to retrieve email addresses from Google contacts (Gmail) in Symfony

The below code is a modification of the example code from the Oauth-php project. The example is for getting the 3 legged OAuth access to Google Docs.

Here the code was modified to get email addresses from Google contacts. That are basically all the email address that are used in Gmail.

The code was integrated in the Symfony framework. To get it running on a single PHP page only use the stuff in the action.class.php.

Of oauth-php project only the files in the library folder where used and copied into the symfony-project-base/lib/oauth.

For the rest a module gmail in symfony-project-base/apps/frontend/modules was created.

Symfony version 1.4.8 and oauth-php version 155.

in the action.class.php

public function executeStart(){

include_once sfConfig::get('sf_lib_dir')."/oauth/OAuthStore.php";
include_once sfConfig::get('sf_lib_dir')."/oauth/OAuthRequester.php";

define("GOOGLE_CONSUMER_KEY", sfConfig::get("app_google_consumer_key", "myDomain.com")); //
define("GOOGLE_CONSUMER_SECRET", sfConfig::get("app_google_consumer_secret", "myConsumerKey")); //

define("GOOGLE_OAUTH_HOST", "https://www.google.com");
define("GOOGLE_REQUEST_TOKEN_URL", GOOGLE_OAUTH_HOST . "/accounts/OAuthGetRequestToken");
define("GOOGLE_AUTHORIZE_URL", GOOGLE_OAUTH_HOST . "/accounts/OAuthAuthorizeToken");
define("GOOGLE_ACCESS_TOKEN_URL", GOOGLE_OAUTH_HOST . "/accounts/OAuthGetAccessToken");

define('OAUTH_TMP_DIR', function_exists('sys_get_temp_dir') ? sys_get_temp_dir() : realpath($_ENV["TMP"]));

//  Init the OAuthStore
$options = array(
'consumer_key' => GOOGLE_CONSUMER_KEY,
'consumer_secret' => GOOGLE_CONSUMER_SECRET,
'server_uri' => GOOGLE_OAUTH_HOST,
'request_token_uri' => GOOGLE_REQUEST_TOKEN_URL,
'authorize_uri' => GOOGLE_AUTHORIZE_URL,
'access_token_uri' => GOOGLE_ACCESS_TOKEN_URL
);


// Note: do not use "Session" storage in production. Prefer a database
// storage, such as MySQL.
OAuthStore::instance("Session", $options);

try
{
//  STEP 1:  If we do not have an OAuth token yet, go get one
if (empty($_GET["oauth_token"]))
{
$getAuthTokenParams = array('scope' =>
'http://www.google.com/m8/feeds/contacts/default/base',
'xoauth_displayname' => 'Oauth test',
'oauth_callback' => 'http://myDomain.com/gmail/start');

// get a request token
$tokenResultParams = OAuthRequester::requestRequestToken(GOOGLE_CONSUMER_KEY, 0, $getAuthTokenParams);

$this->redirect(GOOGLE_AUTHORIZE_URL . "?btmpl=mobile&oauth_token=" . $tokenResultParams['token']);
}
else {
//  STEP 2:  Get an access token
$oauthToken = $_GET["oauth_token"];

$tokenResultParams = $_GET;

try {
OAuthRequester::requestAccessToken(GOOGLE_CONSUMER_KEY, $oauthToken, 0, 'POST', $_GET);

}
catch (OAuthException2 $e)
{
var_dump($e);
// Something wrong with the oauth_token.
// Could be:
// 1. Was already ok
// 2. We were not authorized
return;
}
}
// STEP 3: make the contacts request. Default is 25 contacts per request, the max that worked for me is 32.
$nextUrl = "http://www.google.com/m8/feeds/contacts/default/base?max-results=32";
$emailList = "";
while($nextUrl != ""){
$request = new OAuthRequester($nextUrl, 'GET', $tokenResultParams);
$result = $request->doRequest(0);
if ($result['code'] == 200) {
$oXML = new SimpleXMLElement($result['body']);

$nextUrl = "";
//parse for next url
foreach($oXML->link as $link){
$linkAttributes = $link->attributes();
$nextFlag = $linkAttributes["rel"];
if($nextFlag == "next"){
$nextUrl = $linkAttributes["href"];
//                                    echo "nextUrl: ".$nextUrl."
";
//replace user id with default, because otherwise the next call fails
$endOfFirstPart = strrpos($nextUrl,"feeds/contacts/");
$startOfSecondPart = strrpos($nextUrl,"/base?");

$urlFirstPart = substr($nextUrl,0,$endOfFirstPart+15);
$urlSecondPart = substr($nextUrl,$startOfSecondPart,strlen($nextUrl));
$nextUrl = $urlFirstPart."default".$urlSecondPart;
//                                    echo "nextUrl: ".$nextUrl."
";
break;
}
}

//parse for email entries 
foreach($oXML->entry as $oEntry){
$gd = $oEntry->children("http://schemas.google.com/g/2005");
foreach($gd->email as $emailNode){
$attributeArray = $emailNode->attributes();
$emailList = $emailList.$attributeArray["address"].",\n";
}
}
}
else {
echo 'Error';
}
}
$this->resultList = $emailList;
}
catch(OAuthException2 $e) {
echo "OAuthException:  " . $e->getMessage();
var_dump($e);
}
return sfView::SUCCESS;
}

in the template startSuccess.php

Show Gmail contacts

<?php echo $resultList; ?>

in the routing.yml

gmailStart:
url:    /gmail/start/*.
param:  { module: gmail, action: start }