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

Last change on this file since 1413 was 461, checked in by piv, 18 years ago

updated version.txt

File size: 9.8 KB
Line 
1from xml.dom import minidom
2
3from zope.interface import classProvides, implements
4
5from ZPublisher.HTTPRequest import default_encoding
6
7from collective.transmogrifier.interfaces import ISection, ISectionBlueprint
8from collective.transmogrifier.utils import defaultMatcher
9
10try:
11    from Products.GenericSetup.exceptions import BadRequest
12except ImportError:
13    from Products.CMFSetup.exceptions import BadRequest
14
15from interfaces import IPropertyManager
16
17class Helper(object):
18
19    """PropertyManager im- and export helpers.
20    """
21
22    _encoding = default_encoding
23
24    def _getNodeText(self, node):
25        text = ''
26        for child in node.childNodes:
27            if child.nodeName != '#text':
28                continue
29            text += child.nodeValue.strip()
30        return text
31
32    def _convertToBoolean(self, val):
33        return val.lower() in ('true', 'yes', '1')
34
35    def _extractProperties(self):
36        fragment = self._doc.createDocumentFragment()
37
38        for prop_map in self.context._propertyMap():
39            prop_id = prop_map['id']
40            if prop_id == 'i18n_domain':
41                continue
42
43            # Don't export read-only nodes
44            if 'w' not in prop_map.get('mode', 'wd'):
45                continue
46
47            node = self._doc.createElement('property')
48            node.setAttribute('name', prop_id)
49
50            prop = self.context.getProperty(prop_id)
51            if isinstance(prop, (tuple, list)):
52                for value in prop:
53                    if isinstance(value, str):
54                        value.decode(self._encoding)
55                    child = self._doc.createElement('element')
56                    child.setAttribute('value', value)
57                    node.appendChild(child)
58            else:
59                if prop_map.get('type') == 'boolean':
60                    prop = unicode(bool(prop))
61                elif isinstance(prop, str):
62                    prop = prop.decode(self._encoding)
63                elif not isinstance(prop, basestring):
64                    prop = unicode(prop)
65                child = self._doc.createTextNode(prop)
66                node.appendChild(child)
67
68            if 'd' in prop_map.get('mode', 'wd') and not prop_id == 'title':
69                prop_type = prop_map.get('type', 'string')
70                node.setAttribute('type', unicode(prop_type))
71                select_variable = prop_map.get('select_variable', None)
72                if select_variable is not None:
73                    node.setAttribute('select_variable', select_variable)
74
75            if hasattr(self, '_i18n_props') and prop_id in self._i18n_props:
76                node.setAttribute('i18n:translate', '')
77
78            fragment.appendChild(node)
79
80        return fragment
81
82    def _purgeProperties(self):
83        for prop_map in self.context._propertyMap():
84            mode = prop_map.get('mode', 'wd')
85            if 'w' not in mode:
86                continue
87            prop_id = prop_map['id']
88            if 'd' in mode and not prop_id == 'title':
89                self.context._delProperty(prop_id)
90            else:
91                prop_type = prop_map.get('type')
92                if prop_type == 'multiple selection':
93                    prop_value = ()
94                elif prop_type in ('int', 'float'):
95                    prop_value = 0
96                else:
97                    prop_value = ''
98                self.context._updateProperty(prop_id, prop_value)
99
100    def _initProperties(self, node):
101        obj = self.context
102        if node.hasAttribute('i18n:domain'):
103            i18n_domain = str(node.getAttribute('i18n:domain'))
104            obj._updateProperty('i18n_domain', i18n_domain)
105        for child in node.childNodes:
106            if child.nodeName != 'property':
107                continue
108            prop_id = str(child.getAttribute('name'))
109            prop_map = obj.propdict().get(prop_id, None)
110
111            if prop_map is None:
112                if child.hasAttribute('type'):
113                    val = str(child.getAttribute('select_variable'))
114                    prop_type = str(child.getAttribute('type'))
115                    obj._setProperty(prop_id, val, prop_type)
116                    prop_map = obj.propdict().get(prop_id, None)
117                else:
118                    raise ValueError("undefined property '%s'" % prop_id)
119
120            if not 'w' in prop_map.get('mode', 'wd'):
121                raise BadRequest('%s cannot be changed' % prop_id)
122
123            elements = []
124            for sub in child.childNodes:
125                if sub.nodeName == 'element':
126                    value = sub.getAttribute('value')
127                    elements.append(value.encode(self._encoding))
128
129            if elements or prop_map.get('type') == 'multiple selection':
130                prop_value = tuple(elements) or ()
131            elif prop_map.get('type') == 'boolean':
132                prop_value = self._convertToBoolean(self._getNodeText(child))
133            else:
134                # if we pass a *string* to _updateProperty, all other values
135                # are converted to the right type
136                prop_value = self._getNodeText(child).encode(self._encoding)
137
138            if not self._convertToBoolean(child.getAttribute('purge')
139                                          or 'True'):
140                # If the purge attribute is False, merge sequences
141                prop = obj.getProperty(prop_id)
142                if isinstance(prop, (tuple, list)):
143                    prop_value = (tuple([p for p in prop
144                                         if p not in prop_value]) +
145                                  tuple(prop_value))
146
147            obj._updateProperty(prop_id, prop_value)
148
149class PropertiesExporterSection(object):
150    classProvides(ISectionBlueprint)
151    implements(ISection)
152
153    def __init__(self, transmogrifier, name, options, previous):
154        self.previous = previous
155        self.context = transmogrifier.context
156
157        self.pathkey = defaultMatcher(options, 'path-key', name, 'path')
158        self.fileskey = options.get('files-key', '_files').strip()
159
160        self.excludekey = defaultMatcher(options, 'exclude-key', name, 'excluded_properties')
161        self.exclude = filter(None, [i.strip() for i in 
162                              options.get('exclude', '').splitlines()])
163
164        self.helper = Helper()
165        self.doc = minidom.Document()
166        self.helper._doc = self.doc
167
168    def __iter__(self):
169        helper = self.helper
170        doc = self.doc
171
172        for item in self.previous:
173            pathkey = self.pathkey(*item.keys())[0]
174
175            if not pathkey:
176                yield item; continue
177
178            path = item[pathkey]
179            obj = self.context.unrestrictedTraverse(path, None)
180            if obj is None:         # path doesn't exist
181                yield item; continue
182
183            if IPropertyManager.providedBy(obj):
184                data = None
185                excludekey = self.excludekey(*item.keys())[0]
186                excluded_props = tuple(self.exclude)
187                if excludekey:
188                    excluded_props = tuple(set(item[excludekey]) | set(excluded_props))
189
190                helper.context = obj
191                node = doc.createElement('properties')
192                for elem in helper._extractProperties().childNodes:
193                    if elem.nodeName != 'property':
194                        continue
195                    if elem.getAttribute('name') not in excluded_props:
196                        node.appendChild(elem)
197                if node.hasChildNodes():
198                    doc.appendChild(node)
199                    data = doc.toprettyxml(indent='  ', encoding='utf-8')
200                    doc.unlink()
201
202                if data:
203                    files = item.setdefault(self.fileskey, {})
204                    item[self.fileskey]['propertymanager'] = {
205                        'name': '.properties.xml',
206                        'data': data,
207                    }
208
209            yield item
210
211class PropertiesImporterSection(object):
212    classProvides(ISectionBlueprint)
213    implements(ISection)
214
215    def __init__(self, transmogrifier, name, options, previous):
216        self.previous = previous
217        self.context = transmogrifier.context
218
219        self.pathkey = defaultMatcher(options, 'path-key', name, 'path')
220        self.fileskey = defaultMatcher(options, 'files-key', name, 'files')
221
222        self.excludekey = defaultMatcher(options, 'exclude-key', name, 'excluded_properties')
223        self.exclude = filter(None, [i.strip() for i in 
224                            options.get('exclude', '').splitlines()])
225
226        self.helper = Helper()
227        self.helper._encoding = 'utf-8'
228
229    def __iter__(self):
230        helper = self.helper
231
232        for item in self.previous:
233            pathkey = self.pathkey(*item.keys())[0]
234            fileskey = self.fileskey(*item.keys())[0]
235
236            if not (pathkey and fileskey):
237                yield item; continue
238            if 'propertymanager' not in item[fileskey]:
239                yield item; continue
240
241            path = item[pathkey]
242            obj = self.context.unrestrictedTraverse(path, None)
243            if obj is None:         # path doesn't exist
244                yield item; continue
245
246            if IPropertyManager.providedBy(obj):
247                data = None
248                excludekey = self.excludekey(*item.keys())[0]
249                excluded_props = self.exclude
250                if excludekey:
251                    excluded_props = tuple(set(item[excludekey]) | set(excluded_props))
252
253                data = item[fileskey]['propertymanager']['data']
254                doc = minidom.parseString(data)
255                root = doc.documentElement
256                for child in root.childNodes:
257                    if child.nodeName != 'property':
258                        continue
259                    if child.getAttribute('name') in excluded_props:
260                        root.removeChild(child)
261
262                helper.context = obj
263                helper._initProperties(root)
264
265            yield item
Note: See TracBrowser for help on using the repository browser.