source: products/quintagroup.transmogrifier/trunk/quintagroup/transmogrifier/adapters/marshall.py @ 275

Last change on this file since 275 was 275, checked in by mylan, 18 years ago

Removed file/folder

File size: 5.5 KB
Line 
1from xml.dom import minidom
2
3from zope.interface import implements
4from zope.component import adapts
5
6from OFS.Image import File
7
8from Products.Archetypes import atapi
9from Products.Archetypes.interfaces import IBaseObject
10from Products.ATContentTypes.interface import IATTopicCriterion
11from Products.Marshall.registry import getComponent
12from Products.Archetypes import config as atcfg
13
14from quintagroup.transmogrifier.interfaces import IExportDataCorrector
15
16class ReferenceExporter(object):
17    """ Add reference fields to XML, generated by Marshall product.
18    """
19    implements(IExportDataCorrector)
20    adapts(IBaseObject)
21
22    def __init__(self, context):
23        self.context = context
24
25    def __call__(self, data):
26        data['data'] = self.exportReferences(data['data'])
27        return data
28
29    def exportReferences(self, xml):
30        """ Marshall 1.0.0 doesn't export references, do it manually.
31        """
32        doc = minidom.parseString(xml)
33        root = doc.documentElement
34        for fname in self.context.Schema().keys():
35            if not isinstance(self.context.Schema()[fname], atapi.ReferenceField):
36                continue
37            elem = doc.createElement("field")
38            attr = doc.createAttribute("name")
39            attr.value = fname
40            elem.setAttributeNode(attr)
41            for value in self.context[fname]:
42                ref = doc.createElement('reference')
43                uid = doc.createElement('uid')
44                value = doc.createTextNode(str(value))
45                uid.appendChild(value)
46                ref.appendChild(uid)
47                elem.appendChild(ref)
48            root.appendChild(elem)
49        return doc.toxml('utf-8')
50
51class FileExporter(ReferenceExporter):
52    """ Add file fields to XML, generated by Marshall product.
53    """
54    implements(IExportDataCorrector)
55
56    def __call__(self, data):
57        xml = data['data']
58        xml = self.normalizeBinaryField(xml)
59        data['data'] = self.exportReferences(xml)
60        return data
61
62    def normalizeBinaryField(self, data):
63        """ Rewrite 'image' or 'file' xml element data with binary data.
64
65            'data' - xml text, formed by Marshall. It's basestring and can have non-ascii characters,
66            but minidom.parseString(data) returns unicode string and we need to explicitly set
67            encoding when returning modified xml data (if not and there non-ascii characters in
68            data, UnicodeDecodeError is raised when calling doc.toxml()).
69        """
70        data, tagName = self.repairXMLData(data)
71        if tagName is None:
72            # return unchanged data
73            return data
74        doc = minidom.parseString(data)
75        root = doc.documentElement
76        # there is only one 'field' element with name attribute that is equal to 'file' or 'image'
77        elem = [field for field in root.getElementsByTagName('field') 
78                     if field.getAttribute('name') == tagName][0]
79
80        field = self.context[tagName]
81        # create 'filename' attribute (decode file name to unicode using utf-8 encoding)
82        attr = doc.createAttribute('filename')
83        attr.value = field.getFilename() #.decode('utf-8')
84        elem.setAttributeNode(attr)
85        # create 'content_type' attribute
86        attr = doc.createAttribute('content_type')
87        attr.value = field.getContentType()
88        elem.setAttributeNode(attr)
89        # create 'transfer_encoding' attribute
90        attr = doc.createAttribute('transfer_encoding')
91        attr.value = 'base64'
92        elem.setAttributeNode(attr)
93
94        # get file data and encode it in CDATA section
95        value = field.data
96        if isinstance(value, File):
97            value = getattr(value, 'data', value)
98        value = str(value).encode('base64')
99        value = doc.createCDATASection(value)
100        # remove old childs and add new
101        if elem.hasChildNodes():
102            for i in elem.childNodes:
103                elem.removeChild(i)
104        elem.appendChild(value)
105        elem.normalize()
106        return doc.toxml('utf-8')
107
108    def repairXMLData(self, data):
109        """ Find element with binary data, remove invalid data and remove element's name.
110            This element is 'file' or 'image'.
111        """
112        tagName = None
113        if data.find('<field name="file">') != -1:
114            tagName = 'file'
115        elif data.find('<field name="image">') != -1:
116            tagName = 'image'
117        else:
118            return data, tagName
119        openTag = '<field name="%s">' % tagName
120        closeTag = '</field>'
121        index1 = data.find(openTag) + len(openTag)
122        index2 = data.find(closeTag, index1)
123        out = data[:index1] + data[index2:]
124        return out, tagName
125
126class CriterionExporter(ReferenceExporter):
127    """ Special marshalling adapter for topic criterias.
128    """
129
130    implements(IExportDataCorrector)
131    adapts(IATTopicCriterion)
132
133    def __init__(self, context):
134        self.context = context
135        self.marshaller = getComponent('atxml')
136
137    def __call__(self, data):
138        if data['data'] is not None:
139            return super(CriterionExporter, self).__call__(data)
140        # Marshall fails when object has UID that is equal to None
141        # fix it here by setting it to empty string and after marshalling setting back to old value
142        old_uid_attr = getattr(self.context, atcfg.UUID_ATTR)
143        setattr(self.context, atcfg.UUID_ATTR, "")
144
145        ct, length, xml = self.marshaller.marshall(self.context)
146        xml = self.exportReferences(xml)
147        data['data'] = xml
148
149        setattr(self.context, atcfg.UUID_ATTR, old_uid_attr)
150        return data
Note: See TracBrowser for help on using the repository browser.