Zoho.com is a set of web applications - for communication, collaboration, and documents management (documents, sheets, presentations/slides). There are two APIs available for those apps - Data API used on documents and data stored on Zoho apps, that you use, and Remote API that is used on documents stored on your server. The Data API allows more collaboration, and getting precise sets of data (look at CloudSQL). Remote API allows you to use Zoho editors (Writer, Sheet, Show and others) to edit documents (doc, odt, ppt, odp, xls, ods and many more) on your site. More on api.wiki.zoho.com.
In this article we will use the Zoho Remo API to implement document edition in a Django powered website. To use a remote API call you have to POST a multi-part request to the API url containing the file content, and required data. In Python the best library for this task is
pycurl. Here is an example how to "open" a document in the Zoho editor:
import pycurl
apikey = 'ZOHO API KEY'
class Bodyfetcher:
def __init__(self):
self.contents = ''
def body_callback(self, buf):
self.contents = self.contents + buf
t = Bodyfetcher()
c = pycurl.Curl()
c.setopt(c.POST, 1)
# send the request to the API URL
c.setopt(c.URL, "http://export.writer.zoho.com/remotedoc.im?apikey=%s&output=editor" % apikey)
# set the required data
c.setopt(c.HTTPPOST, [("content", (c.FORM_FILE, "a.odt")), ("filename", 'a.odt'), ("id", '13'), ("format", 'odt'), ("saveurl", 'http://localhost/')])
c.setopt(c.WRITEFUNCTION, t.body_callback)
c.perform()
c.close()
# JS that will show the editor (if displayed in a browser)
print t.contents
On success the API will return a JavaScript code that will open the Zoho Writer editor in the browser with the document.
content is the document content,
filename is the document filename,
id is a unique ID used to identify the file (on saves for example),
format - format to which the document will be saved,
saveurl - URL of a page on you website that will handle saving new versions of files (of course it won't work for localhost). You will find full docs on the wiki:
Show (slides),
Writer i
Sheet.
You can
download the full app and play with it. Just edit settings.py and set you Zoho API key, site_media, then syncdb and add some documents in the Admin Panel.
To edit documents we have organise them in some way. We can use a Django model like this one:
class Document(models.Model):
title = models.CharField(max_length=255, verbose_name='Title')
description = models.TextField(verbose_name='Description')
file = models.FilePathField(verbose_name='File', blank=True, null=True, path=settings.MEDIA_ROOT + 'documents/')
doctype = models.CharField(max_length=10, verbose_name='Type', choices=[('doc', 'Document'), ('sheet', 'Sheet'), ('slide', 'Slides')])
class Meta:
verbose_name = 'Document'
verbose_name_plural = 'Documents'
def __unicode__(self):
return self.title
We split documents into 3 groups using
doctype (as Zoho gives 3 different apps for docs, sheets and slides). When we have a model and some documents in it we can start with views. The first thing we can do is a view that lists documents:
#!/usr/bin/python
import pycurl
from django.shortcuts import render_to_response
from django.conf import settings
from django.template import RequestContext
from django.http import HttpResponseRedirect,HttpResponse
from django.views.generic.list_detail import object_list
from documents.models import *
def show_index(request):
"""
List all documents
"""
return object_list(
request,
Document.objects.all(),
paginate_by = 50,
allow_empty = True,
template_name = 'show_index.html',
extra_context = {})
In case of Zoho Writer API we can open the document in read-only preview, in editor, or embed the editor in an iframe on out web page. That output setting is controlled by variable in GET
output of the POST url. Here are views for preview, edit and embedded edit:
class Bodyfetcher:
def __init__(self):
self.contents = ''
def body_callback(self, buf):
self.contents = self.contents + buf
def view_doc(request, did):
"""
Display the document in read-only
"""
doc = Document.objects.get(id=did)
ext = str(doc.file.split('.')[-1])
fname = str(doc.file.split('/')[-1])
t = Bodyfetcher()
c = pycurl.Curl()
c.setopt(c.POST, 1)
c.setopt(c.URL, "http://export.writer.zoho.com/remotedoc.im?apikey=%s&output=view" % settings.APIKEY)
c.setopt(c.HTTPPOST, [("content", (c.FORM_FILE, str(doc.file))), ("filename", fname), ("id", str(doc.id)), ("format", ext)])
c.setopt(c.VERBOSE, 1)
c.setopt(c.WRITEFUNCTION, t.body_callback)
c.perform()
c.close()
zoho = t.contents
return render_to_response(
'view_doc.html',
{'doc': doc, 'zoho': zoho},
context_instance=RequestContext(request))
def edit_doc(request, did):
"""
Display the document in Zoho editor
"""
doc = Document.objects.get(id=did)
ext = str(doc.file.split('.')[-1])
fname = str(doc.file.split('/')[-1])
t = Bodyfetcher()
c = pycurl.Curl()
c.setopt(c.POST, 1)
c.setopt(c.URL, "http://export.writer.zoho.com/remotedoc.im?apikey=%s&output=editor" % settings.APIKEY)
c.setopt(c.HTTPPOST, [("content", (c.FORM_FILE, str(doc.file))), ("filename", fname), ("id", str(doc.id)), ("format", ext), ("saveurl", 'http://localhost:8080/doc/save_file/')])
c.setopt(c.WRITEFUNCTION, t.body_callback)
c.perform()
c.close()
zoho = t.contents
return render_to_response(
'edit_doc.html',
{'doc': doc, 'zoho': zoho},
context_instance=RequestContext(request))
def edit_doc_in_frame(request, did):
"""
Show the document in editor embeded in an iframe
"""
doc = Document.objects.get(id=did)
ext = str(doc.file.split('.')[-1])
fname = str(doc.file.split('/')[-1])
t = Bodyfetcher()
c = pycurl.Curl()
c.setopt(c.POST, 1)
c.setopt(c.URL, "http://export.writer.zoho.com/remotedoc.im?apikey=%s&output=id" % settings.APIKEY)
c.setopt(c.HTTPPOST, [("content", (c.FORM_FILE, str(doc.file))), ("filename", fname), ("id", str(doc.id)), ("format", ext), ("saveurl", 'http://localhost:8080/doc/save_file/')])
c.setopt(c.WRITEFUNCTION, t.body_callback)
c.perform()
c.close()
zoho = t.contents
return render_to_response(
'edit_doc_in_frame.html',
{'doc': doc, 'zoho': zoho},
context_instance=RequestContext(request))
All views POST the data to Zoho API and then pass the result to the template. For
view_doc and
edit_doc the output will be a JS code that will redirect to the Zoho Editor. API for
edit_doc_in_frame will return a key which can be used to open the editor in for example iframe.
The
saveurl POST variable has the URL that will handle file saving from zoho editor. The editor sends the file content, the ID, and the filename. We can use simple Django view to handle the incoming data:
def save_file(request):
"""
Save POSTed file
"""
if request.POST:
data = request.POST.copy()
doc = Document.objects.get(id=data['id'])
ext = str(doc.file.split('.')[-1])
fname = str(doc.file.split('/')[-1])
if fname == data['filename']:
#ok
# referer check would be recommended here etc.
print 'saving file'
content = request.FILES['content'].read()
f = open(str(doc.file), 'wb')
f.write(content)
f.close()
return HttpResponse('ok')
The view gets the data and if the filename is ok - saves the document.
Remote API for Zoho Sheet and Zoho Show is more simple. You can use the editor, but the API won't return the JS code. Instead you will get the editor URL in response headers (Location header). To handle that we have to use pycurl slightly different:
class Headerfetcher:
def __init__(self):
self.contents = ''
def body_callback(self, buf):
self.contents = self.contents + buf
def edit_sheet(request, did, inframe=False):
"""
Display the sheet in Zoho editor
"""
doc = Document.objects.get(id=did)
ext = str(doc.file.split('.')[-1])
fname = str(doc.file.split('/')[-1])
t = Headerfetcher()
c = pycurl.Curl()
c.setopt(c.POST, 1)
c.setopt(c.URL, "http://sheet.zoho.com/remotedoc.im?apikey=%s&output=editor" % settings.APIKEY)
c.setopt(c.HTTPPOST, [("content", (c.FORM_FILE, str(doc.file))), ("filename", fname), ("id", str(doc.id)), ("format", ext), ("saveurl", 'http://localhost:8080/doc/save_file/')])
c.setopt(c.HEADERFUNCTION, t.body_callback)
c.perform()
c.close()
x = t.contents.split('\n')
for i in x:
if i.startswith('Location: '):
zoho = i.replace('Location: ', '').strip()
if inframe:
return render_to_response(
'edit_sheet_in_frame.html',
{'doc': doc, 'zoho': zoho},
context_instance=RequestContext(request))
else:
return HttpResponseRedirect(zoho)
return HttpResponse('ZohoError :(')
Instead of getting the response body we now collect response headers, and if there is "Location" header - we extract the URL and use it to show the editor in an iframe:
The Data API won't let us use the editors on our websites, but it allows us to manipulate files and data stored in the zoho apps we use. The most interesting for app-making is the data stored in Zoho Reports (and Sheets). Using CloudSQL we can query data saved in sheets and process it in out apps (make reports, whatever). You can check Get Started with Zoho CloudSQL in 4 easy steps.
Here is an example CloudSQL usage with the Python library from Zoho, that maps most of the SQL to objects:
from com.adventnet.zoho.client.report.python.ReportClient import ReportClient
APIKEY = 'YOUR KEY'
LOGINNAME = 'YOUR LOGIN'
PASSWORD = 'YOUR PASSWORD'
DATABASENAME = 'DB NAME'
TABLENAME = 'TABLE NAME'
rc = ReportClient(APIKEY)
rc.login(LOGINNAME,PASSWORD)
uri = rc.getURI(LOGINNAME,DATABASENAME,TABLENAME)
export = open('export.csv', 'w')
rc.exportData(uri, 'csv', export)
#rc.exportData(uri, 'csv', export, "Firma = 'Firma A' AND Zysk = 400")
print 'Done'
You can get the database and table name from the Reports app:
On those screen shots the database is "test", and table "testowy" (is a sheet). As for the Python API - you will find docs in the package.
exportData is used to get data from Reports in a selected format. It takes - "link" to the table, the data format, file object and optional - filter parameters. You can also use plain API calling URL with urllib2:
Executing a SQL Query.
Database is a "database" of reports in Zoho Reports.
Table is a sheet in the Reports app (a database table rendered as a sheet).
column is a sheet column (and the named as you set the column name in the sheet).
- Added: 03.10.2009 by riklaunim