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

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

fix property manager to strip spaces in lines properties

File size: 10.3 KB
Line 
1from copy import deepcopy
2from xml.dom import minidom
3
4from zope.interface import classProvides, implements
5
6from collective.transmogrifier.interfaces import ISection, ISectionBlueprint
7from collective.transmogrifier.utils import defaultMatcher
8
9from OFS.interfaces import IPropertyManager
10from Products.GenericSetup.utils import PropertyManagerHelpers, NodeAdapterBase
11
12class Helper(PropertyManagerHelpers, NodeAdapterBase):
13    """ We need this class because PropertyManagerHelpers in _initProperties
14        method uses _convertToBoolean and _getNodeText methods from
15        NodeAdapterBase class.
16    """
17   
18    _encoding = 'utf-8'
19   
20    def __init__(self):
21        pass
22
23    def _getNodeText(self, node):
24        # We override method in NodeAdapterBase, because it return bad property value.
25        # When properties are extracted newline charcters and indentation were added to
26        # them, but these aren't stripped on import. Maybe this method doesn't handle
27        # properly multiline string values, but it is needed for importing.
28        text = ''
29        for child in node.childNodes:
30            if child.nodeName != '#text':
31                continue
32            text += child.nodeValue.strip()
33        return text
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 = value.decode(self._encoding)
55                    child = self._doc.createElement('element')
56                    child.appendChild(self._doc.createTextNode(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 _initProperties(self, node):
83        obj = self.context
84        if node.hasAttribute('i18n:domain'):
85            i18n_domain = str(node.getAttribute('i18n:domain'))
86            obj._updateProperty('i18n_domain', i18n_domain)
87        for child in node.childNodes:
88            if child.nodeName != 'property':
89                continue
90            prop_id = str(child.getAttribute('name'))
91            prop_map = obj.propdict().get(prop_id, None)
92
93            if prop_map is None:
94                if child.hasAttribute('type'):
95                    val = str(child.getAttribute('select_variable'))
96                    prop_type = str(child.getAttribute('type'))
97                    obj._setProperty(prop_id, val, prop_type)
98                    prop_map = obj.propdict().get(prop_id, None)
99                else:
100                    raise ValueError("undefined property '%s'" % prop_id)
101
102            if not 'w' in prop_map.get('mode', 'wd'):
103                raise BadRequest('%s cannot be changed' % prop_id)
104
105            elements = []
106            remove_elements = []
107            for sub in child.childNodes:
108                if sub.nodeName == 'element':
109                    if len(sub.childNodes) > 0:
110                        value = sub.childNodes[0].nodeValue.strip()
111                        if isinstance(value, unicode):
112                            value = value.encode(self._encoding)
113                        if self._convertToBoolean(sub.getAttribute('remove')
114                                                  or 'False'):
115                            remove_elements.append(value)
116                            if value in elements:
117                                elements.remove(value)
118                        else:
119                            elements.append(value)
120                            if value in remove_elements:
121                                remove_elements.remove(value)
122
123            if elements or prop_map.get('type') == 'multiple selection':
124                prop_value = tuple(elements) or ()
125            elif prop_map.get('type') == 'boolean':
126                prop_value = self._convertToBoolean(self._getNodeText(child))
127            else:
128                # if we pass a *string* to _updateProperty, all other values
129                # are converted to the right type
130                prop_value = self._getNodeText(child).encode(self._encoding)
131
132            if not self._convertToBoolean(child.getAttribute('purge')
133                                          or 'True'):
134                # If the purge attribute is False, merge sequences
135                prop = obj.getProperty(prop_id)
136                if isinstance(prop, (tuple, list)):
137                    prop_value = (tuple([p for p in prop
138                                         if p not in prop_value and
139                                            p not in remove_elements]) +
140                                  tuple(prop_value))
141
142            obj._updateProperty(prop_id, prop_value)
143
144class PropertiesExporterSection(object):
145    classProvides(ISectionBlueprint)
146    implements(ISection)
147
148    def __init__(self, transmogrifier, name, options, previous):
149        self.previous = previous
150        self.context = transmogrifier.context
151
152        self.pathkey = defaultMatcher(options, 'path-key', name, 'path')
153        self.fileskey = options.get('files-key', '_files').strip()
154
155        self.excludekey = defaultMatcher(options, 'exclude-key', name, 'excluded_properties')
156        self.exclude = filter(None, [i.strip() for i in 
157                              options.get('exclude', '').splitlines()])
158
159        self.helper = Helper()
160        self.doc = minidom.Document()
161        self.helper._doc = self.doc
162
163    def __iter__(self):
164        helper = self.helper
165        doc = self.doc
166
167        for item in self.previous:
168            pathkey = self.pathkey(*item.keys())[0]
169
170            if not pathkey:
171                yield item; continue
172
173            path = item[pathkey]
174            obj = self.context.unrestrictedTraverse(path, None)
175            if obj is None:         # path doesn't exist
176                yield item; continue
177
178            if IPropertyManager.providedBy(obj):
179                data = None
180                excludekey = self.excludekey(*item.keys())[0]
181                excluded_props = tuple(self.exclude)
182                if excludekey:
183                    excluded_props = tuple(set(item[excludekey]) | set(excluded_props))
184
185                helper.context = obj
186                node = doc.createElement('properties')
187                for elem in helper._extractProperties().childNodes:
188                    if elem.nodeName != 'property':
189                        continue
190                    if elem.getAttribute('name') not in excluded_props:
191                        node.appendChild(deepcopy(elem))
192                if node.hasChildNodes():
193                    doc.appendChild(node)
194                    data = doc.toprettyxml(indent='  ', encoding='utf-8')
195                    doc.unlink()
196
197                if data:
198                    files = item.setdefault(self.fileskey, {})
199                    item[self.fileskey]['propertymanager'] = {
200                        'name': '.properties.xml',
201                        'data': data,
202                    }
203
204            yield item
205
206class PropertiesImporterSection(object):
207    classProvides(ISectionBlueprint)
208    implements(ISection)
209
210    def __init__(self, transmogrifier, name, options, previous):
211        self.previous = previous
212        self.context = transmogrifier.context
213
214        self.pathkey = defaultMatcher(options, 'path-key', name, 'path')
215        self.fileskey = defaultMatcher(options, 'files-key', name, 'files')
216
217        self.excludekey = defaultMatcher(options, 'exclude-key', name, 'excluded_properties')
218        self.exclude = filter(None, [i.strip() for i in 
219                            options.get('exclude', '').splitlines()])
220
221        self.helper = Helper()
222        self.helper._encoding = 'utf-8'
223
224    def __iter__(self):
225        helper = self.helper
226
227        for item in self.previous:
228            pathkey = self.pathkey(*item.keys())[0]
229            fileskey = self.fileskey(*item.keys())[0]
230
231            if not (pathkey and fileskey):
232                yield item; continue
233            if 'propertymanager' not in item[fileskey]:
234                yield item; continue
235
236            path = item[pathkey]
237            obj = self.context.unrestrictedTraverse(path, None)
238            if obj is None:         # path doesn't exist
239                yield item; continue
240
241            if IPropertyManager.providedBy(obj):
242                data = None
243                excludekey = self.excludekey(*item.keys())[0]
244                excluded_props = self.exclude
245                if excludekey:
246                    excluded_props = tuple(set(item[excludekey]) | set(excluded_props))
247
248                data = item[fileskey]['propertymanager']['data']
249                doc = minidom.parseString(data)
250                root = doc.documentElement
251                children = [k for k in root.childNodes]
252                for child in children:
253                    if child.nodeName != 'property':
254                        continue
255                    if child.getAttribute('name') in excluded_props:
256                        root.removeChild(child)
257
258                helper.context = obj
259                helper._initProperties(root)
260
261            yield item
Note: See TracBrowser for help on using the repository browser.