diff --git a/gen/plone25/skin/widgets/boolean.pt b/gen/plone25/skin/widgets/boolean.pt
index e9a475e..b486886 100644
--- a/gen/plone25/skin/widgets/boolean.pt
+++ b/gen/plone25/skin/widgets/boolean.pt
@@ -1,5 +1,7 @@
View macro for a Boolean.
-
+
+
+
Edit macro for an Boolean.
diff --git a/shared/dav.py b/shared/dav.py
index 15f5e79..963d717 100644
--- a/shared/dav.py
+++ b/shared/dav.py
@@ -1,24 +1,89 @@
# ------------------------------------------------------------------------------
-import os, re, httplib, sys, stat
+import os, re, httplib, sys, stat, urlparse, time
+from urllib import quote
from StringIO import StringIO
from mimetypes import guess_type
from base64 import encodestring
from appy.shared.utils import copyData
+from appy.gen.utils import sequenceTypes
+
+# ------------------------------------------------------------------------------
+class DataEncoder:
+ '''Allows to encode form data for sending it through a HTTP request.'''
+ def __init__(self, data):
+ self.data = data # The data to encode, as a dict
+
+ def marshalValue(self, name, value):
+ if isinstance(value, basestring):
+ return '%s=%s' % (name, quote(str(value)))
+ elif isinstance(value, float):
+ return '%s:float=%s' % (name, value)
+ elif isinstance(value, int):
+ return '%s:int=%s' % (name, value)
+ elif isinstance(value, long):
+ res = '%s:long=%s' % (name, value)
+ if res[-1] == 'L':
+ res = res[:-1]
+ return res
+ else:
+ raise 'Cannot encode value %s' % str(value)
+
+ def encode(self):
+ res = []
+ for name, value in self.data.iteritems():
+ res.append(self.marshalValue(name, value))
+ return '&'.join(res)
+
+# ------------------------------------------------------------------------------
+class HttpResponse:
+ '''Stores information about a HTTP response.'''
+ def __init__(self, code, text, headers, body, duration=None):
+ self.code = code # The return code, ie 404, 200, ...
+ self.text = text # Textual description of the code
+ self.headers = headers # A dict-like object containing the headers
+ self.body = body # The body of the HTTP response
+ # The following attribute may contain specific data extracted from
+ # the previous fields. For example, when response if 302 (Redirect),
+ # self.data contains the URI where we must redirect the user to.
+ self.data = self.extractData()
+ # p_duration, if given, is the time, in seconds, we have waited, before
+ # getting this response after having sent the request.
+ self.duration = duration
+
+ def __repr__(self):
+ duration = ''
+ if self.duration: duration = ', got in %.4f seconds' % self.duration
+ return '' % (self.code, self.text, duration)
+
+ def extractData(self):
+ '''This method extracts, from the various parts of the HTTP response,
+ some useful information. For example, it will find the URI where to
+ redirect the user to if self.code is 302.'''
+ if self.code == 302:
+ return urlparse.urlparse(self.headers['location'])[2]
# ------------------------------------------------------------------------------
urlRex = re.compile(r'http://([^:/]+)(:[0-9]+)?(/.+)?', re.I)
binaryRex = re.compile(r'[\000-\006\177-\277]')
-# ------------------------------------------------------------------------------
class Resource:
'''Every instance of this class represents some web resource accessible
through WebDAV.'''
- def __init__(self, url, username=None, password=None):
+ def __init__(self, url, username=None, password=None, measure=False):
self.username = username
self.password = password
self.url = url
-
+ # If some headers must be sent with any request sent through this
+ # resource (like a cookie), you can store them in the following dict.
+ self.headers = {}
+ # If p_measure is True, we will measure, for every request sent, the
+ # time we wait until we receive the response.
+ self.measure = measure
+ # If measure is True, we will store hereafter, the total time (in
+ # seconds) spent waiting for the server for all requests sent through
+ # this resource object.
+ self.serverTime = 0
# Split the URL into its components
res = urlRex.match(url)
if res:
@@ -50,16 +115,29 @@ class Resource:
'''Sends a HTTP request with p_method, for p_uri.'''
conn = httplib.HTTP()
conn.connect(self.host, self.port)
+ # Tell what kind of HTTP request it will be.
conn.putrequest(method, uri)
# Add HTTP headers
self.updateHeaders(headers)
+ if self.headers: headers.update(self.headers)
for n, v in headers.items(): conn.putheader(n, v)
conn.endheaders()
- if body: copyData(body, conn, 'send', type=bodyType)
- ver, code, msg = conn.getreply()
- data = conn.getfile().read()
+ # Add HTTP body
+ if body:
+ if not bodyType: bodyType = 'string'
+ copyData(body, conn, 'send', type=bodyType)
+ # Send the request, get the reply
+ if self.measure: startTime = time.time()
+ code, text, headers = conn.getreply()
+ if self.measure: endTime = time.time()
+ body = conn.getfile().read()
conn.close()
- return data
+ # Return a smart object containing the various parts of the response
+ duration = None
+ if self.measure:
+ duration = endTime - startTime
+ self.serverTime += duration
+ return HttpResponse(code, text, headers, body, duration=duration)
def mkdir(self, name):
'''Creates a folder named p_name in this resource.'''
@@ -105,4 +183,32 @@ class Resource:
res = self.sendRequest('PUT', fileUri, body, headers, bodyType=bodyType)
# Close the file when relevant
if type =='fileName': body.close()
+ return res
+
+ def _encodeFormData(self, data):
+ '''Returns the encoded form p_data.'''
+ res = []
+ for name, value in data.items():
+ n = name.rfind( '__')
+ if n > 0:
+ tag = name[n+2:]
+ key = name[:n]
+ else: tag = 'string'
+ func = varfuncs.get(tag, marshal_string)
+ res.append(func(name, value))
+ return '&'.join(res)
+
+ def get(self, uri=None, headers={}):
+ '''Perform a HTTP GET on the server.'''
+ if not uri: uri = self.uri
+ return self.sendRequest('GET', uri, headers=headers)
+
+ def post(self, data, uri=None, headers={}):
+ '''Perform a HTTP POST on the server.'''
+ if not uri: uri = self.uri
+ # Format the form data and prepare headers
+ body = DataEncoder(data).encode()
+ headers['Content-Type'] = 'application/x-www-form-urlencoded'
+ headers['Content-Length'] = str(len(body))
+ return self.sendRequest('POST', uri, headers=headers, body=body)
# ------------------------------------------------------------------------------
diff --git a/shared/utils.py b/shared/utils.py
index 36168c3..3a627a9 100644
--- a/shared/utils.py
+++ b/shared/utils.py
@@ -95,7 +95,7 @@ def copyData(data, target, targetMethod, type='string', encoding=None,
that is compatible with the content of p_data, ie file('myFile.doc','wb')
if content is binary.'''
dump = getattr(target, targetMethod)
- if type == 'string': dump(encodeData(data, encoding))
+ if not type or (type == 'string'): dump(encodeData(data, encoding))
elif type == 'file':
while True:
chunk = data.read(chunkSize)