The Resource object in shared/dav.py is now able to send SOAP requests, with marshalled and unmarshalled Python objects.
This commit is contained in:
		
							parent
							
								
									ca6dd26906
								
							
						
					
					
						commit
						3d87036f85
					
				
					 5 changed files with 206 additions and 106 deletions
				
			
		
							
								
								
									
										25
									
								
								__init__.py
									
										
									
									
									
								
							
							
						
						
									
										25
									
								
								__init__.py
									
										
									
									
									
								
							| 
						 | 
					@ -1,5 +1,9 @@
 | 
				
			||||||
 | 
					'''Appy allows you to create easily complete applications in Python.'''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
import os.path
 | 
					import os.path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
def getPath(): return os.path.dirname(__file__)
 | 
					def getPath(): return os.path.dirname(__file__)
 | 
				
			||||||
def versionIsGreaterThanOrEquals(version):
 | 
					def versionIsGreaterThanOrEquals(version):
 | 
				
			||||||
    '''This method returns True if the current Appy version is greater than or
 | 
					    '''This method returns True if the current Appy version is greater than or
 | 
				
			||||||
| 
						 | 
					@ -12,3 +16,24 @@ def versionIsGreaterThanOrEquals(version):
 | 
				
			||||||
        paramVersion = [int(i) for i in version.split('.')]
 | 
					        paramVersion = [int(i) for i in version.split('.')]
 | 
				
			||||||
        currentVersion = [int(i) for i in appy.version.short.split('.')]
 | 
					        currentVersion = [int(i) for i in appy.version.short.split('.')]
 | 
				
			||||||
        return currentVersion >= paramVersion
 | 
					        return currentVersion >= paramVersion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					class Object:
 | 
				
			||||||
 | 
					    '''At every place we need an object, but without any requirement on its
 | 
				
			||||||
 | 
					       class (methods, attributes,...) we will use this minimalist class.'''
 | 
				
			||||||
 | 
					    def __init__(self, **fields):
 | 
				
			||||||
 | 
					        for k, v in fields.iteritems():
 | 
				
			||||||
 | 
					            setattr(self, k, v)
 | 
				
			||||||
 | 
					    def __repr__(self):
 | 
				
			||||||
 | 
					        res = u'<Object '
 | 
				
			||||||
 | 
					        for attrName, attrValue in self.__dict__.iteritems():
 | 
				
			||||||
 | 
					            v = attrValue
 | 
				
			||||||
 | 
					            if hasattr(v, '__repr__'):
 | 
				
			||||||
 | 
					                v = v.__repr__()
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                res += u'%s=%s ' % (attrName, v)
 | 
				
			||||||
 | 
					            except UnicodeDecodeError:
 | 
				
			||||||
 | 
					                res += u'%s=<encoding problem> ' % attrName
 | 
				
			||||||
 | 
					        res  = res.strip() + '>'
 | 
				
			||||||
 | 
					        return res.encode('utf-8')
 | 
				
			||||||
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,25 +21,6 @@ mimeTypesExts = {
 | 
				
			||||||
xmlPrologue = '<?xml version="1.0" encoding="utf-8" ?>\n'
 | 
					xmlPrologue = '<?xml version="1.0" encoding="utf-8" ?>\n'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
class UnmarshalledObject:
 | 
					 | 
				
			||||||
    '''Used for producing objects from a marshalled Python object (in some files
 | 
					 | 
				
			||||||
       like a CSV file or an XML file).'''
 | 
					 | 
				
			||||||
    def __init__(self, **fields):
 | 
					 | 
				
			||||||
        for k, v in fields.iteritems():
 | 
					 | 
				
			||||||
            setattr(self, k, v)
 | 
					 | 
				
			||||||
    def __repr__(self):
 | 
					 | 
				
			||||||
        res = u'<PythonObject '
 | 
					 | 
				
			||||||
        for attrName, attrValue in self.__dict__.iteritems():
 | 
					 | 
				
			||||||
            v = attrValue
 | 
					 | 
				
			||||||
            if hasattr(v, '__repr__'):
 | 
					 | 
				
			||||||
                v = v.__repr__()
 | 
					 | 
				
			||||||
            try:
 | 
					 | 
				
			||||||
                res += u'%s = %s ' % (attrName, v)
 | 
					 | 
				
			||||||
            except UnicodeDecodeError:
 | 
					 | 
				
			||||||
                res += u'%s = <encoding problem> ' % attrName
 | 
					 | 
				
			||||||
        res  = res.strip() + '>'
 | 
					 | 
				
			||||||
        return res.encode('utf-8')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class UnmarshalledFile:
 | 
					class UnmarshalledFile:
 | 
				
			||||||
    '''Used for producing file objects from a marshalled Python object.'''
 | 
					    '''Used for producing file objects from a marshalled Python object.'''
 | 
				
			||||||
    def __init__(self):
 | 
					    def __init__(self):
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,7 @@
 | 
				
			||||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,USA.
 | 
					# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,USA.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
from appy.shared import UnmarshalledObject
 | 
					from appy import Object
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
WRONG_LINE = 'Line number %d in file %s does not have the right number of ' \
 | 
					WRONG_LINE = 'Line number %d in file %s does not have the right number of ' \
 | 
				
			||||||
| 
						 | 
					@ -61,9 +61,9 @@ class CsvParser:
 | 
				
			||||||
        # list): string, integer, float, boolean.
 | 
					        # list): string, integer, float, boolean.
 | 
				
			||||||
        self.references = references
 | 
					        self.references = references
 | 
				
			||||||
        self.klass = klass # If a klass is given here, instead of creating
 | 
					        self.klass = klass # If a klass is given here, instead of creating
 | 
				
			||||||
        # UnmarshalledObject instances we will create instances of this class.
 | 
					        # Object instances we will create instances of this class.
 | 
				
			||||||
        # But be careful: we will not call the constructor of this class. We
 | 
					        # But be careful: we will not call the constructor of this class. We
 | 
				
			||||||
        # will simply create instances of UnmarshalledObject and dynamically
 | 
					        # will simply create instances of Object and dynamically
 | 
				
			||||||
        # change the class of created instances to this class.
 | 
					        # change the class of created instances to this class.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def identifySeparator(self, line):
 | 
					    def identifySeparator(self, line):
 | 
				
			||||||
| 
						 | 
					@ -125,10 +125,10 @@ class CsvParser:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def parse(self):
 | 
					    def parse(self):
 | 
				
			||||||
        '''Parses the CSV file named self.fileName and creates a list of
 | 
					        '''Parses the CSV file named self.fileName and creates a list of
 | 
				
			||||||
           corresponding Python objects (UnmarshalledObject instances). Among
 | 
					           corresponding Python objects (Object instances). Among object fields,
 | 
				
			||||||
           object fields, some may be references. If it is the case, you may
 | 
					           some may be references. If it is the case, you may specify in
 | 
				
			||||||
           specify in p_references a dict of referred objects. The parser will
 | 
					           p_references a dict of referred objects. The parser will then
 | 
				
			||||||
           then replace string values of some fields (which are supposed to be
 | 
					           replace string values of some fields (which are supposed to be
 | 
				
			||||||
           ids of referred objects) with corresponding objects in p_references.
 | 
					           ids of referred objects) with corresponding objects in p_references.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
           How does this work? p_references must be a dictionary:
 | 
					           How does this work? p_references must be a dictionary:
 | 
				
			||||||
| 
						 | 
					@ -154,7 +154,7 @@ class CsvParser:
 | 
				
			||||||
                firstLine = False
 | 
					                firstLine = False
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                # Add an object corresponding to this line.
 | 
					                # Add an object corresponding to this line.
 | 
				
			||||||
                lineObject = UnmarshalledObject()
 | 
					                lineObject = Object()
 | 
				
			||||||
                if self.klass:
 | 
					                if self.klass:
 | 
				
			||||||
                    lineObject.__class__ = self.klass
 | 
					                    lineObject.__class__ = self.klass
 | 
				
			||||||
                i = -1
 | 
					                i = -1
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										102
									
								
								shared/dav.py
									
										
									
									
									
								
							
							
						
						
									
										102
									
								
								shared/dav.py
									
										
									
									
									
								
							| 
						 | 
					@ -4,11 +4,13 @@ from urllib import quote
 | 
				
			||||||
from StringIO import StringIO
 | 
					from StringIO import StringIO
 | 
				
			||||||
from mimetypes import guess_type
 | 
					from mimetypes import guess_type
 | 
				
			||||||
from base64 import encodestring
 | 
					from base64 import encodestring
 | 
				
			||||||
 | 
					from appy import Object
 | 
				
			||||||
from appy.shared.utils import copyData
 | 
					from appy.shared.utils import copyData
 | 
				
			||||||
from appy.gen.utils import sequenceTypes
 | 
					from appy.gen.utils import sequenceTypes
 | 
				
			||||||
 | 
					from appy.shared.xml_parser import XmlUnmarshaller, XmlMarshaller
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
class DataEncoder:
 | 
					class FormDataEncoder:
 | 
				
			||||||
    '''Allows to encode form data for sending it through a HTTP request.'''
 | 
					    '''Allows to encode form data for sending it through a HTTP request.'''
 | 
				
			||||||
    def __init__(self, data):
 | 
					    def __init__(self, data):
 | 
				
			||||||
        self.data = data # The data to encode, as a dict
 | 
					        self.data = data # The data to encode, as a dict
 | 
				
			||||||
| 
						 | 
					@ -34,6 +36,33 @@ class DataEncoder:
 | 
				
			||||||
            res.append(self.marshalValue(name, value))
 | 
					            res.append(self.marshalValue(name, value))
 | 
				
			||||||
        return '&'.join(res)
 | 
					        return '&'.join(res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					class SoapDataEncoder:
 | 
				
			||||||
 | 
					    '''Allows to encode SOAP data for sending it through a HTTP request.'''
 | 
				
			||||||
 | 
					    namespaces = {'SOAP-ENV': 'http://schemas.xmlsoap.org/soap/envelope/',
 | 
				
			||||||
 | 
					                  'xsd'     : 'http://www.w3.org/2001/XMLSchema',
 | 
				
			||||||
 | 
					                  'xsi'     : 'http://www.w3.org/2001/XMLSchema-instance'}
 | 
				
			||||||
 | 
					    namespacedTags = {'Envelope': 'SOAP-ENV', 'Body': 'SOAP-ENV', '*': 'py'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, data, namespace='http://appyframework.org'):
 | 
				
			||||||
 | 
					        self.data = data
 | 
				
			||||||
 | 
					        # p_data can be:
 | 
				
			||||||
 | 
					        # - a string already containing a complete SOAP message
 | 
				
			||||||
 | 
					        # - a Python object, that we will convert to a SOAP message
 | 
				
			||||||
 | 
					        # Define the namespaces for this request
 | 
				
			||||||
 | 
					        self.ns = self.namespaces.copy()
 | 
				
			||||||
 | 
					        self.ns['py'] = namespace
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def encode(self):
 | 
				
			||||||
 | 
					        # Do nothing if we have a SOAP message already
 | 
				
			||||||
 | 
					        if isinstance(self.data, basestring): return self.data
 | 
				
			||||||
 | 
					        # self.data is here a Python object. Wrap it a SOAP Body.
 | 
				
			||||||
 | 
					        soap = Object(Body=self.data)
 | 
				
			||||||
 | 
					        # Marshall it.
 | 
				
			||||||
 | 
					        marshaller = XmlMarshaller(rootTag='Envelope', namespaces=self.ns,
 | 
				
			||||||
 | 
					                                   namespacedTags=self.namespacedTags)
 | 
				
			||||||
 | 
					        return marshaller.marshall(soap)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
class HttpResponse:
 | 
					class HttpResponse:
 | 
				
			||||||
    '''Stores information about a HTTP response.'''
 | 
					    '''Stores information about a HTTP response.'''
 | 
				
			||||||
| 
						 | 
					@ -42,13 +71,13 @@ class HttpResponse:
 | 
				
			||||||
        self.text = text # Textual description of the code
 | 
					        self.text = text # Textual description of the code
 | 
				
			||||||
        self.headers = headers # A dict-like object containing the headers
 | 
					        self.headers = headers # A dict-like object containing the headers
 | 
				
			||||||
        self.body = body # The body of the HTTP response
 | 
					        self.body = body # The body of the HTTP response
 | 
				
			||||||
 | 
					        # p_duration, if given, is the time, in seconds, we have waited, before
 | 
				
			||||||
 | 
					        # getting this response after having sent the request.
 | 
				
			||||||
 | 
					        self.duration = duration
 | 
				
			||||||
        # The following attribute may contain specific data extracted from
 | 
					        # The following attribute may contain specific data extracted from
 | 
				
			||||||
        # the previous fields. For example, when response if 302 (Redirect),
 | 
					        # the previous fields. For example, when response if 302 (Redirect),
 | 
				
			||||||
        # self.data contains the URI where we must redirect the user to.
 | 
					        # self.data contains the URI where we must redirect the user to.
 | 
				
			||||||
        self.data = self.extractData()
 | 
					        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):
 | 
					    def __repr__(self):
 | 
				
			||||||
        duration = ''
 | 
					        duration = ''
 | 
				
			||||||
| 
						 | 
					@ -58,9 +87,15 @@ class HttpResponse:
 | 
				
			||||||
    def extractData(self):
 | 
					    def extractData(self):
 | 
				
			||||||
        '''This method extracts, from the various parts of the HTTP response,
 | 
					        '''This method extracts, from the various parts of the HTTP response,
 | 
				
			||||||
           some useful information. For example, it will find the URI where to
 | 
					           some useful information. For example, it will find the URI where to
 | 
				
			||||||
           redirect the user to if self.code is 302.'''
 | 
					           redirect the user to if self.code is 302, or will unmarshall XML
 | 
				
			||||||
 | 
					           data into Python objects.'''
 | 
				
			||||||
        if self.code == 302:
 | 
					        if self.code == 302:
 | 
				
			||||||
            return urlparse.urlparse(self.headers['location'])[2]
 | 
					            return urlparse.urlparse(self.headers['location'])[2]
 | 
				
			||||||
 | 
					        elif self.headers.has_key('content-type') and \
 | 
				
			||||||
 | 
					             self.headers['content-type'].startswith('text/xml'):
 | 
				
			||||||
 | 
					            # Return an unmarshalled version of the XML content, for easy use
 | 
				
			||||||
 | 
					            # in Python.
 | 
				
			||||||
 | 
					            return XmlUnmarshaller().parse(self.body)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
urlRex = re.compile(r'http://([^:/]+)(:[0-9]+)?(/.+)?', re.I)
 | 
					urlRex = re.compile(r'http://([^:/]+)(:[0-9]+)?(/.+)?', re.I)
 | 
				
			||||||
| 
						 | 
					@ -109,7 +144,7 @@ class Resource:
 | 
				
			||||||
        headers['Accept'] = '*/*'
 | 
					        headers['Accept'] = '*/*'
 | 
				
			||||||
        return headers
 | 
					        return headers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def sendRequest(self, method, uri, body=None, headers={}, bodyType=None):
 | 
					    def send(self, method, uri, body=None, headers={}, bodyType=None):
 | 
				
			||||||
        '''Sends a HTTP request with p_method, for p_uri.'''
 | 
					        '''Sends a HTTP request with p_method, for p_uri.'''
 | 
				
			||||||
        conn = httplib.HTTP()
 | 
					        conn = httplib.HTTP()
 | 
				
			||||||
        conn.connect(self.host, self.port)
 | 
					        conn.connect(self.host, self.port)
 | 
				
			||||||
| 
						 | 
					@ -143,13 +178,13 @@ class Resource:
 | 
				
			||||||
        #body = '<d:propertyupdate xmlns:d="DAV:"><d:set><d:prop>' \
 | 
					        #body = '<d:propertyupdate xmlns:d="DAV:"><d:set><d:prop>' \
 | 
				
			||||||
        #       '<d:displayname>%s</d:displayname></d:prop></d:set>' \
 | 
					        #       '<d:displayname>%s</d:displayname></d:prop></d:set>' \
 | 
				
			||||||
        #       '</d:propertyupdate>' % name
 | 
					        #       '</d:propertyupdate>' % name
 | 
				
			||||||
        return self.sendRequest('MKCOL', folderUri)
 | 
					        return self.send('MKCOL', folderUri)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def delete(self, name):
 | 
					    def delete(self, name):
 | 
				
			||||||
        '''Deletes a file or a folder (and all contained files if any) named
 | 
					        '''Deletes a file or a folder (and all contained files if any) named
 | 
				
			||||||
           p_name within this resource.'''
 | 
					           p_name within this resource.'''
 | 
				
			||||||
        toDeleteUri = self.uri + '/' + name
 | 
					        toDeleteUri = self.uri + '/' + name
 | 
				
			||||||
        return self.sendRequest('DELETE', toDeleteUri)
 | 
					        return self.send('DELETE', toDeleteUri)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def add(self, content, type='fileName', name=''):
 | 
					    def add(self, content, type='fileName', name=''):
 | 
				
			||||||
        '''Adds a file in this resource. p_type can be:
 | 
					        '''Adds a file in this resource. p_type can be:
 | 
				
			||||||
| 
						 | 
					@ -178,45 +213,44 @@ class Resource:
 | 
				
			||||||
        headers = {'Content-Length': str(size)}
 | 
					        headers = {'Content-Length': str(size)}
 | 
				
			||||||
        if fileType: headers['Content-Type'] = fileType
 | 
					        if fileType: headers['Content-Type'] = fileType
 | 
				
			||||||
        if encoding: headers['Content-Encoding'] = encoding
 | 
					        if encoding: headers['Content-Encoding'] = encoding
 | 
				
			||||||
        res = self.sendRequest('PUT', fileUri, body, headers, bodyType=bodyType)
 | 
					        res = self.send('PUT', fileUri, body, headers, bodyType=bodyType)
 | 
				
			||||||
        # Close the file when relevant
 | 
					        # Close the file when relevant
 | 
				
			||||||
        if type =='fileName': body.close()
 | 
					        if type =='fileName': body.close()
 | 
				
			||||||
        return res
 | 
					        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={}):
 | 
					    def get(self, uri=None, headers={}):
 | 
				
			||||||
        '''Perform a HTTP GET on the server.'''
 | 
					        '''Perform a HTTP GET on the server.'''
 | 
				
			||||||
        if not uri: uri = self.uri
 | 
					        if not uri: uri = self.uri
 | 
				
			||||||
        return self.sendRequest('GET', uri, headers=headers)
 | 
					        return self.send('GET', uri, headers=headers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def post(self, data=None, uri=None, headers={}, type='form'):
 | 
					    def post(self, data=None, uri=None, headers={}, encode='form'):
 | 
				
			||||||
        '''Perform a HTTP POST on the server. If p_type is:
 | 
					        '''Perform a HTTP POST on the server. If p_encode is "form", p_data is
 | 
				
			||||||
           - "form", p_data is a dict representing form data that will be
 | 
					           considered to be a dict representing form data that will be
 | 
				
			||||||
             form-encoded;
 | 
					           form-encoded. Else, p_data will be considered as the ready-to-send
 | 
				
			||||||
           - "soap", p_data is a XML request that will be wrapped in a SOAP
 | 
					           body of the HTTP request.'''
 | 
				
			||||||
             message.'''
 | 
					 | 
				
			||||||
        if not uri: uri = self.uri
 | 
					        if not uri: uri = self.uri
 | 
				
			||||||
        # Prepare the data to send
 | 
					        # Prepare the data to send
 | 
				
			||||||
        if type == 'form':
 | 
					        headers['Host'] = self.host
 | 
				
			||||||
 | 
					        if encode == 'form':
 | 
				
			||||||
            # Format the form data and prepare headers
 | 
					            # Format the form data and prepare headers
 | 
				
			||||||
            body = DataEncoder(data).encode()
 | 
					            body = FormDataEncoder(data).encode()
 | 
				
			||||||
            headers['Content-Type'] = 'application/x-www-form-urlencoded'
 | 
					            headers['Content-Type'] = 'application/x-www-form-urlencoded'
 | 
				
			||||||
        elif type =='soap':
 | 
					        else:
 | 
				
			||||||
            body = data
 | 
					            body = data
 | 
				
			||||||
 | 
					        headers['Content-Length'] = str(len(body))
 | 
				
			||||||
 | 
					        return self.send('POST', uri, headers=headers, body=body)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def soap(self, data, uri=None, headers={}, namespace=None):
 | 
				
			||||||
 | 
					        '''Sends a SOAP message to this resource. p_namespace is the URL of the
 | 
				
			||||||
 | 
					           server-specific namespace.'''
 | 
				
			||||||
 | 
					        if not uri: uri = self.uri
 | 
				
			||||||
 | 
					        # Prepare the data to send
 | 
				
			||||||
 | 
					        data = SoapDataEncoder(data, namespace).encode()
 | 
				
			||||||
        headers['SOAPAction'] = self.url
 | 
					        headers['SOAPAction'] = self.url
 | 
				
			||||||
        headers['Content-Type'] = 'text/xml'
 | 
					        headers['Content-Type'] = 'text/xml'
 | 
				
			||||||
        headers['Content-Length'] = str(len(body))
 | 
					        res = self.post(data, uri, headers=headers, encode=None)
 | 
				
			||||||
        return self.sendRequest('POST', uri, headers=headers, body=body)
 | 
					        # Unwrap content from the SOAP envelope
 | 
				
			||||||
 | 
					        res.data = res.data.Body
 | 
				
			||||||
 | 
					        return res
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -140,7 +140,8 @@ class XmlParser(ContentHandler, ErrorHandler):
 | 
				
			||||||
        return self.res
 | 
					        return self.res
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# ------------------------------------------------------------------------------
 | 
					# ------------------------------------------------------------------------------
 | 
				
			||||||
from appy.shared import UnmarshalledObject, UnmarshalledFile
 | 
					from appy.shared import UnmarshalledFile
 | 
				
			||||||
 | 
					from appy import Object
 | 
				
			||||||
try:
 | 
					try:
 | 
				
			||||||
    from DateTime import DateTime
 | 
					    from DateTime import DateTime
 | 
				
			||||||
except ImportError:
 | 
					except ImportError:
 | 
				
			||||||
| 
						 | 
					@ -161,14 +162,14 @@ class XmlUnmarshaller(XmlParser):
 | 
				
			||||||
        XmlParser.__init__(self)
 | 
					        XmlParser.__init__(self)
 | 
				
			||||||
        # self.classes below is a dict whose keys are tag names and values are
 | 
					        # self.classes below is a dict whose keys are tag names and values are
 | 
				
			||||||
        # Python classes. During the unmarshalling process, when an object is
 | 
					        # Python classes. During the unmarshalling process, when an object is
 | 
				
			||||||
        # encountered, instead of creating an instance of UnmarshalledObject,
 | 
					        # encountered, instead of creating an instance of Object, we will create
 | 
				
			||||||
        # we will create an instance of the class specified in self.classes.
 | 
					        # an instance of the class specified in self.classes.
 | 
				
			||||||
        # Root tag is named "xmlPythonData" by default by the XmlMarshaller.
 | 
					        # Root tag is named "xmlPythonData" by default by the XmlMarshaller.
 | 
				
			||||||
        # This will not work if the object in the specified tag is not a
 | 
					        # This will not work if the object in the specified tag is not an
 | 
				
			||||||
        # UnmarshalledObject instance (ie it is a list or tuple or simple
 | 
					        # Object instance (ie it is a list or tuple or simple value). Note that
 | 
				
			||||||
        # value). Note that we will not call the constructor of the specified
 | 
					        # we will not call the constructor of the specified class. We will
 | 
				
			||||||
        # class. We will simply create an instance of UnmarshalledObject and
 | 
					        # simply create an instance of Objects and dynamically change the class
 | 
				
			||||||
        # dynamically change the class of the created instance to this class.
 | 
					        # of the created instance to this class.
 | 
				
			||||||
        if not isinstance(classes, dict) and classes:
 | 
					        if not isinstance(classes, dict) and classes:
 | 
				
			||||||
            # The user may only need to define a class for the root tag
 | 
					            # The user may only need to define a class for the root tag
 | 
				
			||||||
            self.classes = {'xmlPythonData': classes}
 | 
					            self.classes = {'xmlPythonData': classes}
 | 
				
			||||||
| 
						 | 
					@ -198,12 +199,14 @@ class XmlUnmarshaller(XmlParser):
 | 
				
			||||||
    def convertAttrs(self, attrs):
 | 
					    def convertAttrs(self, attrs):
 | 
				
			||||||
        '''Converts XML attrs to a dict.'''
 | 
					        '''Converts XML attrs to a dict.'''
 | 
				
			||||||
        res = {}
 | 
					        res = {}
 | 
				
			||||||
        for k, v in attrs.items(): res[str(k)] = v
 | 
					        for k, v in attrs.items():
 | 
				
			||||||
 | 
					            if ':' in k: # An attr prefixed with a namespace. Remove this.
 | 
				
			||||||
 | 
					                k = k.split(':')[-1]
 | 
				
			||||||
 | 
					            res[str(k)] = v
 | 
				
			||||||
        return res
 | 
					        return res
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def startDocument(self):
 | 
					    def startDocument(self):
 | 
				
			||||||
        self.res = None # The resulting web of Python objects
 | 
					        self.res = None # The resulting web of Python objects (Object instances)
 | 
				
			||||||
        # (UnmarshalledObject instances).
 | 
					 | 
				
			||||||
        self.env.containerStack = [] # The stack of current "containers" where
 | 
					        self.env.containerStack = [] # The stack of current "containers" where
 | 
				
			||||||
        # to store the next parsed element. A container can be a list, a tuple,
 | 
					        # to store the next parsed element. A container can be a list, a tuple,
 | 
				
			||||||
        # an object (the root object of the whole web or a sub-object).
 | 
					        # an object (the root object of the whole web or a sub-object).
 | 
				
			||||||
| 
						 | 
					@ -214,6 +217,10 @@ class XmlUnmarshaller(XmlParser):
 | 
				
			||||||
    containerTags = ('tuple', 'list', 'object', 'file')
 | 
					    containerTags = ('tuple', 'list', 'object', 'file')
 | 
				
			||||||
    numericTypes = ('bool', 'int', 'float', 'long')
 | 
					    numericTypes = ('bool', 'int', 'float', 'long')
 | 
				
			||||||
    def startElement(self, elem, attrs):
 | 
					    def startElement(self, elem, attrs):
 | 
				
			||||||
 | 
					        # Remember the name of the previous element
 | 
				
			||||||
 | 
					        previousElem = None
 | 
				
			||||||
 | 
					        if self.env.currentElem:
 | 
				
			||||||
 | 
					            previousElem = self.env.currentElem.name
 | 
				
			||||||
        e = XmlParser.startElement(self, elem, attrs)
 | 
					        e = XmlParser.startElement(self, elem, attrs)
 | 
				
			||||||
        # Determine the type of the element.
 | 
					        # Determine the type of the element.
 | 
				
			||||||
        elemType = 'unicode' # Default value
 | 
					        elemType = 'unicode' # Default value
 | 
				
			||||||
| 
						 | 
					@ -224,7 +231,7 @@ class XmlUnmarshaller(XmlParser):
 | 
				
			||||||
        if elemType in self.containerTags:
 | 
					        if elemType in self.containerTags:
 | 
				
			||||||
            # I must create a new container object.
 | 
					            # I must create a new container object.
 | 
				
			||||||
            if elemType == 'object':
 | 
					            if elemType == 'object':
 | 
				
			||||||
                newObject = UnmarshalledObject(**self.convertAttrs(attrs))
 | 
					                newObject = Object(**self.convertAttrs(attrs))
 | 
				
			||||||
            elif elemType == 'tuple': newObject = [] # Tuples become lists
 | 
					            elif elemType == 'tuple': newObject = [] # Tuples become lists
 | 
				
			||||||
            elif elemType == 'list': newObject = []
 | 
					            elif elemType == 'list': newObject = []
 | 
				
			||||||
            elif elemType == 'file':
 | 
					            elif elemType == 'file':
 | 
				
			||||||
| 
						 | 
					@ -233,20 +240,31 @@ class XmlUnmarshaller(XmlParser):
 | 
				
			||||||
                    newObject.name = attrs['name']
 | 
					                    newObject.name = attrs['name']
 | 
				
			||||||
                if attrs.has_key('mimeType'):
 | 
					                if attrs.has_key('mimeType'):
 | 
				
			||||||
                    newObject.mimeType = attrs['mimeType']
 | 
					                    newObject.mimeType = attrs['mimeType']
 | 
				
			||||||
            else: newObject = UnmarshalledObject(**self.convertAttrs(attrs))
 | 
					            else: newObject = Object(**self.convertAttrs(attrs))
 | 
				
			||||||
            # Store the value on the last container, or on the root object.
 | 
					            # Store the value on the last container, or on the root object.
 | 
				
			||||||
            self.storeValue(elem, newObject)
 | 
					            self.storeValue(elem, newObject)
 | 
				
			||||||
            # Push the new object on the container stack
 | 
					            # Push the new object on the container stack
 | 
				
			||||||
            e.containerStack.append(newObject)
 | 
					            e.containerStack.append(newObject)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
 | 
					            # If we are already parsing a basic type, it means that we were
 | 
				
			||||||
 | 
					            # wrong for our diagnotsic of the containing element: it was not
 | 
				
			||||||
 | 
					            # basic. We will make the assumption that the containing element is
 | 
				
			||||||
 | 
					            # then an object.
 | 
				
			||||||
 | 
					            if e.currentBasicType:
 | 
				
			||||||
 | 
					                # Previous elem was an object: create it on the stack.
 | 
				
			||||||
 | 
					                newObject = Object()
 | 
				
			||||||
 | 
					                self.storeValue(previousElem, newObject)
 | 
				
			||||||
 | 
					                e.containerStack.append(newObject)
 | 
				
			||||||
            e.currentBasicType = elemType
 | 
					            e.currentBasicType = elemType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def storeValue(self, name, value):
 | 
					    def storeValue(self, name, value):
 | 
				
			||||||
        '''Stores the newly parsed p_value (contained in tag p_name) on the
 | 
					        '''Stores the newly parsed p_value (contained in tag p_name) on the
 | 
				
			||||||
           current container in environment self.env.'''
 | 
					           current container in environment self.env.'''
 | 
				
			||||||
        e = self.env
 | 
					        e = self.env
 | 
				
			||||||
 | 
					        # Remove namespace prefix when relevant
 | 
				
			||||||
 | 
					        if ':' in name: name = name.split(':')[-1]
 | 
				
			||||||
        # Change the class of the value if relevant
 | 
					        # Change the class of the value if relevant
 | 
				
			||||||
        if (name in self.classes) and isinstance(value, UnmarshalledObject):
 | 
					        if (name in self.classes) and isinstance(value, Object):
 | 
				
			||||||
            value.__class__ = self.classes[name]
 | 
					            value.__class__ = self.classes[name]
 | 
				
			||||||
        # Where must I store this value?
 | 
					        # Where must I store this value?
 | 
				
			||||||
        if not e.containerStack:
 | 
					        if not e.containerStack:
 | 
				
			||||||
| 
						 | 
					@ -344,7 +362,8 @@ class XmlMarshaller:
 | 
				
			||||||
    atFiles = ('image', 'file') # Types of archetypes fields that contain files.
 | 
					    atFiles = ('image', 'file') # Types of archetypes fields that contain files.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, cdata=False, dumpUnicode=False, conversionFunctions={},
 | 
					    def __init__(self, cdata=False, dumpUnicode=False, conversionFunctions={},
 | 
				
			||||||
                 dumpXmlPrologue=True, rootTag='xmlPythonData'):
 | 
					                 dumpXmlPrologue=True, rootTag='xmlPythonData', namespaces={},
 | 
				
			||||||
 | 
					                 namespacedTags={}):
 | 
				
			||||||
        # If p_cdata is True, all string values will be dumped as XML CDATA.
 | 
					        # If p_cdata is True, all string values will be dumped as XML CDATA.
 | 
				
			||||||
        self.cdata = cdata
 | 
					        self.cdata = cdata
 | 
				
			||||||
        # If p_dumpUnicode is True, the result will be unicode.
 | 
					        # If p_dumpUnicode is True, the result will be unicode.
 | 
				
			||||||
| 
						 | 
					@ -363,6 +382,41 @@ class XmlMarshaller:
 | 
				
			||||||
        self.dumpXmlPrologue = dumpXmlPrologue
 | 
					        self.dumpXmlPrologue = dumpXmlPrologue
 | 
				
			||||||
        # The name of the root tag
 | 
					        # The name of the root tag
 | 
				
			||||||
        self.rootElementName = rootTag
 | 
					        self.rootElementName = rootTag
 | 
				
			||||||
 | 
					        # The namespaces that will be defined at the root of the XML message.
 | 
				
			||||||
 | 
					        # It is a dict whose keys are namespace prefixes and whose values are
 | 
				
			||||||
 | 
					        # namespace URLs.
 | 
				
			||||||
 | 
					        self.namespaces = namespaces
 | 
				
			||||||
 | 
					        # The following dict will tell which XML tags will get which namespace
 | 
				
			||||||
 | 
					        # prefix ({s_tagName: s_prefix}). Special optional dict entry
 | 
				
			||||||
 | 
					        # '*':s_prefix will indicate a default prefix that will be applied to
 | 
				
			||||||
 | 
					        # any tag that does not have it own key in this dict.
 | 
				
			||||||
 | 
					        self.namespacedTags = namespacedTags
 | 
				
			||||||
 | 
					        self.objectType = None # Will be given by method m_marshal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getTagName(self, name):
 | 
				
			||||||
 | 
					        '''Returns the name of tag p_name as will be dumped. It can be p_name,
 | 
				
			||||||
 | 
					           or p_name prefixed with a namespace prefix (will depend on
 | 
				
			||||||
 | 
					           self.prefixedTags).'''
 | 
				
			||||||
 | 
					        # Determine the prefix
 | 
				
			||||||
 | 
					        prefix = ''
 | 
				
			||||||
 | 
					        if name in self.namespacedTags: prefix = self.namespacedTags[name]
 | 
				
			||||||
 | 
					        elif '*' in self.namespacedTags: prefix = self.namespacedTags['*']
 | 
				
			||||||
 | 
					        if prefix: return '%s:%s' % (prefix, name)
 | 
				
			||||||
 | 
					        return name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def dumpRootTag(self, res, instance):
 | 
				
			||||||
 | 
					        '''Dumps the root tag.'''
 | 
				
			||||||
 | 
					        # Dumps the name of the tag.
 | 
				
			||||||
 | 
					        tagName = self.getTagName(self.rootElementName)
 | 
				
			||||||
 | 
					        res.write('<'); res.write(tagName)
 | 
				
			||||||
 | 
					        # Dumps namespace definitions if any
 | 
				
			||||||
 | 
					        for prefix, url in self.namespaces.iteritems():
 | 
				
			||||||
 | 
					            res.write(' xmlns:%s="%s"' % (prefix, url))
 | 
				
			||||||
 | 
					        # Dumps Appy- or Plone-specific attributed
 | 
				
			||||||
 | 
					        if self.objectType != 'popo':
 | 
				
			||||||
 | 
					            res.write(' type="object" id="%s"' % instance.UID())
 | 
				
			||||||
 | 
					        res.write('>')
 | 
				
			||||||
 | 
					        return tagName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def dumpString(self, res, s):
 | 
					    def dumpString(self, res, s):
 | 
				
			||||||
        '''Dumps a string into the result.'''
 | 
					        '''Dumps a string into the result.'''
 | 
				
			||||||
| 
						 | 
					@ -382,7 +436,8 @@ class XmlMarshaller:
 | 
				
			||||||
        if not v: return
 | 
					        if not v: return
 | 
				
			||||||
        # p_value contains the (possibly binary) content of a file. We will
 | 
					        # p_value contains the (possibly binary) content of a file. We will
 | 
				
			||||||
        # encode it in Base64, in one or several parts.
 | 
					        # encode it in Base64, in one or several parts.
 | 
				
			||||||
        res.write('<part type="base64" number="1">')
 | 
					        partTag = self.getTagName('part')
 | 
				
			||||||
 | 
					        res.write('<%s type="base64" number="1">' % partTag)
 | 
				
			||||||
        if hasattr(v, 'data'):
 | 
					        if hasattr(v, 'data'):
 | 
				
			||||||
            # The file is an Archetypes file.
 | 
					            # The file is an Archetypes file.
 | 
				
			||||||
            valueType = v.data.__class__.__name__
 | 
					            valueType = v.data.__class__.__name__
 | 
				
			||||||
| 
						 | 
					@ -393,8 +448,9 @@ class XmlMarshaller:
 | 
				
			||||||
                nextPart = v.data.next
 | 
					                nextPart = v.data.next
 | 
				
			||||||
                nextPartNumber = 2
 | 
					                nextPartNumber = 2
 | 
				
			||||||
                while nextPart:
 | 
					                while nextPart:
 | 
				
			||||||
                    res.write('</part>') # Close the previous part
 | 
					                    res.write('</%s>' % partTag) # Close the previous part
 | 
				
			||||||
                    res.write('<part type="base64" number="%d">'%nextPartNumber)
 | 
					                    res.write('<%s type="base64" number="%d">' % \
 | 
				
			||||||
 | 
					                              (partTag, nextPartNumber))
 | 
				
			||||||
                    res.write(nextPart.data.encode('base64'))
 | 
					                    res.write(nextPart.data.encode('base64'))
 | 
				
			||||||
                    nextPart = nextPart.next
 | 
					                    nextPart = nextPart.next
 | 
				
			||||||
                    nextPartNumber += 1
 | 
					                    nextPartNumber += 1
 | 
				
			||||||
| 
						 | 
					@ -402,7 +458,7 @@ class XmlMarshaller:
 | 
				
			||||||
                res.write(v.data.encode('base64'))
 | 
					                res.write(v.data.encode('base64'))
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            res.write(v.encode('base64'))
 | 
					            res.write(v.encode('base64'))
 | 
				
			||||||
        res.write('</part>')
 | 
					        res.write('</%s>' % partTag)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def dumpValue(self, res, value, fieldType, isRef=False):
 | 
					    def dumpValue(self, res, value, fieldType, isRef=False):
 | 
				
			||||||
        '''Dumps the XML version of p_value to p_res.'''
 | 
					        '''Dumps the XML version of p_value to p_res.'''
 | 
				
			||||||
| 
						 | 
					@ -432,19 +488,23 @@ class XmlMarshaller:
 | 
				
			||||||
            res.write(self.trueFalse[value])
 | 
					            res.write(self.trueFalse[value])
 | 
				
			||||||
        elif fieldType == 'object':
 | 
					        elif fieldType == 'object':
 | 
				
			||||||
            if hasattr(value, 'absolute_url'):
 | 
					            if hasattr(value, 'absolute_url'):
 | 
				
			||||||
 | 
					                # Dump the URL to the object only
 | 
				
			||||||
                res.write(value.absolute_url())
 | 
					                res.write(value.absolute_url())
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                res.write(value)
 | 
					                # Dump the entire object content
 | 
				
			||||||
            # We could dump the entire object content, too. Maybe we could add a
 | 
					                for k, v in value.__dict__.iteritems():
 | 
				
			||||||
            # parameter to the marshaller to know how to marshall objects
 | 
					                    if not k.startswith('__'):
 | 
				
			||||||
            # (produce an ID, an URL, include the entire tag but we need to take
 | 
					                        self.dumpField(res, k, v)
 | 
				
			||||||
            # care of circular references,...)
 | 
					                # Maybe we could add a parameter to the marshaller to know how
 | 
				
			||||||
 | 
					                # to marshall objects (produce an ID, an URL, include the entire
 | 
				
			||||||
 | 
					                # tag but we need to take care of circular references,...)
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            res.write(value)
 | 
					            res.write(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def dumpField(self, res, fieldName, fieldValue, fieldType='basic'):
 | 
					    def dumpField(self, res, fieldName, fieldValue, fieldType='basic'):
 | 
				
			||||||
        '''Dumps in p_res, the value of the p_field for p_instance.'''
 | 
					        '''Dumps in p_res, the value of the p_field for p_instance.'''
 | 
				
			||||||
        res.write('<'); res.write(fieldName);
 | 
					        fieldTag = self.getTagName(fieldName)
 | 
				
			||||||
 | 
					        res.write('<'); res.write(fieldTag)
 | 
				
			||||||
        # Dump the type of the field as an XML attribute
 | 
					        # Dump the type of the field as an XML attribute
 | 
				
			||||||
        fType = None # No type will mean "unicode".
 | 
					        fType = None # No type will mean "unicode".
 | 
				
			||||||
        if   fieldType == 'file':                         fType = 'file'
 | 
					        if   fieldType == 'file':                         fType = 'file'
 | 
				
			||||||
| 
						 | 
					@ -457,6 +517,7 @@ class XmlMarshaller:
 | 
				
			||||||
        elif isinstance(fieldValue, list):                fType = 'list'
 | 
					        elif isinstance(fieldValue, list):                fType = 'list'
 | 
				
			||||||
        elif fieldValue.__class__.__name__ == 'DateTime': fType = 'DateTime'
 | 
					        elif fieldValue.__class__.__name__ == 'DateTime': fType = 'DateTime'
 | 
				
			||||||
        elif self.isAnObject(fieldValue):                 fType = 'object'
 | 
					        elif self.isAnObject(fieldValue):                 fType = 'object'
 | 
				
			||||||
 | 
					        if self.objectType != 'popo':
 | 
				
			||||||
            if fType: res.write(' type="%s"' % fType)
 | 
					            if fType: res.write(' type="%s"' % fType)
 | 
				
			||||||
            # Dump other attributes if needed
 | 
					            # Dump other attributes if needed
 | 
				
			||||||
            if type(fieldValue) in self.sequenceTypes:
 | 
					            if type(fieldValue) in self.sequenceTypes:
 | 
				
			||||||
| 
						 | 
					@ -471,7 +532,7 @@ class XmlMarshaller:
 | 
				
			||||||
        res.write('>')
 | 
					        res.write('>')
 | 
				
			||||||
        # Dump the field value
 | 
					        # Dump the field value
 | 
				
			||||||
        self.dumpValue(res, fieldValue, fType, isRef=(fieldType=='ref'))
 | 
					        self.dumpValue(res, fieldValue, fType, isRef=(fieldType=='ref'))
 | 
				
			||||||
        res.write('</'); res.write(fieldName); res.write('>')
 | 
					        res.write('</'); res.write(fieldTag); res.write('>')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def isAnObject(self, instance):
 | 
					    def isAnObject(self, instance):
 | 
				
			||||||
        '''Returns True if p_instance is a class instance, False if it is a
 | 
					        '''Returns True if p_instance is a class instance, False if it is a
 | 
				
			||||||
| 
						 | 
					@ -484,6 +545,7 @@ class XmlMarshaller:
 | 
				
			||||||
            return True
 | 
					            return True
 | 
				
			||||||
        elif iType.__class__.__name__ == 'ExtensionClass':
 | 
					        elif iType.__class__.__name__ == 'ExtensionClass':
 | 
				
			||||||
            return True
 | 
					            return True
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        return False
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def marshall(self, instance, objectType='popo', conversionFunctions={}):
 | 
					    def marshall(self, instance, objectType='popo', conversionFunctions={}):
 | 
				
			||||||
| 
						 | 
					@ -494,6 +556,7 @@ class XmlMarshaller:
 | 
				
			||||||
           a Appy object, specify "appy" as p_objectType. If p_instance is not
 | 
					           a Appy object, specify "appy" as p_objectType. If p_instance is not
 | 
				
			||||||
           an instance at all, but another Python data structure or basic type,
 | 
					           an instance at all, but another Python data structure or basic type,
 | 
				
			||||||
           p_objectType is ignored.'''
 | 
					           p_objectType is ignored.'''
 | 
				
			||||||
 | 
					        self.objectType = objectType
 | 
				
			||||||
        # Call the XmlMarshaller constructor if it hasn't been called yet.
 | 
					        # Call the XmlMarshaller constructor if it hasn't been called yet.
 | 
				
			||||||
        if not hasattr(self, 'cdata'):
 | 
					        if not hasattr(self, 'cdata'):
 | 
				
			||||||
            XmlMarshaller.__init__(self)
 | 
					            XmlMarshaller.__init__(self)
 | 
				
			||||||
| 
						 | 
					@ -505,14 +568,9 @@ class XmlMarshaller:
 | 
				
			||||||
        if self.dumpXmlPrologue:
 | 
					        if self.dumpXmlPrologue:
 | 
				
			||||||
            res.write(xmlPrologue)
 | 
					            res.write(xmlPrologue)
 | 
				
			||||||
        if self.isAnObject(instance):
 | 
					        if self.isAnObject(instance):
 | 
				
			||||||
            # Determine object ID
 | 
					            # Dump the root tag
 | 
				
			||||||
            if objectType in ('archetype', 'appy'):
 | 
					            rootTagName = self.dumpRootTag(res, instance)
 | 
				
			||||||
                objectId = instance.UID() # ID in DB
 | 
					            # Dump the fields of this root object
 | 
				
			||||||
            else:
 | 
					 | 
				
			||||||
                objectId = str(id(instance)) # ID in RAM
 | 
					 | 
				
			||||||
            res.write('<'); res.write(self.rootElementName)
 | 
					 | 
				
			||||||
            res.write(' type="object" id="');res.write(objectId);res.write('">')
 | 
					 | 
				
			||||||
            # Dump the object ID and the value of the fields that must be dumped
 | 
					 | 
				
			||||||
            if objectType == 'popo':
 | 
					            if objectType == 'popo':
 | 
				
			||||||
                for fieldName, fieldValue in instance.__dict__.iteritems():
 | 
					                for fieldName, fieldValue in instance.__dict__.iteritems():
 | 
				
			||||||
                    mustDump = False
 | 
					                    mustDump = False
 | 
				
			||||||
| 
						 | 
					@ -565,18 +623,20 @@ class XmlMarshaller:
 | 
				
			||||||
                    self.dumpField(res, field.name,field.getValue(instance),
 | 
					                    self.dumpField(res, field.name,field.getValue(instance),
 | 
				
			||||||
                                   fieldType=fieldType)
 | 
					                                   fieldType=fieldType)
 | 
				
			||||||
                # Dump the object history.
 | 
					                # Dump the object history.
 | 
				
			||||||
                res.write('<history type="list">')
 | 
					                histTag = self.getTagName('history')
 | 
				
			||||||
 | 
					                eventTag = self.getTagName('event')
 | 
				
			||||||
 | 
					                res.write('<%s type="list">' % histTag)
 | 
				
			||||||
                wfInfo = instance.portal_workflow.getWorkflowsFor(instance)
 | 
					                wfInfo = instance.portal_workflow.getWorkflowsFor(instance)
 | 
				
			||||||
                if wfInfo:
 | 
					                if wfInfo:
 | 
				
			||||||
                    history = instance.workflow_history[wfInfo[0].id]
 | 
					                    history = instance.workflow_history[wfInfo[0].id]
 | 
				
			||||||
                    for event in history:
 | 
					                    for event in history:
 | 
				
			||||||
                        res.write('<event type="object">')
 | 
					                        res.write('<%s type="object">' % eventTag)
 | 
				
			||||||
                        for k, v in event.iteritems():
 | 
					                        for k, v in event.iteritems():
 | 
				
			||||||
                            self.dumpField(res, k, v)
 | 
					                            self.dumpField(res, k, v)
 | 
				
			||||||
                        res.write('</event>')
 | 
					                        res.write('</%s>' % eventTag)
 | 
				
			||||||
                res.write('</history>')
 | 
					                res.write('</%s>' % histTag)
 | 
				
			||||||
            self.marshallSpecificElements(instance, res)
 | 
					            self.marshallSpecificElements(instance, res)
 | 
				
			||||||
            res.write('</'); res.write(self.rootElementName); res.write('>')
 | 
					            res.write('</'); res.write(rootTagName); res.write('>')
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            self.dumpField(res, self.rootElementName, instance)
 | 
					            self.dumpField(res, self.rootElementName, instance)
 | 
				
			||||||
        # Return the result
 | 
					        # Return the result
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue