vendor/CMF/1.5.3/CMFSetup

view utils.py @ 0:3ed006215eb6

Vendor import of CMF 1.5.3
author fguillaume
date Tue, 09 Aug 2005 10:47:34 +0000
parents
children
line source
1 ##############################################################################
2 #
3 # Copyright (c) 2004 Zope Corporation and Contributors. All Rights Reserved.
4 #
5 # This software is subject to the provisions of the Zope Public License,
6 # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10 # FOR A PARTICULAR PURPOSE.
11 #
12 ##############################################################################
13 """ CMFSetup product utilities
15 $Id: utils.py 37139 2005-07-08 14:38:17Z efge $
16 """
18 import os
19 from inspect import getdoc
20 from xml.dom.minidom import parseString as domParseString
21 from xml.sax.handler import ContentHandler
23 import Products
24 from AccessControl import ClassSecurityInfo
25 from Acquisition import aq_base
26 from Acquisition import Implicit
27 from Globals import InitializeClass
28 from Globals import package_home
29 from Products.PageTemplates.PageTemplateFile import PageTemplateFile
31 from exceptions import BadRequest
32 from permissions import ManagePortal
35 _pkgdir = package_home( globals() )
36 _wwwdir = os.path.join( _pkgdir, 'www' )
37 _datadir = os.path.join( _pkgdir, 'data' )
38 _xmldir = os.path.join( _pkgdir, 'xml' )
40 CONVERTER, DEFAULT, KEY = range(3)
43 def _getDottedName( named ):
45 if isinstance( named, basestring ):
46 return str( named )
48 try:
49 return '%s.%s' % ( named.__module__, named.__name__ )
50 except AttributeError:
51 raise ValueError, 'Cannot compute dotted name: %s' % named
53 def _resolveDottedName( dotted ):
55 parts = dotted.split( '.' )
57 if not parts:
58 raise ValueError, "incomplete dotted name: %s" % dotted
60 parts_copy = parts[:]
62 while parts_copy:
63 try:
64 module = __import__( '.'.join( parts_copy ) )
65 break
67 except ImportError:
69 del parts_copy[ -1 ]
71 if not parts_copy:
72 raise
74 parts = parts[ 1: ] # Funky semantics of __import__'s return value
76 obj = module
78 for part in parts:
79 obj = getattr( obj, part )
81 return obj
83 def _extractDocstring( func, default_title, default_description ):
85 try:
86 doc = getdoc( func )
87 lines = doc.split( '\n' )
89 except AttributeError:
91 title = default_title
92 description = default_description
94 else:
95 title = lines[ 0 ]
97 if len( lines ) > 1 and lines[ 1 ].strip() == '':
98 del lines[ 1 ]
100 description = '\n'.join( lines[ 1: ] )
102 return title, description
105 class HandlerBase( ContentHandler ):
107 _encoding = None
108 _MARKER = object()
110 def _extract( self, attrs, key, default=None ):
112 result = attrs.get( key, self._MARKER )
114 if result is self._MARKER:
115 return default
117 return self._encode( result )
119 def _extractBoolean( self, attrs, key, default ):
121 result = attrs.get( key, self._MARKER )
123 if result is self._MARKER:
124 return default
126 result = result.lower()
127 return result in ( '1', 'yes', 'true' )
129 def _encode( self, content ):
131 if self._encoding is None:
132 return content
134 return content.encode( self._encoding )
137 class ImportConfiguratorBase(Implicit):
138 """ Synthesize data from XML description.
139 """
140 security = ClassSecurityInfo()
141 security.setDefaultAccess('allow')
143 def __init__(self, site, encoding=None):
145 self._site = site
146 self._encoding = encoding
148 security.declareProtected(ManagePortal, 'parseXML')
149 def parseXML(self, xml):
150 """ Pseudo API.
151 """
152 reader = getattr(xml, 'read', None)
154 if reader is not None:
155 xml = reader()
157 dom = domParseString(xml)
158 root = dom.documentElement
160 return self._extractNode(root)
162 def _extractNode(self, node):
164 nodes_map = self._getImportMapping()
165 if node.nodeName not in nodes_map:
166 nodes_map = self._getSharedImportMapping()
167 if node.nodeName not in nodes_map:
168 raise ValueError('Unknown node: %s' % node.nodeName)
169 node_map = nodes_map[node.nodeName]
170 info = {}
172 for name, val in node.attributes.items():
173 key = node_map[name].get( KEY, str(name) )
174 val = self._encoding and val.encode(self._encoding) or val
175 info[key] = val
177 for child in node.childNodes:
178 name = child.nodeName
180 if name == '#comment':
181 continue
183 if not name == '#text':
184 key = node_map[name].get(KEY, str(name) )
185 info[key] = info.setdefault( key, () ) + (
186 self._extractNode(child),)
188 elif '#text' in node_map:
189 key = node_map['#text'].get(KEY, 'value')
190 val = child.nodeValue.lstrip()
191 val = self._encoding and val.encode(self._encoding) or val
192 info[key] = info.setdefault(key, '') + val
194 for k, v in node_map.items():
195 key = v.get(KEY, k)
197 if DEFAULT in v and not key in info:
198 if isinstance( v[DEFAULT], basestring ):
199 info[key] = v[DEFAULT] % info
200 else:
201 info[key] = v[DEFAULT]
203 elif CONVERTER in v and key in info:
204 info[key] = v[CONVERTER]( info[key] )
206 if key is None:
207 info = info[key]
209 return info
211 def _getSharedImportMapping(self):
213 return {
214 'object':
215 { 'name': {KEY: 'id'},
216 'meta_type': {},
217 'insert-before': {},
218 'insert-after': {},
219 'property': {KEY: 'properties', DEFAULT: ()},
220 'object': {KEY: 'objects', DEFAULT: ()} },
221 'property':
222 { 'name': {KEY: 'id'},
223 '#text': {KEY: 'value', DEFAULT: ''},
224 'element': {KEY: 'elements', DEFAULT: ()},
225 'type': {},
226 'select_variable': {} },
227 'element':
228 { 'value': {KEY: None} },
229 'description':
230 { '#text': {KEY: None, DEFAULT: ''} } }
232 def _convertToBoolean(self, val):
234 return val.lower() in ('true', 'yes', '1')
236 def _convertToUnique(self, val):
238 assert len(val) == 1
239 return val[0]
241 security.declareProtected(ManagePortal, 'initObject')
242 def initObject(self, parent, o_info):
244 obj_id = str(o_info['id'])
245 if obj_id not in parent.objectIds():
246 meta_type = o_info['meta_type']
247 for mt_info in Products.meta_types:
248 if mt_info['name'] == meta_type:
249 parent._setObject( obj_id, mt_info['instance'](obj_id) )
250 break
251 else:
252 raise ValueError('unknown meta_type \'%s\'' % obj_id)
253 obj = parent._getOb(obj_id)
255 if 'insert-before' in o_info:
256 if o_info['insert-before'] == '*':
257 parent.moveObjectsToTop(obj_id)
258 else:
259 try:
260 position = parent.getObjectPosition(o_info['insert-before'])
261 parent.moveObjectToPosition(obj_id, position)
262 except ValueError:
263 pass
264 elif 'insert-after' in o_info:
265 if o_info['insert-after'] == '*':
266 parent.moveObjectsToBottom(obj_id)
267 else:
268 try:
269 position = parent.getObjectPosition(o_info['insert-after'])
270 parent.moveObjectToPosition(obj_id, position+1)
271 except ValueError:
272 pass
274 [ self.initObject(obj, info) for info in o_info['objects'] ]
276 [ self.initProperty(obj, info) for info in o_info['properties'] ]
278 security.declareProtected(ManagePortal, 'initProperty')
279 def initProperty(self, obj, p_info):
281 prop_id = p_info['id']
282 prop_map = obj.propdict().get(prop_id, None)
284 if prop_map is None:
285 type = p_info.get('type', None)
286 if type:
287 val = p_info.get('select_variable', '')
288 obj._setProperty(prop_id, val, type)
289 prop_map = obj.propdict().get(prop_id, None)
290 else:
291 raise ValueError('undefined property \'%s\'' % prop_id)
293 if not 'w' in prop_map.get('mode', 'wd'):
294 raise BadRequest('%s cannot be changed' % prop_id)
296 if prop_map.get('type') == 'multiple selection':
297 prop_value = p_info['elements'] or ()
298 elif prop_map.get('type') == 'boolean':
299 # Make sure '0' is imported as False
300 prop_value = str(p_info['value'])
301 if prop_value == '0':
302 prop_value = ''
303 else:
304 # if we pass a *string* to _updateProperty, all other values
305 # are converted to the right type
306 prop_value = p_info['elements'] or str( p_info['value'] )
308 obj._updateProperty(prop_id, prop_value)
310 InitializeClass(ImportConfiguratorBase)
313 class ExportConfiguratorBase(Implicit):
314 """ Synthesize XML description.
315 """
316 security = ClassSecurityInfo()
317 security.setDefaultAccess('allow')
319 def __init__(self, site, encoding=None):
321 self._site = site
322 self._encoding = encoding
323 self._template = self._getExportTemplate()
325 security.declareProtected(ManagePortal, 'generateXML')
326 def generateXML(self, **kw):
327 """ Pseudo API.
328 """
329 return self._template(**kw)
331 #
332 # generic object and property support
333 #
334 _ob_nodes = PageTemplateFile('object_nodes.xml', _xmldir)
335 _prop_nodes = PageTemplateFile('property_nodes.xml', _xmldir)
337 security.declareProtected(ManagePortal, 'generateObjectNodes')
338 def generateObjectNodes(self, obj_infos):
339 """ Pseudo API.
340 """
341 lines = self._ob_nodes(objects=obj_infos).splitlines()
342 return '\n'.join(lines)
344 security.declareProtected(ManagePortal, 'generatePropertyNodes')
345 def generatePropertyNodes(self, prop_infos):
346 """ Pseudo API.
347 """
348 lines = self._prop_nodes(properties=prop_infos).splitlines()
349 return '\n'.join(lines)
351 def _extractObject(self, obj):
353 properties = []
354 subobjects = []
356 if getattr( aq_base(obj), '_propertyMap' ):
357 for prop_map in obj._propertyMap():
358 properties.append( self._extractProperty(obj, prop_map) )
360 if getattr( aq_base(obj), 'objectValues' ):
361 for sub in obj.objectValues():
362 subobjects.append( self._extractObject(sub) )
364 return { 'id': obj.getId(),
365 'meta_type': obj.meta_type,
366 'properties': tuple(properties),
367 'subobjects': tuple(subobjects) }
369 def _extractProperty(self, obj, prop_map):
371 prop_id = prop_map['id']
372 prop = obj.getProperty(prop_id)
374 if isinstance(prop, tuple):
375 prop_value = ''
376 prop_elements = prop
377 else:
378 prop_value = prop
379 prop_elements = ()
381 if 'd' in prop_map.get('mode', 'wd') and not prop_id == 'title':
382 type = prop_map.get('type', 'string')
383 select_variable = prop_map.get('select_variable', None)
384 else:
385 type = None
386 select_variable = None
388 return { 'id': prop_id,
389 'value': prop_value,
390 'elements': prop_elements,
391 'type': type,
392 'select_variable': select_variable }
394 InitializeClass(ExportConfiguratorBase)
397 # BBB: old class mixing the two, will be removed in CMF 1.7
398 class ConfiguratorBase(ImportConfiguratorBase, ExportConfiguratorBase):
399 """ Synthesize XML description.
400 """
401 security = ClassSecurityInfo()
402 security.setDefaultAccess('allow')
404 def __init__(self, site, encoding=None):
405 ImportConfiguratorBase.__init__(self, site, encoding)
406 ExportConfiguratorBase.__init__(self, site, encoding)
408 InitializeClass(ConfiguratorBase)
411 #
412 # deprecated DOM parsing utilities
413 #
414 _marker = object()
416 def _queryNodeAttribute( node, attr_name, default, encoding=None ):
418 """ Extract a string-valued attribute from node.
420 o Return 'default' if the attribute is not present.
421 """
422 attr_node = node.attributes.get( attr_name, _marker )
424 if attr_node is _marker:
425 return default
427 value = attr_node.nodeValue
429 if encoding is not None:
430 value = value.encode( encoding )
432 return value
434 def _getNodeAttribute( node, attr_name, encoding=None ):
436 """ Extract a string-valued attribute from node.
437 """
438 value = _queryNodeAttribute( node, attr_name, _marker, encoding )
440 if value is _marker:
441 raise ValueError, 'Invaid attribute: %s' % attr_name
443 return value
445 def _queryNodeAttributeBoolean( node, attr_name, default ):
447 """ Extract a string-valued attribute from node.
449 o Return 'default' if the attribute is not present.
450 """
451 attr_node = node.attributes.get( attr_name, _marker )
453 if attr_node is _marker:
454 return default
456 value = node.attributes[ attr_name ].nodeValue.lower()
458 return value in ( 'true', 'yes', '1' )
460 def _getNodeAttributeBoolean( node, attr_name ):
462 """ Extract a string-valued attribute from node.
463 """
464 value = node.attributes[ attr_name ].nodeValue.lower()
466 return value in ( 'true', 'yes', '1' )
468 def _coalesceTextNodeChildren( node, encoding=None ):
470 """ Concatenate all childe text nodes into a single string.
471 """
472 from xml.dom import Node
473 fragments = []
474 node.normalize()
475 child = node.firstChild
477 while child is not None:
479 if child.nodeType == Node.TEXT_NODE:
480 fragments.append( child.nodeValue )
482 child = child.nextSibling
484 joined = ''.join( fragments )
486 if encoding is not None:
487 joined = joined.encode( encoding )
489 return ''.join( [ line.lstrip() for line in joined.splitlines(True) ] )
491 def _extractDescriptionNode(parent, encoding=None):
493 d_nodes = parent.getElementsByTagName('description')
494 if d_nodes:
495 return _coalesceTextNodeChildren(d_nodes[0], encoding)
496 else:
497 return ''