source: products/quintagroup.transmogrifier/trunk/quintagroup/transmogrifier/binary.py @ 1589

Last change on this file since 1589 was 1446, checked in by piv, 14 years ago

merge from plone 2.1 branch: r2465-2483

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