Google Documents List API - How to Publish a Document - gdata-api

I'm utterly lost as to how one can programmatically publish a Google Document (specifically a spreadsheet).
I've read the Google Documents List API Protocol Guide and have found this:
http://code.google.com/apis/documents/docs/3.0/developers_guide_protocol.html#GettingRevisions
The next section of the article begins with 'Publishing documents by publishing a single revision' and this is where I found this example:
PUT /feeds/default/private/full/resource_id/revisions/revision_number
GData-Version: 3.0
Authorization: <your authorization header here>
Content-Length: 722
Content-Type: application/atom+xml
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:gd='http://schemas.google.com/g/2005'
xmlns:docs="http://schemas.google.com/docs/2007" gd:etag="W/"DkIBR3st7ImA9WxNbF0o."">
<id>https://docs.google.com/feeds/id/resource_id/revisions/1</id>
<updated>2009-08-17T04:22:10.440Z</updated>
<app:edited xmlns:app="http://www.w3.org/2007/app">2009-08-06T03:25:07.799Z</app:edited>
<title>Revision 1</title>
<content type="text/html" src="https://docs.google.com/feeds/download/documents/Export?docId=doc_id&revision=1"/>
<link rel="alternate" type="text/html"
href="https://docs.google.com/Doc?id=doc_id&revision=1"/>
<link rel="self" type="application/atom+xml"
href="https://docs.google.com/feeds/default/private/full/resource_id/revisions/1"/>
<author>
<name>user</name>
<email>user#gmail.com</email>
</author>
<docs:publish value="true"/>
<docs:publishAuto value="false"/>
</entry>
I have been retrieving document list feeds and CRUDing worksheets but I cannot get the publishing to work nor do I understand how it is supposed to work. My basic setup for establishing a connection to my feed and preparing the data to be PUT is as follows:
<?php
set_include_path($_SERVER['DOCUMENT_ROOT'].'/library/');
require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->setFallbackAutoloader(true);
$theId = 'my-worksheet-id';
$user = "my-gmail-account-name";
$pass = "my-gmail-account-password";
$service = Zend_Gdata_Docs::AUTH_SERVICE_NAME;
$client = Zend_Gdata_ClientLogin::getHttpClient($user, $pass, $service);
$service = new Zend_Gdata($client);
$xml = "<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gd='http://schemas.google.com/g/2005'
xmlns:docs='http://schemas.google.com/docs/2007' gd:etag='W/\"DkIBR3st7ImA9WxNbF0o.\"'>
<id>https://docs.google.com/feeds/id/spreadsheet:$theId/revisions/1</id>
<updated>2009-08-17T04:22:10.440Z</updated>
<app:edited xmlns:app='http://www.w3.org/2007/app'>2009-08-06T03:25:07.799Z</app:edited>
<title>Revision 1</title>
<content type='text/html' src='https://docs.google.com/feeds/download/documents/Export?docId=$theId&revision=1'/>
<link rel='alternate' type='text/html'
href='https://docs.google.com/Doc?id=$theId&revision=1'/>
<link rel='self' type='application/atom+xml'
href='https://docs.google.com/feeds/default/private/full/spreadsheet:$theId/revisions/1'/>
<author>
<name>$user</name>
<email>$user</email>
</author>
<docs:publish value='true'/>
<docs:publishAuto value='false'/>
</entry>";
$putURL = "http://docs.google.com/feeds/default/private/full/spreadsheet:".$theId."/revisions/0";
$data = $service->put($xml, $putURL);
?>
Which results in a
Fatal error: Uncaught exception 'Zend_Gdata_App_HttpException' with message 'Expected response code 200, got 400 Invalid request URI
Can someone help me out? Has anyone successfully published a Google Document programmatically?

Assuming the document has already been created and has a document id of XXXX
What you need to do is send a "PUT" request with specific headers, and XML (an entry describing your document) as the body, to a specific URL.
Since you are not changing any content of the doc (only the meta-data), your target URL will look like this...
https://docs.google.com/feeds/default/private/full/XXXX/revisions/0
The first thing you need to do is authenticate with the proper Google service.
$client = Zend_Gdata_ClientLogin::getHttpClient(GDOC_LOGIN, GDOC_PASS,'writely');
Use the returned object to grab your auth token.
$auth_token = $client->getClientLoginToken();
In Zend/Gdata/App.php is a helper function for executing the PUT request.
Prepare parameters for this method like so...
$method = "PUT";
$url ="https://docs.google.com/feeds/default/private/full/XXXX/revisions/0";
$headers['GData-Version'] = '3.0';
$headers['If-Match'] = '*';
$headers['Authorization'] = "GoogleLogin auth = $auth_token";
$headers['Content-Length'] = '380';
$headers['Content-Type'] = 'application/atom+xml';
$body = <<<XML
<?xml version='1.0' encoding='UTF-8'?>
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:docs="http://schemas.google.com/docs/2007"
xmlns:gd="http://schemas.google.com/g/2005">
<category scheme="http://schemas.google.com/g/2005#kind"
term="http://schemas.google.com/docs/2007#spreadsheet"/>
<docs:publish value="true"/>
<docs:publishAuto value="true"/>
</entry>
XML;
$contentType = "application/atom+xml";
$remainingRedirects = 99;
Then call the helper function...
$app = new Zend_Gdata_App();
$app->performHttpRequest($method, $url, $headers, $body, $contentType, $remainingRedirects);
Good luck!
Let me know if this helps!

Ok... where do I start?
First of all, your URL is incorrect. (the resource ID you're using is for JSON/XML not URL)
you have
$putURL = "http://docs.google.com/feeds/default/private/full/spreadsheet:".$theId."/revisions/0";
and you should have
$putURL = "http://docs.google.com/feeds/default/private/full/$theId/revisions/0";
(you can omit . for concatenation if you use " as delimiters)
now there are other problems since you're manually creating a xml entry.
Your authorization header is missing.
In your XML you're using revision 1 but in your URL you have revision/0
value is manually written and I'm pretty sure you are not trying to publish a 2 years old file. Same for and
MUST MATCH the retrieved etag or you won't be able to perform any PUT request.
Now you can change these values manually assigning variables but I think it's better to use Zend GData structured returned object.
In any case:
Retrieve from google the document you want to publish.
Find the correct entry (in this case the entry with the ID https://docs.google.com/feeds/id/spreadsheet:$theId/revisions/1)
change docs:publish value property to "true"
send a put request with the modified entry
that should work

I am new at Zend_Gdata myself but have sucessfully uploaded to Google Docs.
I don't know if this is what you are after but here is my code:
$client = Zend_Gdata_ClientLogin::getHttpClient(
'my#googleDocsEmail.address',
'MyPassword',
Zend_Gdata_Docs::AUTH_SERVICE_NAME
);
$gdClient = new Zend_Gdata_Docs($client);
$newDocumentEntry = $gdClient->uploadFile(
$file,
null,
null,
Zend_Gdata_Docs::DOCUMENTS_LIST_FEED_URI
);
I hope this helps,
Garry

Google says that putted data is wrong and response you with code 400.
try to place this code
<?xml version='1.0' encoding='UTF-8'?>
before
<entry xmlns='http://www.w3.org/2005/Atom'...

Related

The correct way to use Ocamlnet 3 - Http_client.Convenience.http_post

I am trying to use Http_client.Convenience.http_post to make a http post request.
The API is fairly simple:
val http_post : string -> (string * string) list -> string
Does a "POST" request with the given URL and returns the response
body. The list contains the parameters send with the POST request.
What I wish to do is to construct a http post request to get the flight information via google flights, explained as part 1 in here: http://www.nohup.in/blog/using-json-google-flights
To maintain the format of the Post request, I took a screenshot as this:
So finally, I construct a Http_client.Convenience.http_post for it:
open Http_client.Convenience;;
let post_para = [("(Request-Line)", "POST /flights/rpc HTTP/1.1");
("Host", "www.google.com");
("Content-Type", "application/json; charset=utf-8");
("X-GWT-Permutation", "0BB89375061712D90759336B50687E78");
("X-GWT-Module-Base", "http://www.google.com/flights/static/");
("Referer", "http://www.google.com/flights/");
("Content-Length", "275");
("Cookie", "PREF=ID=2dc218fc830df28d:U=29aaf343dd519bca:FF=0:TM=1307225398:LM=1308065727:GM=1:S=RWC3dYzVvVSpkrlz; NID=52=VTp1QILW1ntPlrkLx7yLUtOYhchNk35G4Lk35KBd7A3lCznVV5glz7lwDoDP2RkjtTJVNZSomv3iffPqiJz4oXfpoph3ljb2eInGOe-FwosvrmSXPpnLkEWxMHIbuaid; S=travel-flights=YFCjkd9M9h3Z_uEqBmgynA");
("Pragma", "no-cache");
("Cache-Control", "no-cache");
("data", "[,[[,\"fs\",\"[,[,[\"SJC\"]\n,\"2012-04-05\",[\"EWR\",\"JFK\",\"LGA\"]\n,\"2012-04-12\"]\n]\n\"]],[,[[,\"b_ca\",\"54\"],[,\"f_ut\",\"search;f=SJC;t=EWR,JFK,LGA;d=2012-04-05;r=2012-04-12\"],[,\"b_lr\",\"11:36\"],[,\"b_lr\",\"1:1528\"],[,\"b_lr\",\"2:1827\"],[,\"b_qu\",\"3\"],[,\"b_qc\",\"1\"]]]]")];;
let search () = try (http_post "http://www.google.com/flights/rpc" post_para) with
Http_client.Http_error (id, msg) -> msg;;
let _ = print_endline (search());;
When I run it, it just give me the following error html page:
<HTML>
<HEAD>
<TITLE>Internal Server Error</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>Internal Server Error</H1>
<H2>Error 500</H2>
</BODY>
</HTML>
Can anyone tell me why? What's wrong with my http_post?
Drop this from post_para, it is not an HTTP Header, OCamlnet will send that automatically for you: "(Request-Line)", "POST /flights/rpc HTTP/1.1".
To send the headers and POST data separately you need to set the headers use set_request_header on the http_call object.
Also the Convenience module in OCamlnet will send the data as application/x-www-form-urlencoded, but I think you need the data sent as is. You can do that by using Http_client.post_raw.

Select attribute in simplexml

The xml data looks like this:
<feed>
<entry>
<id>12345</id>
<title>Lorem ipsum</title>
<link type="type1" href="https://foo.bar" />
<link type="type2" href="https://foo2.bar"/>
</entry>
<entry>
<id>56789</id>
<title>ipsum</title>
<link type="type2" href="https://foo4.bar"/>
<link type="type1" href="https://foo3.bar" />
</entry>
</feed>
I want to select the content of the href attribute from a link with certain type. (note that type 1 is not always the first link)
Part of the code that works:
for($i=0; $i<=5; $i++) {
foreach($xml->entry[$i]->link as $a) {
if($a["type"] == "type2")
$link = (string)($a["href"]);
}
}
However, I wonder if there is a faster and more elegant solution to this that does not require a foreach loop. Any ideas?
Have you tried using xpath? http://php.net/manual/en/simplexmlelement.xpath.php
This will allow you to do a search for nodes with the specified tag/attribute.
$nodes = $xml->xpath('//link[#type="type2"]');
foreach ($node in $nodes)
{
$link = $node['href'];
}
// Updated
You can skip the for loop if you are interested in only the first value. The xpath function returns an array of SimpleXmlElement objects so you can use index 0 to retrieve the first element, and then it's property.
Note - If the element is missing or cannot be found, the xpath element will return false, and the below code will error. The code is for illustration only, so you should verify error checking when implementing it.
// This will work if the xml always has the required attrbiute - will error if it's missing
$link = $xml->xpath('//link['#type="type2"]')[0]['href'];
Use xpath:
$xml->xpath('//link[#type="type2"]');
More on the language at w3.org

Cross site call in ajax: responseXML undefined

I'm developing a script that performs a cross site call to a python webservice, which returns a xml.
This is the full code of the html page that executes the cross site call:
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
<script src="./jquery.xdomainajax.js"></script>
<script>
jQuery.ajax({
type: "GET",
url: "http://api.notmywebsite.net/search/parameters",
async: true,
beforeSend: function(xhr) {
xhr.withCredentials = true;
},
success: function(data) {
alert(data.responseXML);
},
error: function(err){
alert("error: " + err.status);
}
});
</script>
</head>
<body>
<div id="xmlOutput"></div>
</body>
</html>
The problem is, of course, the script isn't working:
- When I call the webservice through the browser, the xml is successfully displayed.
- I'm using WebScarab to check if the answer has any content, and it has. The HTTP response has the right headers and the expected XML response.
- I debugged the python webservice to check if a call made by my request would return the expected XML, and it did.
After some research I came across the same-origin policy, for which I started using (hope i'm using it right) the following proxy: https://github.com/padolsey/jQuery-Plugins/blob/master/cross-domain-ajax/jquery.xdomainajax.js
The webservice is also called using php, through a "simplexml_load_file($url);", which returns the expected XML response.
I tried executing this same call (but using responseText instead of responseXML) to google, and it was successful.
The xml has the following format:
<OAI-PMH xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/OAI-PMH http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd">
<record>
<header>
<identifier>oai:epimarketplace.net:empid:1066</identifier>
<datestamp>2012-06-25T14:54:01.058Z</datestamp>
<setSpec>resource</setSpec>
<downLink>http://api.epimarketplace.net/fetch/pid/empid:1066</downLink>
</header>
<metadata>
<em>
<metadataLink>http://api.epimarketplace.net/rawfetch/pid/empid:1066/datastream/EM</metadataLink>
<field name="PID">empid:1066</field>
<field name="em.dateSubmitted">2012-08-20T10:43:40Z</field>
<field name="em.generalDescription.subject">Behaviour</field>
<field name="em.generalDescription.type">Dataset harvested from social networks</field>
<field name="em.title">&#60&#115&#99&#114&#105&#112&#116&#62&#88&#83&#83&#60&#47&#115&#99&#114&#105&#112&#116&#62&#10</field>
<field name="em.uploader.name">Tiago André Posse</field>
<field name="isCollection_b">false</field>
<field name="nComments">0</field>
<field name="nLikes">0</field>
</em>
</metadata>
</record>
</OAI-PMH>
Hoping you can help me,
Thanks in advance.
In order for the browser to correctly return an XML object using responseXML, you must ensure the following:
1.Your XML document is well formed( responseXML will always return null if not)
2.In Firefox, call request.overrideMimeType('text/xml') at the start of your Ajax request (upon instantiation) to explicitly tell the browser that the returned data will have a content type of "text/xml".
3.IE doesn't support the client side overrideMimeType() method, so you must ensure that your server returns the proper "text/xml" content header type for the XML file that is being returned.
NOTE: If your XML file is named with an extension of ".xml", most servers by default send out the proper "text/xml" headers, though if it is not, you'll want to modify your server settings to do so. See "XML documents and the Content-type pitfall in IE" for more info.
If any one of the above conditions are not met, the data returned will be as plain text, not an XML object as expected.
EDIT: I looked into your JQuery Ajax call(again) and guess what?! The responseXML will always be undefined, as you are trying to access it even before the AJAX request has completed. My solution would be to call the function under complete attribute instead of success.

Html tags in xml (rss)

Followed http://damieng.com/blog/2010/04/26/creating-rss-feeds-in-asp-net-mvc to create RSS for my blog. Everything fine except html tags in xml document. Typical problem:
<br />
insted of
<br />
Normally I would use
#HtmlRaw()
or
MvcHtmlString()
But how can I fix it in XML document created with SyndicationFeed?
Edit:
Ok, I'm starting to think that my question is pointless.
Should I just leave my RSS as it is?
With the XML element, you can wrap the text with your HTML in it in as a CDATA section:
<![CDATA[
your html
]]>
I don't recommend doing that, however.
wrap the text in side the CDATA
var xml= '<person><name><![CDATA[<h1>john smith</h1>]]></name></person>',
xmlDoc = $.parseXML( xml ),
$xml = $( xmlDoc ),
$title = $xml.find( "name" );
$($title.text()).appendTo("body");
DEMO

Firefox Extension, get a reference to the loaded document using browser.contentDocument

I want to write a simple firefox extension.
How can I get a reference to the loaded document object in a browser window? For example, how can I access the document in this html page loaded here on stackoverflow? According to the vague mozilla development center I can use browser.contentDocument, but it is not working for me.
<?xml version="1.0"?>
<overlay id="sample"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/x-javascript">
function change(){
//var doc = document.getElementsByTagName("browser")[0].contentDocument;
//var doc = document.browser.contentDocument;
var doc = browser.contentDocument;
var body = doc.getElementsByTagName("body")[0];
var text = doc.createTextNode("blah");
body.appendChild(text);
}
</script>
<statusbar id="status-bar">
<statusbarpanel id="my-panel" label="click me" onclick="change();" />
</statusbar>
</overlay>
I'm not sure where you read that the variable is browser.contentDocument, since it's listed in several spots to be content.document (but I'm linking to the FAQ).
Variable for accessing content of current displayed document: window.content.document

Resources