source: products/quintagroup.transmogrifier/branches/plone-2.1/quintagroup.transmogrifier/quintagroup/transmogrifier/binary.py @ 1413

Last change on this file since 1413 was 1389, checked in by piv, 14 years ago

fix manifest and binary export sections to generate valid xml

File size: 7.4 KB
Line 
1import traceback
2from xml.dom import minidom
3
4from zope.interface import classProvides, implements
5
6from ZODB.POSException import ConflictError
7
8from plone.app.transmogrifier.interfaces import IBaseObject
9
10from collective.transmogrifier.interfaces import ISection, ISectionBlueprint
11from collective.transmogrifier.utils import defaultMatcher
12
13class FileExporterSection(object):
14    classProvides(ISectionBlueprint)
15    implements(ISection)
16
17    def __init__(self, transmogrifier, name, options, previous):
18        self.previous = previous
19        self.context = transmogrifier.context
20
21        self.pathkey = defaultMatcher(options, 'path-key', name, 'path')
22        self.fileskey = options.get('files-key', '_files').strip()
23        # only this section can add 'excluded_field' for marshalling
24        #self.excludekey = defaultMatcher(options, 'exclude-key', name, 'excluded_fields')
25        self.excludekey = options.get('exclude-key', '_excluded_fields').strip()
26       
27        self.doc = minidom.Document()
28
29    def __iter__(self):
30        for item in self.previous:
31            pathkey = self.pathkey(*item.keys())[0]
32
33            if not pathkey:
34                yield item; continue
35
36            path = item[pathkey]
37            obj = self.context.unrestrictedTraverse(path, None)
38            if obj is None:         # path doesn't exist
39                yield item; continue
40
41            if IBaseObject.providedBy(obj):
42                schema = obj.Schema()
43                binary_fields = {}
44                binary_field_names = []
45                for field in schema.keys():
46                    if obj.isBinary(field):
47                        fname, ct, data = self.extractFile(obj, field)
48                        binary_field_names.append(field)
49                        if data == '':
50                            # empty file fields have no data and we skip them
51                            continue
52                        elif fname == '':
53                            fname = 'field-%s' % field
54
55                        binary_fields[field] = dict(filename=fname, mimetype=ct)
56                        files = item.setdefault(self.fileskey, {})
57                        #key = "field-%s" % field
58                        files[fname] = {
59                            # now we export FileField as file with it's original name,
60                            # but it may cause name collapse
61                            'name': fname,
62                            'data': data,
63                            'content_type': ct,
64                        }
65                if binary_fields:
66                    files['file-fields'] = {
67                        'name': '.file-fields.xml',
68                        'data': self.createManifest(binary_fields),
69                    }
70                if binary_field_names:
71                    item[self.excludekey] = binary_field_names
72
73            yield item
74
75    def extractFile(self, obj, field):
76        """ Return tuple of (filename, content_type, data)
77        """
78        field = obj.getField(field)
79        # temporarily:
80        # dirty call, I know, just lazy to get method arguments
81        # TextField overrided getBaseUnit method but didn't follow API
82        try:
83            base_unit = field.getBaseUnit(obj, full=True)
84        except TypeError, e:
85            base_unit = field.getBaseUnit(obj)
86        fname = base_unit.getFilename() 
87        ct = base_unit.getContentType()
88        value = base_unit.getRaw()
89
90        return fname, ct, value
91
92    def createManifest(self, binary_fields):
93        doc = self.doc
94
95        root = doc.createElement('manifest')
96        for fname, info in binary_fields.items():
97            # create field node
98            field = doc.createElement('field')
99
100            # set name attribute
101            attr = doc.createAttribute('name')
102            attr.value = fname
103            field.setAttributeNode(attr)
104
105            # create filename node
106            filename = doc.createElement('filename')
107            filename.appendChild(doc.createTextNode(info['filename']))
108            field.appendChild(filename)
109
110            # create mimetype node
111            mimetype = doc.createElement('mimetype')
112            mimetype.appendChild(doc.createTextNode(info['mimetype']))
113            field.appendChild(mimetype)
114
115            root.appendChild(field)
116
117        doc.appendChild(root)
118
119        try:
120            data = doc.toprettyxml(indent='  ', encoding='utf-8')
121        except UnicodeDecodeError, e:
122            # all comments are strings encoded in 'utf-8' and they will properly
123            # saved in xml file, but if we explicitly give 'utf-8' encoding
124            # UnicodeDecodeError will be raised when they have non-ascii chars
125            data = doc.toprettyxml(indent='  ')
126
127        doc.unlink()
128        return data
129
130
131class FileImporterSection(object):
132    classProvides(ISectionBlueprint)
133    implements(ISection)
134
135    def __init__(self, transmogrifier, name, options, previous):
136        self.previous = previous
137        self.context = transmogrifier.context
138
139        self.pathkey = defaultMatcher(options, 'path-key', name, 'path')
140        self.fileskey = defaultMatcher(options, 'files-key', name, 'files')
141        self.contextkey = defaultMatcher(options, 'context-key', name, 'import_context')
142
143    def __iter__(self):
144        for item in self.previous:
145            pathkey = self.pathkey(*item.keys())[0]
146            fileskey = self.fileskey(*item.keys())[0]
147            contextkey = self.contextkey(*item.keys())[0]
148
149            if not (pathkey and fileskey):
150                yield item; continue
151            if 'file-fields' not in item[fileskey]:
152                yield item; continue
153
154            path = item[pathkey]
155            obj = self.context.unrestrictedTraverse(path, None)
156            if obj is None:         # path doesn't exist
157                yield item; continue
158
159            if IBaseObject.providedBy(obj):
160                try:
161                    manifest = item[fileskey]['file-fields']['data']
162                    for field, info in self.parseManifest(manifest).items():
163                        fname = info['filename']
164                        ct = info['mimetype']
165                        if fname in item[fileskey]:
166                            data = item[fileskey][fname]['data']
167                        elif contextkey:
168                            data = context.readDataFile("%s/%s" % (path, fname))
169                            if data is None:
170                                continue
171                        mutator = obj.getField(field).getMutator(obj)
172                        mutator(data, filename=fname, mimetype=ct)
173                except ConflictError:
174                    raise
175                except Exception, e:
176                    print "Exception in fileimporter section:"
177                    print '-'*60
178                    traceback.print_exc()
179                    print '-'*60
180
181            yield item
182
183    def parseManifest(self, manifest):
184        doc = minidom.parseString(manifest)
185        fields = {}
186        for elem in doc.getElementsByTagName('field'):
187            field = fields.setdefault(str(elem.getAttribute('name')), {})
188            for child in elem.childNodes:
189                if child.nodeType != child.ELEMENT_NODE:
190                    continue
191                if child.tagName == u'filename':
192                    field['filename'] = child.firstChild.nodeValue.strip().encode('utf-8')
193                elif child.tagName == u'mimetype':
194                    field['mimetype'] = str(child.firstChild.nodeValue.strip())
195
196        return fields
Note: See TracBrowser for help on using the repository browser.