Integrating Google Docs using CForms in WordPress

Given the ubiquitous nature of Google Docs, it’s no surprise that I’ve had a few clients ask about direct integration from their WordPress sites. One of my favorite solutions for robust form building on WordPress is Oliver Seidel’s very excellent CForms II. If you don’t use CForms already, go grab the latest copy (it’s not available through the WordPress repository), and install it before you begin. Given that CForms supports alternate action on post, I assumed it would probably be a very straight forward process to setup posting directly to our spreadsheet on Docs.

You know what they say about assumption.

Ten hours later, and after many hair pulling failures and fruitless Google searches, here’s the method that worked for me. Important: This method will set the default action for all CForms on your site. Setting just one form is slightly different, and I’ll note that at the end.

Create your Google FormFirst, create your form in Google Docs: 

Include all fields you want to collect, but don’t make any of them required. You’ll want to control all data requirement and validation from CForms. Once you’ve created the form, click the blue Share button in the upper right, and click ‘Change’ under “Who has access:”. Select “Public on the web”, and save. Once that’s done, click the link at the bottom of the page to view the form live on the web. View the source of that page to get the data needed for the next step.

Collect your form action and field positions:

In the source code of the form you just created, you’ll need two things: your post url, and your field positions. Your post url will look like this:

https://docs.google.com/spreadsheet/formResponse?formkey=dE1vV3cwZTdDeEt6U0lZR0hpR0hZeFE6MQ&ifq

Astute eyed individuals will note that there is an HTML encoded character in that string; you will need to fix that when you set it as your post url in the script below. Those that don’t catch that on the first time around (like a certain slightly hung over web guy, for instance), will find that their post fails.

The second piece of the puzzle is the field positions assigned to the form. They’ll look like this:

name="entry.0.single"

Entry is the action, 0 is the field position in the row, and single is the data type. Multi-select, radio buttons and checkbox groups will all use “group”.

Create a matching form in CForms:

Create your form in CForms

Disable the “Ajax enabled” option; it makes bad things happen (specifically, the dreaded “One moment please” error). Save your form, and open it in your browser.

Now that you’ve got your form setup, and your form information at hand, on to the code!

Open my-functions.php, and find ‘function my_cforms_action’:

Depending on your version, this will be on or around line 228. Uncomment the function, and delete everything inside it. Start by setting your global variables, and your post url:

#Get form info
$formID = $cformsdata['id'];
$form = $cformsdata['data'];

#Assign Address
$url = 'https://docs.google.com/spreadsheet/formResponse?formkey=dE1vV3cwZTdDeEt6U0lZR0hpR0hZeFE6MQ&ifq&';

In our url string, we’ve corrected the & to &, and added an additional ampersand to the end; this url is going to prepend our curl string, and the trailing ampersand gets it ready for our variables.

Next we need to add our fields. I tried several methods of using custom field ID’s in CForms, and none of them ever worked; it always added in extra fields to the array that ended up causing null returns for the specific values being called. Using the default field names (which are generated by the field label) is the only method that worked for me, despite their verbose structure (one of my field variables is “$form ['Special Instructions or Additional Details']“).

Gather the field names from the source of your CForms page, and build the following array with your fields:

#prepare post data
$fields = array(
'entry.0.single'=>urlencode($form ['Field 1']),
'entry.1.single'=>urlencode($form ['Field 2']),
'entry.2.single'=>urlencode($form ['Field 3']),
'entry.3.group'=>urlencode($form ['Field 4']),
'entry.4.single'=>urlencode($form ['Field 5']),
'backupCache'=>urlencode("") ,
'pageNumber'=>urlencode("0"),
'submit'=>urlencode("Submit"));

Pay close attention to the entry positions and field types. Depending on what you’ve done while editing the Google Form, your entry positions may not be serial.

Now add a function to turn your form values and post url into a single string:

foreach($fields as $key=>$value) {
$fields_string .= $key.'='.$value.'&';
}

rtrim($fields_string,"& ");
$fields_string = substr($fields_string, 0, strlen($fields_string)-1);

Finally, create your curl connection and execute:

$ch = curl_init();
#set curl_setopt for your preferences
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

#set post url and timeout
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_TIMEOUT, 120);

#execute post
$result = curl_exec($ch) ;
# uncomment for debug print_r($url.$fields_string);
curl_close($ch);

The second to last line is strictly for debug. If your post isn’t writing to the spreadsheet, uncomment that line to dump the value of the post array, and verify that the string is being built correctly.

And, all together:

function my_cforms_action($cformsdata) {

#Get form info
$formID = $cformsdata['id'];
$form = $cformsdata['data'];

#Assign Address
$url = 'https://docs.google.com/spreadsheet/formResponse?formkey=dE1vV3cwZTdDeEt6U0lZR0hpR0hZeFE6MQ&ifq&';

#prepare post data
$fields = array(
'entry.0.single'=>urlencode($form ['Field 1']),
'entry.1.single'=>urlencode($form ['Field 2']),
'entry.2.single'=>urlencode($form ['Field 3']),
'entry.3.group'=>urlencode($form ['Field 4']),
'entry.4.single'=>urlencode($form ['Field 5']),
'backupCache'=>urlencode("") ,
'pageNumber'=>urlencode("0"),
'submit'=>urlencode("Submit"));

foreach($fields as $key=>$value) {
$fields_string .= $key.'='.$value.'&';
}

rtrim($fields_string,"& ");
$fields_string = substr($fields_string, 0, strlen($fields_string)-1);

$ch = curl_init();
#set curl_setopt for your preferences
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

#set post url and timeout
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_TIMEOUT, 120);

#execute post
$result = curl_exec($ch) ;
# uncomment for debug print_r($url.$fields_string);
curl_close($ch);
}

As mentioned above, this sets the default action for CForms in your WordPress install. To only have one form call this function, you’ll have to move the code into the formID conditional:

function my_cforms_action($cformsdata) {

$formID = $cformsdata['id'];
$form = $cformsdata['data'];

### triggers on your third form. The first form ID is null: if ( $formID == '' )
if ( $formID == '3' ) {

#Assign Address
$url = 'https://docs.google.com/spreadsheet/formResponse?formkey=dE1vV3cwZTdDeEt6U0lZR0hpR0hZeFE6MQ&ifq&';

#prepare post data
$fields = array(
'entry.0.single'=>urlencode($form ['Field 1']),
'entry.1.single'=>urlencode($form ['Field 2']),
'entry.2.single'=>urlencode($form ['Field 3']),
'entry.3.group'=>urlencode($form ['Field 4']),
'entry.4.single'=>urlencode($form ['Field 5']),
'backupCache'=>urlencode("") ,
'pageNumber'=>urlencode("0"),
'submit'=>urlencode("Submit"));

foreach($fields as $key=>$value) {
$fields_string .= $key.'='.$value.'&';
}

rtrim($fields_string,"& ");
$fields_string = substr($fields_string, 0, strlen($fields_string)-1);

$ch = curl_init();
#set curl_setopt for your preferences
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 0);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $fields_string);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);

#set post url and timeout
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_TIMEOUT, 120);

#execute post
$result = curl_exec($ch) ;
# uncomment for debug print_r($url.$fields_string);
curl_close($ch);

}

}

I hope this helps save you tons of time. Got a better method? Let me know about it in the comments!