1

Edited 10/12/2011 at 17:00 Eastern to correct two typos in XML code of calendar_functions.js

I'm working locally on my laptop, behind a dynamic IP, to develop a website application to interact with the Google Calendar API via AJAX. I'm not sure how to get around AJAX's cross-domain restrictions as Google's documentation doesn't really talk about that. Perhaps the problem is that the request to Google comes from http://domain instead of a full URL like http://domain.net?

Google provides the ability to register your web app (http://code.google.com/apis/accounts/docs/RegistrationForWebAppsAuto.html), which would provide me with a unique consumer_key/consumer_secret pair, but the registration form won't accept http://domain as a domain. Without registration, that pair is anonymous/anonymous.

The Stackoverflow post How does Google's javascript API get around the cross-domain security in AJAX explains that Google gets around this by dynamically injecting SCRIPT tags into the head of the document. I tried the three methods described in that thread, but still have the same message in Chrome's console: "Origin http://domain is not allowed by Access-Control-Allow-Origin."

Google's documentation on signing OAuth requests is at http://code.google.com/apis/accounts/docs/OAuth_ref.html#SigningOAuth.

Here's the form I'm testing with that would add a single-occurrence event to a calendar:

<?php  // calendar.php

session_start();
require_once 'header.php';

?>


<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Site's Title</title>

    <link rel="stylesheet" href="/styles.css">
    <script type="text/javascript" src="/js/jquery.js"></script>
    <script type="text/javascript" src="/js/UMDNJ.js"></script>
    <script type="text/javascript" src="/js/oauth_lib/sha1.js"></script>
    <script type="text/javascript" src="/js/oauth_lib/oauth.js"></script>
    <script type="text/javascript">
      $(document).ready( function() {
          $.getScript('/includes/calendar_functions.js', function() {
            alert("calendar_functions.js has been added");
          });
      });
    </script>
</head>
<body>

<h3>Add Calendar Event</h3>

<div id="addEvent">
<form action="calendar.php" id="addEventForm" method="POST">
  <label>Event title: </label><input type="text" name="eventTitle" id="eventTitle" size="20" /><br />
  <label>Event content: </label><textarea name="eventContent" id="eventContent" cols="20" rows="5"></textarea><br />
  <label>Location: </label><input type="text" name="eventLoc" id="eventLoc" size="20" /><br />
  <label>Start time: </label><input type="text" name="startTime" id="startTime" size="20" /><br />
  <label>End time: </label><input type="text" name="endTime" id="endTime" size="20" /><br />
  <input type="submit" name="eventSubmit" value="Submit" />
</form>
</div> <!-- /#addEvent -->

</body>
</html>


And here's the jQuery that processes the form's submit event, with borrowed code from this library:

// calendar_functions.js

dateObj = new Date();


function nonce( length ) {
  var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
    var result = "";
    for (var i = 0; i < length; ++i) {
    var rnum = Math.floor(Math.random() * chars.length);
    result += chars.substring(rnum, rnum+1);
    }
    return result;
}


  $("#addEventForm").submit(function() {

    // Make the XML for creating a new calendar event
    var eventTitle = $("#eventTitle").val();
    var eventContent = $("#eventContent").val();
    var startTime = $("#startTime").val();
    var endTime = $("#endTime").val();
    var eventLoc = $("#eventLoc").val();
    var eventXML = "<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gd='http://schemas.google.com/g/2005'>";
    eventXML += "<category scheme='http://schemas.google.com/g/2005#kind' term='http://schemas.google.com/g/2005#event'></category>";
    eventXML += "<title type='text'>"+eventTitle+"</title>";
    eventXML += "<content type='text'>"+eventContent+"</content>";
    eventXML += "<gd:transparency value='http://schemas.google.com/g/2005#event.opaque'></gd:transparency>";
    eventXML += "<gd:eventStatus value='http://schemas.google.com/g/2005#event.confirmed'></gd:eventStatus>";
    eventXML += "<gd:where valueString='"+eventLoc+"'></gd:where>";
    eventXML += "<gd:when startTime='"+startTime+"' endTime='"+endTime+"'></gd:when>";
    eventXML += "</entry>";


    // Set connection details
    var Connection = {
      method: "POST",
      url: "https://www.google.com/calendar/feeds/default/private/full",
      params: {
        oauth_version: "1.0",
        oauth_nonce: nonce(30),
        oauth_timestamp: dateObj.getTime(),
        oauth_consumer_key: "anonymous",
        oauth_token: "--hidden for stackoverflow post for security reasons--",
        oauth_signature_method: "HMAC-SHA1",
      }
    };

    var oauth_consumer_secret = "anonymous";
    var oauth_token_secret = "--hidden for stackoverflow post for security reasons--";
    var oauth_verifier = "--hidden for stackoverflow post for security reasons--";

    // Encode connection details
    var connectD = OAuth.percentEncode('oauth_version')+'="'+OAuth.percentEncode(Connection.params.oauth_version)+'"';
    connectD += ','+OAuth.percentEncode('oauth_nonce')+'="'+OAuth.percentEncode(Connection.params.oauth_nonce)+'"';
    connectD += ','+OAuth.percentEncode('oauth_timestamp')+'="'+OAuth.percentEncode(Connection.params.oauth_timestamp)+'"';
    connectD += ','+OAuth.percentEncode('oauth_consumer_key')+'="'+OAuth.percentEncode(Connection.params.oauth_consumer_key)+'"';
    connectD += ','+OAuth.percentEncode('oauth_token')+'="'+OAuth.percentEncode(Connection.params.oauth_token)+'"';
    connectD += ','+OAuth.percentEncode('oauth_signature_method')+'="'+OAuth.percentEncode(Connection.params.oauth_signature_method)+'"';

    // Make the base string
    var baseString = Connection.method+'&'+Connection.url+'&'+OAuth.SignatureMethod.normalizeParameters(Connection.params);    
    baseString = OAuth.percentEncode(baseString);

    // Make the signature
    var theKey = oauth_consumer_secret+'&'+oauth_token_secret;
    var b64pad = '=';    
    var signature = b64_hmac_sha1(theKey, baseString);

    // Add signature to encoded base connection details
    connectD += ','+OAuth.percentEncode('oauth_signature')+'="'+OAuth.percentEncode(signature)+'"';

//     alert(connectD);
//     alert(baseString);
    alert(signature);

    // Submit request to Google via AJAX
    $.ajax({
      type: 'POST',
      url: 'https://www.google.com/calendar/feeds/default/private/full',
      data: eventXML,
      dataType: 'xml',
      contentType: 'application/atom+xml',
//       headers: [
//          ['Access-Control-Allow-Origin', '*'],
//          ['Authorization', 'OAuth '+connectD]
//       ],
      beforeSend: function(xhr) {
        $("#addEvent").before('<div id="message_center">uploading...<br /></div>');
        xhr.setRequestHeader('Authorization', 'OAuth '+connectD);
      },
      success: function(data, textStatus, jqXHR) {
        $("#addEvent").before('<div id="message_center">Success!<br /> Data: '+data+'<br />textStatus: '+textStatus);
        console.log(data, textStatus);
      },
      error: function(jqXHR, textStatus, errorThrown) {
        $("#addEvent").before('<div id="message_center">'+textStatus+'<br />'+errorThrown+'</div>');
        var res = jqXHR.getAllResponseHeaders();
        console.info(jqXHR, textStatus, errorThrown);
      },
    }); 
    return false;
  });

I appreciate any suggestions anyone may have.

Thank you.

Community
  • 1
  • 1
csuggs4
  • 29
  • 5

1 Answers1

0

A workaround you could you use is to add a domain to your hosts file and redirect it to 127.0.0.1 Then register that domain.

I'm not sure if it'll work as Google may have to communicate with the 'server' - which they can't since you are behind a firewall.

Nuno Cordeiro
  • 394
  • 2
  • 13