vendor/CMF/1.6-r41367/GenericSetup

view context.py @ 0:d62b03aaa782

CMF 1.6 r41367 snapshot.
author fguillaume
date Thu, 19 Jan 2006 16:49:24 +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 """ Various context implementations for export / import of configurations.
15 Wrappers representing the state of an import / export operation.
17 $Id: context.py 40715 2005-12-12 10:33:40Z yuppie $
18 """
20 import logging
21 import os
22 import time
23 from StringIO import StringIO
24 from tarfile import TarFile
25 from tarfile import TarInfo
27 from AccessControl import ClassSecurityInfo
28 from Acquisition import aq_inner
29 from Acquisition import aq_parent
30 from Acquisition import aq_self
31 from Acquisition import Implicit
32 from DateTime.DateTime import DateTime
33 from Globals import InitializeClass
34 from OFS.DTMLDocument import DTMLDocument
35 from OFS.Folder import Folder
36 from OFS.Image import File
37 from OFS.Image import Image
38 from Products.PageTemplates.ZopePageTemplate import ZopePageTemplate
39 from Products.PythonScripts.PythonScript import PythonScript
40 from zope.interface import implements
42 from interfaces import IExportContext
43 from interfaces import IImportContext
44 from interfaces import IWriteLogger
45 from permissions import ManagePortal
48 class Logger:
50 implements(IWriteLogger)
52 def __init__(self, id, messages):
53 """Initialize the logger with a name and an optional level.
54 """
55 self._id = id
56 self._messages = messages
57 self._logger = logging.getLogger('GenericSetup.%s' % id)
59 def debug(self, msg, *args, **kwargs):
60 """Log 'msg % args' with severity 'DEBUG'.
61 """
62 self.log(logging.DEBUG, msg, *args, **kwargs)
64 def info(self, msg, *args, **kwargs):
65 """Log 'msg % args' with severity 'INFO'.
66 """
67 self.log(logging.INFO, msg, *args, **kwargs)
69 def warning(self, msg, *args, **kwargs):
70 """Log 'msg % args' with severity 'WARNING'.
71 """
72 self.log(logging.WARNING, msg, *args, **kwargs)
74 def error(self, msg, *args, **kwargs):
75 """Log 'msg % args' with severity 'ERROR'.
76 """
77 self.log(logging.ERROR, msg, *args, **kwargs)
79 def exception(self, msg, *args):
80 """Convenience method for logging an ERROR with exception information.
81 """
82 self.error(msg, *args, **{'exc_info': 1})
84 def critical(self, msg, *args, **kwargs):
85 """Log 'msg % args' with severity 'CRITICAL'.
86 """
87 self.log(logging.CRITICAL, msg, *args, **kwargs)
89 def log(self, level, msg, *args, **kwargs):
90 """Log 'msg % args' with the integer severity 'level'.
91 """
92 self._messages.append((level, self._id, msg))
93 self._logger.log(level, msg, *args, **kwargs)
96 class BaseContext( Implicit ):
98 security = ClassSecurityInfo()
100 def __init__( self, tool, encoding ):
102 self._tool = tool
103 self._site = aq_parent( aq_inner( tool ) )
104 self._loggers = {}
105 self._messages = []
106 self._encoding = encoding
107 self._should_purge = True
109 security.declareProtected( ManagePortal, 'getSite' )
110 def getSite( self ):
112 """ See ISetupContext.
113 """
114 return aq_self(self._site)
116 security.declareProtected( ManagePortal, 'getSetupTool' )
117 def getSetupTool( self ):
119 """ See ISetupContext.
120 """
121 return self._tool
123 security.declareProtected( ManagePortal, 'getEncoding' )
124 def getEncoding( self ):
126 """ See ISetupContext.
127 """
128 return self._encoding
130 security.declareProtected( ManagePortal, 'getLogger' )
131 def getLogger( self, name ):
132 """ See ISetupContext.
133 """
134 return self._loggers.setdefault(name, Logger(name, self._messages))
136 security.declareProtected( ManagePortal, 'listNotes' )
137 def listNotes(self):
139 """ See ISetupContext.
140 """
141 return self._messages[:]
143 security.declareProtected( ManagePortal, 'clearNotes' )
144 def clearNotes(self):
146 """ See ISetupContext.
147 """
148 self._messages[:] = []
150 security.declareProtected( ManagePortal, 'shouldPurge' )
151 def shouldPurge( self ):
153 """ See ISetupContext.
154 """
155 return self._should_purge
158 class DirectoryImportContext( BaseContext ):
160 implements(IImportContext)
162 security = ClassSecurityInfo()
164 def __init__( self
165 , tool
166 , profile_path
167 , should_purge=False
168 , encoding=None
169 ):
171 BaseContext.__init__( self, tool, encoding )
172 self._profile_path = profile_path
173 self._should_purge = bool( should_purge )
175 security.declareProtected( ManagePortal, 'readDataFile' )
176 def readDataFile( self, filename, subdir=None ):
178 """ See IImportContext.
179 """
180 if subdir is None:
181 full_path = os.path.join( self._profile_path, filename )
182 else:
183 full_path = os.path.join( self._profile_path, subdir, filename )
185 if not os.path.exists( full_path ):
186 return None
188 file = open( full_path, 'rb' )
189 result = file.read()
190 file.close()
192 return result
194 security.declareProtected( ManagePortal, 'getLastModified' )
195 def getLastModified( self, path ):
197 """ See IImportContext.
198 """
199 full_path = os.path.join( self._profile_path, path )
201 if not os.path.exists( full_path ):
202 return None
204 return DateTime( os.path.getmtime( full_path ) )
206 security.declareProtected( ManagePortal, 'isDirectory' )
207 def isDirectory( self, path ):
209 """ See IImportContext.
210 """
211 full_path = os.path.join( self._profile_path, path )
213 if not os.path.exists( full_path ):
214 return None
216 return os.path.isdir( full_path )
218 security.declareProtected( ManagePortal, 'listDirectory' )
219 def listDirectory( self, path, skip=('CVS', '.svn') ):
221 """ See IImportContext.
222 """
223 if path is None:
224 path = ''
226 full_path = os.path.join( self._profile_path, path )
228 if not os.path.exists( full_path ) or not os.path.isdir( full_path ):
229 return None
231 names = os.listdir( full_path )
233 return [ name for name in names if name not in skip ]
235 InitializeClass( DirectoryImportContext )
238 class DirectoryExportContext( BaseContext ):
240 implements(IExportContext)
242 security = ClassSecurityInfo()
244 def __init__( self, tool, profile_path, encoding=None ):
246 BaseContext.__init__( self, tool, encoding )
247 self._profile_path = profile_path
249 security.declareProtected( ManagePortal, 'writeDataFile' )
250 def writeDataFile( self, filename, text, content_type, subdir=None ):
252 """ See IExportContext.
253 """
254 if subdir is None:
255 prefix = self._profile_path
256 else:
257 prefix = os.path.join( self._profile_path, subdir )
259 full_path = os.path.join( prefix, filename )
261 if not os.path.exists( prefix ):
262 os.makedirs( prefix )
264 mode = content_type.startswith( 'text/' ) and 'w' or 'wb'
266 file = open( full_path, mode )
267 file.write( text )
268 file.close()
270 InitializeClass( DirectoryExportContext )
273 class TarballImportContext( BaseContext ):
275 implements(IImportContext)
277 security = ClassSecurityInfo()
279 def __init__( self, tool, archive_bits, encoding=None, should_purge=False ):
281 BaseContext.__init__( self, tool, encoding )
282 timestamp = time.gmtime()
283 self._archive_stream = StringIO(archive_bits)
284 self._archive = TarFile.open( 'foo.bar', 'r:gz'
285 , self._archive_stream )
286 self._should_purge = bool( should_purge )
288 def readDataFile( self, filename, subdir=None ):
290 """ See IImportContext.
291 """
292 if subdir is not None:
293 filename = '/'.join( ( subdir, filename ) )
295 try:
296 file = self._archive.extractfile( filename )
297 except KeyError:
298 return None
300 return file.read()
302 def getLastModified( self, path ):
304 """ See IImportContext.
305 """
306 info = self._getTarInfo( path )
307 return info and info.mtime or None
309 def isDirectory( self, path ):
311 """ See IImportContext.
312 """
313 info = self._getTarInfo( path )
315 if info is not None:
316 return info.isdir()
318 def listDirectory( self, path, skip=('CVS', '.svn') ):
320 """ See IImportContext.
321 """
322 if path is None: # root is special case: no leading '/'
323 path = ''
324 else:
325 if not self.isDirectory(path):
326 return None
328 if path[-1] != '/':
329 path = path + '/'
331 pfx_len = len(path)
333 beneath = [x[pfx_len:] for x in self._archive.getnames()
334 if x.startswith(path) and x != path]
336 return [x for x in beneath if '/' not in x and x not in skip]
338 def shouldPurge( self ):
340 """ See IImportContext.
341 """
342 return self._should_purge
344 def _getTarInfo( self, path ):
345 if path[-1] == '/':
346 path = path[:-1]
347 try:
348 return self._archive.getmember( path )
349 except KeyError:
350 pass
351 try:
352 return self._archive.getmember( path + '/' )
353 except KeyError:
354 return None
357 class TarballExportContext( BaseContext ):
359 implements(IExportContext)
361 security = ClassSecurityInfo()
363 def __init__( self, tool, encoding=None ):
365 BaseContext.__init__( self, tool, encoding )
367 timestamp = time.gmtime()
368 archive_name = ( 'setup_tool-%4d%02d%02d%02d%02d%02d.tar.gz'
369 % timestamp[:6] )
371 self._archive_stream = StringIO()
372 self._archive_filename = archive_name
373 self._archive = TarFile.open( archive_name, 'w:gz'
374 , self._archive_stream )
376 security.declareProtected( ManagePortal, 'writeDataFile' )
377 def writeDataFile( self, filename, text, content_type, subdir=None ):
379 """ See IExportContext.
380 """
381 if subdir is not None:
382 filename = '/'.join( ( subdir, filename ) )
384 stream = StringIO( text )
385 info = TarInfo( filename )
386 info.size = len( text )
387 info.mtime = time.time()
388 self._archive.addfile( info, stream )
390 security.declareProtected( ManagePortal, 'getArchive' )
391 def getArchive( self ):
393 """ Close the archive, and return it as a big string.
394 """
395 self._archive.close()
396 return self._archive_stream.getvalue()
398 security.declareProtected( ManagePortal, 'getArchiveFilename' )
399 def getArchiveFilename( self ):
401 """ Close the archive, and return it as a big string.
402 """
403 return self._archive_filename
405 InitializeClass( TarballExportContext )
408 class SnapshotExportContext( BaseContext ):
410 implements(IExportContext)
412 security = ClassSecurityInfo()
414 def __init__( self, tool, snapshot_id, encoding=None ):
416 BaseContext.__init__( self, tool, encoding )
417 self._snapshot_id = snapshot_id
419 security.declareProtected( ManagePortal, 'writeDataFile' )
420 def writeDataFile( self, filename, text, content_type, subdir=None ):
422 """ See IExportContext.
423 """
424 if subdir is not None:
425 filename = '/'.join( ( subdir, filename ) )
427 sep = filename.rfind('/')
428 if sep != -1:
429 subdir = filename[:sep]
430 filename = filename[sep+1:]
431 folder = self._ensureSnapshotsFolder( subdir )
433 # TODO: switch on content_type
434 ob = self._createObjectByType( filename, text, content_type )
435 folder._setObject( str( filename ), ob ) # No Unicode IDs!
437 security.declareProtected( ManagePortal, 'getSnapshotURL' )
438 def getSnapshotURL( self ):
440 """ See IExportContext.
441 """
442 return '%s/%s' % ( self._tool.absolute_url(), self._snapshot_id )
444 security.declareProtected( ManagePortal, 'getSnapshotFolder' )
445 def getSnapshotFolder( self ):
447 """ See IExportContext.
448 """
449 return self._ensureSnapshotsFolder()
451 #
452 # Helper methods
453 #
454 security.declarePrivate( '_createObjectByType' )
455 def _createObjectByType( self, name, body, content_type ):
457 if isinstance( body, unicode ):
458 encoding = self.getEncoding()
459 if encoding is None:
460 body = body.encode()
461 else:
462 body = body.encode( encoding )
464 if name.endswith('.py'):
466 ob = PythonScript( name )
467 ob.write( body )
469 elif name.endswith('.dtml'):
471 ob = DTMLDocument( '', __name__=name )
472 ob.munge( body )
474 elif content_type in ('text/html', 'text/xml' ):
476 ob = ZopePageTemplate( name, body
477 , content_type=content_type )
479 elif content_type[:6]=='image/':
481 ob=Image( name, '', body, content_type=content_type )
483 else:
484 ob=File( name, '', body, content_type=content_type )
486 return ob
488 security.declarePrivate( '_ensureSnapshotsFolder' )
489 def _ensureSnapshotsFolder( self, subdir=None ):
491 """ Ensure that the appropriate snapshot folder exists.
492 """
493 path = [ 'snapshots', self._snapshot_id ]
495 if subdir is not None:
496 path.extend( subdir.split( '/' ) )
498 current = self._tool
500 for element in path:
502 if element not in current.objectIds():
503 # No Unicode IDs!
504 current._setObject( str( element ), Folder( element ) )
506 current = current._getOb( element )
508 return current
510 InitializeClass( SnapshotExportContext )
513 class SnapshotImportContext( BaseContext ):
515 implements(IImportContext)
517 security = ClassSecurityInfo()
519 def __init__( self
520 , tool
521 , snapshot_id
522 , should_purge=False
523 , encoding=None
524 ):
526 BaseContext.__init__( self, tool, encoding )
527 self._snapshot_id = snapshot_id
528 self._encoding = encoding
529 self._should_purge = bool( should_purge )
531 security.declareProtected( ManagePortal, 'readDataFile' )
532 def readDataFile( self, filename, subdir=None ):
534 """ See IImportContext.
535 """
536 if subdir is not None:
537 filename = '/'.join( ( subdir, filename ) )
539 sep = filename.rfind('/')
540 if sep != -1:
541 subdir = filename[:sep]
542 filename = filename[sep+1:]
543 try:
544 snapshot = self._getSnapshotFolder( subdir )
545 object = snapshot._getOb( filename )
546 except ( AttributeError, KeyError ):
547 return None
549 try:
550 return object.read()
551 except AttributeError:
552 return object.manage_FTPget()
554 security.declareProtected( ManagePortal, 'getLastModified' )
555 def getLastModified( self, path ):
557 """ See IImportContext.
558 """
559 try:
560 snapshot = self._getSnapshotFolder()
561 object = snapshot.restrictedTraverse( path )
562 except ( AttributeError, KeyError ):
563 return None
564 else:
565 return object.bobobase_modification_time()
567 security.declareProtected( ManagePortal, 'isDirectory' )
568 def isDirectory( self, path ):
570 """ See IImportContext.
571 """
572 try:
573 snapshot = self._getSnapshotFolder()
574 object = snapshot.restrictedTraverse( path )
575 except ( AttributeError, KeyError ):
576 return None
577 else:
578 folderish = getattr( object, 'isPrincipiaFolderish', False )
579 return bool( folderish )
581 security.declareProtected( ManagePortal, 'listDirectory' )
582 def listDirectory( self, path, skip=() ):
584 """ See IImportContext.
585 """
586 try:
587 snapshot = self._getSnapshotFolder()
588 subdir = snapshot.restrictedTraverse( path )
589 except ( AttributeError, KeyError ):
590 return None
591 else:
592 if not getattr( subdir, 'isPrincipiaFolderish', False ):
593 return None
595 object_ids = subdir.objectIds()
596 return [ x for x in object_ids if x not in skip ]
598 security.declareProtected( ManagePortal, 'shouldPurge' )
599 def shouldPurge( self ):
601 """ See IImportContext.
602 """
603 return self._should_purge
605 #
606 # Helper methods
607 #
608 security.declarePrivate( '_getSnapshotFolder' )
609 def _getSnapshotFolder( self, subdir=None ):
611 """ Return the appropriate snapshot (sub)folder.
612 """
613 path = [ 'snapshots', self._snapshot_id ]
615 if subdir is not None:
616 path.extend( subdir.split( '/' ) )
618 return self._tool.restrictedTraverse( path )
620 InitializeClass( SnapshotImportContext )