class SetupTool( UniqueObject, Folder ): """ Profile-based site configuration manager. """ __implements__ = ( ISetupTool, ) + Folder.__implements__ id = 'portal_setup' meta_type = 'Portal Setup Tool' _import_context_id = '' security = ClassSecurityInfo() def __init__( self ): self._import_registry = ImportStepRegistry() self._export_registry = ExportStepRegistry() self._export_registry.registerStep( 'step_registries' , exportStepRegistries , 'Export import / export steps.' ) self._toolset_registry = ToolsetRegistry() # # ISetupTool API # security.declareProtected( ManagePortal, 'getEncoding' ) def getEncoding( self ): """ See ISetupTool. """ return 'ascii' security.declareProtected( ManagePortal, 'getImportContextId' ) def getImportContextID( self ): """ See ISetupTool. """ return self._import_context_id security.declareProtected(ManagePortal, 'setImportContext') def setImportContext(self, context_id, encoding=None): """ See ISetupTool. """ self._import_context_id = context_id self._updateImportStepsRegistry( encoding ) self._updateExportStepsRegistry( encoding ) self._updateToolsetRegistry( encoding ) security.declareProtected( ManagePortal, 'getImportStepRegistry' ) def getImportStepRegistry( self ): """ See ISetupTool. """ return self._import_registry security.declareProtected( ManagePortal, 'getImportStepRegistry' ) def getExportStepRegistry( self ): """ See ISetupTool. """ return self._export_registry security.declareProtected( ManagePortal, 'getToolsetRegistry' ) def getToolsetRegistry( self ): """ See ISetupTool. """ return self._toolset_registry security.declareProtected( ManagePortal, 'executeStep' ) def runImportStep( self, step_id, run_dependencies=True, purge_old=True ): """ See ISetupTool. """ context = self._getImportContext(self._import_context_id, purge_old) info = self._import_registry.getStepMetadata( step_id ) if info is None: raise ValueError, 'No such import step: %s' % step_id dependencies = info.get( 'dependencies', () ) messages = {} steps = [] if run_dependencies: for dependency in dependencies: if dependency not in steps: message = self._doRunImportStep( dependency, context ) messages[ dependency ] = message steps.append( dependency ) message = self._doRunImportStep( step_id, context ) messages[ step_id ] = message steps.append( step_id ) return { 'steps' : steps, 'messages' : messages } security.declareProtected( ManagePortal, 'runAllSetupSteps') def runAllImportSteps( self, purge_old=True ): """ See ISetupTool. """ context = self._getImportContext(self._import_context_id, purge_old) steps = self._import_registry.sortSteps() messages = {} for step in steps: message = self._doRunImportStep( step, context ) messages[ step ] = message return { 'steps' : steps, 'messages' : messages } security.declareProtected( ManagePortal, 'runExportStep') def runExportStep( self, step_id ): """ See ISetupTool. """ return self._doRunExportSteps( [ step_id ] ) security.declareProtected(ManagePortal, 'runAllExportSteps') def runAllExportSteps( self ): """ See ISetupTool. """ return self._doRunExportSteps( self._export_registry.listSteps() ) security.declareProtected( ManagePortal, 'createSnapshot') def createSnapshot( self, snapshot_id ): """ See ISetupTool. """ context = SnapshotExportContext( self, snapshot_id ) messages = {} steps = self._export_registry.listSteps() for step_id in steps: handler = self._export_registry.getStep( step_id ) if handler is None: raise ValueError( 'Invalid export step: %s' % step_id ) messages[ step_id ] = handler( context ) return { 'steps' : steps , 'messages' : messages , 'url' : context.getSnapshotURL() , 'snapshot' : context.getSnapshotFolder() } security.declareProtected(ManagePortal, 'compareConfigurations') def compareConfigurations( self , lhs_context , rhs_context , missing_as_empty=False , ignore_blanks=False , skip=( 'CVS', '.svn' ) ): """ See ISetupTool. """ differ = ConfigDiff( lhs_context , rhs_context , missing_as_empty , ignore_blanks , skip ) return differ.compare() security.declareProtected( ManagePortal, 'markupComparison') def markupComparison(self, lines): """ See ISetupTool. """ result = [] for line in lines.splitlines(): if line.startswith('** '): if line.find('File') > -1: if line.find('replaced') > -1: result.append( ( 'file-to-dir', line ) ) elif line.find('added') > -1: result.append( ( 'file-added', line ) ) else: result.append( ( 'file-removed', line ) ) else: if line.find('replaced') > -1: result.append( ( 'dir-to-file', line ) ) elif line.find('added') > -1: result.append( ( 'dir-added', line ) ) else: result.append( ( 'dir-removed', line ) ) elif line.startswith('@@'): result.append( ( 'diff-range', line ) ) elif line.startswith(' '): result.append( ( 'diff-context', line ) ) elif line.startswith('+'): result.append( ( 'diff-added', line ) ) elif line.startswith('-'): result.append( ( 'diff-removed', line ) ) elif line == '\ No newline at end of file': result.append( ( 'diff-context', line ) ) else: result.append( ( 'diff-header', line ) ) return '<pre>\n%s\n</pre>' % ( '\n'.join( [ ( '<span class="%s">%s</span>' % ( cl, escape( l ) ) ) for cl, l in result] ) ) # # ZMI # manage_options = ( Folder.manage_options[ :1 ] + ( { 'label' : 'Properties' , 'action' : 'manage_tool' } , { 'label' : 'Import' , 'action' : 'manage_importSteps' } , { 'label' : 'Export' , 'action' : 'manage_exportSteps' } , { 'label' : 'Snapshots' , 'action' : 'manage_snapshots' } , { 'label' : 'Comparison' , 'action' : 'manage_showDiff' } ) + Folder.manage_options[ 3: ] # skip "View", "Properties" ) security.declareProtected( ManagePortal, 'manage_tool' ) manage_tool = PageTemplateFile( 'sutProperties', _wwwdir ) security.declareProtected( ManagePortal, 'manage_updateToolProperties' ) def manage_updateToolProperties(self, context_id, RESPONSE): """ Update the tool's settings. """ self.setImportContext(context_id) RESPONSE.redirect( '%s/manage_tool?manage_tabs_message=%s' % ( self.absolute_url(), 'Properties+updated.' ) ) security.declareProtected( ManagePortal, 'manage_importSteps' ) manage_importSteps = PageTemplateFile( 'sutImportSteps', _wwwdir ) security.declareProtected( ManagePortal, 'manage_importSelectedSteps' ) def manage_importSelectedSteps( self , ids , run_dependencies , purge_old , RESPONSE ): """ Import the steps selected by the user. """ if not ids: message = 'No+steps+selected.' else: steps_run = [] for step_id in ids: result = self.runImportStep( step_id , run_dependencies , purge_old ) steps_run.extend( result[ 'steps' ] ) message = 'Steps+run:%s' % '+,'.join( steps_run ) RESPONSE.redirect( '%s/manage_importSteps?manage_tabs_message=%s' % ( self.absolute_url(), message ) ) security.declareProtected( ManagePortal, 'manage_importSelectedSteps' ) def manage_importAllSteps( self, purge_old, RESPONSE ): """ Import all steps. """ result = self.runAllImportSteps( purge_old ) message = 'Steps+run:%s' % '+,'.join( result[ 'steps' ] ) RESPONSE.redirect( '%s/manage_importSteps?manage_tabs_message=%s' % ( self.absolute_url(), message ) ) security.declareProtected( ManagePortal, 'manage_exportSteps' ) manage_exportSteps = PageTemplateFile( 'sutExportSteps', _wwwdir ) security.declareProtected( ManagePortal, 'manage_exportSelectedSteps' ) def manage_exportSelectedSteps( self, ids, RESPONSE ): """ Export the steps selected by the user. """ if not ids: RESPONSE.redirect( '%s/manage_exportSteps?manage_tabs_message=%s' % ( self.absolute_url(), 'No+steps+selected.' ) ) result = self._doRunExportSteps( ids ) RESPONSE.setHeader( 'Content-type', 'application/x-gzip') RESPONSE.setHeader( 'Content-disposition' , 'attachment; filename=%s' % result[ 'filename' ] ) return result[ 'tarball' ] security.declareProtected( ManagePortal, 'manage_exportAllSteps' ) def manage_exportAllSteps( self, RESPONSE ): """ Export all steps. """ result = self.runAllExportSteps() RESPONSE.setHeader( 'Content-type', 'application/x-gzip') RESPONSE.setHeader( 'Content-disposition' , 'attachment; filename=%s' % result[ 'filename' ] ) return result[ 'tarball' ] security.declareProtected( ManagePortal, 'manage_snapshots' ) manage_snapshots = PageTemplateFile( 'sutSnapshots', _wwwdir ) security.declareProtected( ManagePortal, 'listSnapshotInfo' ) def listSnapshotInfo( self ): """ Return a list of mappings describing available snapshots. o Keys include: 'id' -- snapshot ID 'title' -- snapshot title or ID 'url' -- URL of the snapshot folder """ result = [] snapshots = self._getOb( 'snapshots', None ) if snapshots: for id, folder in snapshots.objectItems( 'Folder' ): result.append( { 'id' : id , 'title' : folder.title_or_id() , 'url' : folder.absolute_url() } ) return result security.declareProtected( ManagePortal, 'listProfileInfo' ) def listProfileInfo( self ): """ Return a list of mappings describing registered profiles. o Keys include: 'id' -- profile ID 'title' -- profile title or ID 'description' -- description of the profile 'path' -- path to the profile within its product 'product' -- name of the registering product """ return _profile_registry.listProfileInfo() security.declareProtected(ManagePortal, 'listContextInfos') def listContextInfos(self): """ List registered profiles and snapshots. """ s_infos = [ { 'id': 'snapshot-%s' % info['id'], 'title': info['title'] } for info in self.listSnapshotInfo() ] p_infos = [ { 'id': 'profile-%s' % info['id'], 'title': info['title'] } for info in self.listProfileInfo() ] return tuple(s_infos + p_infos) security.declareProtected( ManagePortal, 'manage_createSnapshot' ) def manage_createSnapshot( self, RESPONSE, snapshot_id=None ): """ Create a snapshot with the given ID. o If no ID is passed, generate one. """ if snapshot_id is None: timestamp = time.gmtime() snapshot_id = 'snapshot-%4d%02d%02d%02d%02d%02d' % timestamp[:6] self.createSnapshot( snapshot_id ) RESPONSE.redirect( '%s/manage_snapshots?manage_tabs_message=%s' % ( self.absolute_url(), 'Snapshot+created.' ) ) security.declareProtected( ManagePortal, 'manage_showDiff' ) manage_showDiff = PageTemplateFile( 'sutCompare', _wwwdir ) def manage_downloadDiff( self , lhs , rhs , missing_as_empty , ignore_blanks , RESPONSE ): """ Crack request vars and call compareConfigurations. o Return the result as a 'text/plain' stream, suitable for framing. """ comparison = self.manage_compareConfigurations( lhs , rhs , missing_as_empty , ignore_blanks ) RESPONSE.setHeader( 'Content-Type', 'text/plain' ) return _PLAINTEXT_DIFF_HEADER % ( lhs, rhs, comparison ) security.declareProtected( ManagePortal, 'manage_compareConfigurations' ) def manage_compareConfigurations( self , lhs , rhs , missing_as_empty , ignore_blanks ): """ Crack request vars and call compareConfigurations. """ lhs_context = self._getImportContext( lhs ) rhs_context = self._getImportContext( rhs ) return self.compareConfigurations( lhs_context , rhs_context , missing_as_empty , ignore_blanks ) # # Helper methods # security.declarePrivate( '_getProductPath' ) def _getProductPath( self, product_name ): """ Return the absolute path of the product's directory. """ try: product = __import__( 'Products.%s' % product_name , globals(), {}, ['initialize' ] ) except ImportError: raise ValueError, 'Not a valid product name: %s' % product_name return product.__path__[0] security.declarePrivate( '_getImportContext' ) def _getImportContext( self, context_id, should_purge=False ): """ Crack ID and generate appropriate import context. """ encoding = self.getEncoding() if context_id.startswith( 'profile-' ): context_id = context_id[ len( 'profile-' ): ] info = _profile_registry.getProfileInfo( context_id ) if info.get( 'product' ): path = os.path.join( self._getProductPath( info[ 'product' ] ) , info[ 'path' ] ) else: path = info[ 'path' ] return DirectoryImportContext(self, path, should_purge, encoding) # else snapshot context_id = context_id[ len( 'snapshot-' ): ] return SnapshotImportContext(self, context_id, should_purge, encoding) security.declarePrivate( '_updateImportStepsRegistry' ) def _updateImportStepsRegistry( self, encoding ): """ Update our import steps registry from our profile. """ context = self._getImportContext(self._import_context_id) xml = context.readDataFile(IMPORT_STEPS_XML) info_list = self._import_registry.parseXML( xml, encoding ) for step_info in info_list: id = step_info[ 'id' ] version = step_info[ 'version' ] handler = _resolveDottedName( step_info[ 'handler' ] ) dependencies = tuple( step_info.get( 'dependencies', () ) ) title = step_info.get( 'title', id ) description = ''.join( step_info.get( 'description', [] ) ) self._import_registry.registerStep( id=id , version=version , handler=handler , dependencies=dependencies , title=title , description=description ) security.declarePrivate( '_updateExportStepsRegistry' ) def _updateExportStepsRegistry( self, encoding ): """ Update our export steps registry from our profile. """ context = self._getImportContext(self._import_context_id) xml = context.readDataFile(EXPORT_STEPS_XML) info_list = self._export_registry.parseXML( xml, encoding ) for step_info in info_list: id = step_info[ 'id' ] handler = _resolveDottedName( step_info[ 'handler' ] ) title = step_info.get( 'title', id ) description = ''.join( step_info.get( 'description', [] ) ) self._export_registry.registerStep( id=id , handler=handler , title=title , description=description ) security.declarePrivate( '_updateToolsetRegistry' ) def _updateToolsetRegistry( self, encoding ): """ Update our toolset registry from our profile. """ context = self._getImportContext(self._import_context_id) xml = context.readDataFile(TOOLSET_XML) self._toolset_registry.parseXML( xml, encoding ) security.declarePrivate( '_doRunImportStep' ) def _doRunImportStep( self, step_id, context ): """ Run a single import step, using a pre-built context. """ handler = self._import_registry.getStep( step_id ) if handler is None: raise ValueError( 'Invalid import step: %s' % step_id ) return handler( context ) security.declarePrivate( '_doRunExportSteps') def _doRunExportSteps( self, steps ): """ See ISetupTool. """ context = TarballExportContext( self ) messages = {} for step_id in steps: handler = self._export_registry.getStep( step_id ) if handler is None: raise ValueError( 'Invalid export step: %s' % step_id ) messages[ step_id ] = handler( context ) return { 'steps' : steps , 'messages' : messages , 'tarball' : context.getArchive() , 'filename' : context.getArchiveFilename() }
class SetupTool(UniqueObject, Folder): """ Profile-based site configuration manager. """ __implements__ = (ISetupTool, ) + Folder.__implements__ id = 'portal_setup' meta_type = 'Portal Setup Tool' _import_context_id = '' security = ClassSecurityInfo() def __init__(self): self._import_registry = ImportStepRegistry() self._export_registry = ExportStepRegistry() self._export_registry.registerStep('step_registries', exportStepRegistries, 'Export import / export steps.') self._toolset_registry = ToolsetRegistry() # # ISetupTool API # security.declareProtected(ManagePortal, 'getEncoding') def getEncoding(self): """ See ISetupTool. """ return 'ascii' security.declareProtected(ManagePortal, 'getImportContextId') def getImportContextID(self): """ See ISetupTool. """ return self._import_context_id security.declareProtected(ManagePortal, 'setImportContext') def setImportContext(self, context_id, encoding=None): """ See ISetupTool. """ self._import_context_id = context_id self._updateImportStepsRegistry(encoding) self._updateExportStepsRegistry(encoding) self._updateToolsetRegistry(encoding) security.declareProtected(ManagePortal, 'getImportStepRegistry') def getImportStepRegistry(self): """ See ISetupTool. """ return self._import_registry security.declareProtected(ManagePortal, 'getImportStepRegistry') def getExportStepRegistry(self): """ See ISetupTool. """ return self._export_registry security.declareProtected(ManagePortal, 'getToolsetRegistry') def getToolsetRegistry(self): """ See ISetupTool. """ return self._toolset_registry security.declareProtected(ManagePortal, 'executeStep') def runImportStep(self, step_id, run_dependencies=True, purge_old=None): """ See ISetupTool. """ context = self._getImportContext(self._import_context_id, purge_old) info = self._import_registry.getStepMetadata(step_id) if info is None: raise ValueError, 'No such import step: %s' % step_id dependencies = info.get('dependencies', ()) messages = {} steps = [] if run_dependencies: for dependency in dependencies: if dependency not in steps: message = self._doRunImportStep(dependency, context) messages[dependency] = message steps.append(dependency) message = self._doRunImportStep(step_id, context) messages[step_id] = message steps.append(step_id) return {'steps': steps, 'messages': messages} security.declareProtected(ManagePortal, 'runAllSetupSteps') def runAllImportSteps(self, purge_old=None): """ See ISetupTool. """ context = self._getImportContext(self._import_context_id, purge_old) steps = self._import_registry.sortSteps() messages = {} for step in steps: message = self._doRunImportStep(step, context) messages[step] = message return {'steps': steps, 'messages': messages} security.declareProtected(ManagePortal, 'runExportStep') def runExportStep(self, step_id): """ See ISetupTool. """ return self._doRunExportSteps([step_id]) security.declareProtected(ManagePortal, 'runAllExportSteps') def runAllExportSteps(self): """ See ISetupTool. """ return self._doRunExportSteps(self._export_registry.listSteps()) security.declareProtected(ManagePortal, 'createSnapshot') def createSnapshot(self, snapshot_id): """ See ISetupTool. """ context = SnapshotExportContext(self, snapshot_id) messages = {} steps = self._export_registry.listSteps() for step_id in steps: handler = self._export_registry.getStep(step_id) if handler is None: raise ValueError('Invalid export step: %s' % step_id) messages[step_id] = handler(context) return { 'steps': steps, 'messages': messages, 'url': context.getSnapshotURL(), 'snapshot': context.getSnapshotFolder() } security.declareProtected(ManagePortal, 'compareConfigurations') def compareConfigurations(self, lhs_context, rhs_context, missing_as_empty=False, ignore_blanks=False, skip=('CVS', '.svn')): """ See ISetupTool. """ differ = ConfigDiff(lhs_context, rhs_context, missing_as_empty, ignore_blanks, skip) return differ.compare() security.declareProtected(ManagePortal, 'markupComparison') def markupComparison(self, lines): """ See ISetupTool. """ result = [] for line in lines.splitlines(): if line.startswith('** '): if line.find('File') > -1: if line.find('replaced') > -1: result.append(('file-to-dir', line)) elif line.find('added') > -1: result.append(('file-added', line)) else: result.append(('file-removed', line)) else: if line.find('replaced') > -1: result.append(('dir-to-file', line)) elif line.find('added') > -1: result.append(('dir-added', line)) else: result.append(('dir-removed', line)) elif line.startswith('@@'): result.append(('diff-range', line)) elif line.startswith(' '): result.append(('diff-context', line)) elif line.startswith('+'): result.append(('diff-added', line)) elif line.startswith('-'): result.append(('diff-removed', line)) elif line == '\ No newline at end of file': result.append(('diff-context', line)) else: result.append(('diff-header', line)) return '<pre>\n%s\n</pre>' % ('\n'.join( [('<span class="%s">%s</span>' % (cl, escape(l))) for cl, l in result])) # # ZMI # manage_options = ( Folder.manage_options[:1] + ({ 'label': 'Properties', 'action': 'manage_tool' }, { 'label': 'Import', 'action': 'manage_importSteps' }, { 'label': 'Export', 'action': 'manage_exportSteps' }, { 'label': 'Snapshots', 'action': 'manage_snapshots' }, { 'label': 'Comparison', 'action': 'manage_showDiff' }) + Folder.manage_options[3:] # skip "View", "Properties" ) security.declareProtected(ManagePortal, 'manage_tool') manage_tool = PageTemplateFile('sutProperties', _wwwdir) security.declareProtected(ManagePortal, 'manage_updateToolProperties') def manage_updateToolProperties(self, context_id, RESPONSE): """ Update the tool's settings. """ self.setImportContext(context_id) RESPONSE.redirect('%s/manage_tool?manage_tabs_message=%s' % (self.absolute_url(), 'Properties+updated.')) security.declareProtected(ManagePortal, 'manage_importSteps') manage_importSteps = PageTemplateFile('sutImportSteps', _wwwdir) security.declareProtected(ManagePortal, 'manage_importSelectedSteps') def manage_importSelectedSteps(self, ids, run_dependencies, RESPONSE): """ Import the steps selected by the user. """ if not ids: message = 'No+steps+selected.' else: steps_run = [] for step_id in ids: result = self.runImportStep(step_id, run_dependencies) steps_run.extend(result['steps']) message = 'Steps+run:%s' % '+,'.join(steps_run) RESPONSE.redirect('%s/manage_importSteps?manage_tabs_message=%s' % (self.absolute_url(), message)) security.declareProtected(ManagePortal, 'manage_importSelectedSteps') def manage_importAllSteps(self, RESPONSE): """ Import all steps. """ result = self.runAllImportSteps() message = 'Steps+run:%s' % '+,'.join(result['steps']) RESPONSE.redirect('%s/manage_importSteps?manage_tabs_message=%s' % (self.absolute_url(), message)) security.declareProtected(ManagePortal, 'manage_exportSteps') manage_exportSteps = PageTemplateFile('sutExportSteps', _wwwdir) security.declareProtected(ManagePortal, 'manage_exportSelectedSteps') def manage_exportSelectedSteps(self, ids, RESPONSE): """ Export the steps selected by the user. """ if not ids: RESPONSE.redirect('%s/manage_exportSteps?manage_tabs_message=%s' % (self.absolute_url(), 'No+steps+selected.')) result = self._doRunExportSteps(ids) RESPONSE.setHeader('Content-type', 'application/x-gzip') RESPONSE.setHeader('Content-disposition', 'attachment; filename=%s' % result['filename']) return result['tarball'] security.declareProtected(ManagePortal, 'manage_exportAllSteps') def manage_exportAllSteps(self, RESPONSE): """ Export all steps. """ result = self.runAllExportSteps() RESPONSE.setHeader('Content-type', 'application/x-gzip') RESPONSE.setHeader('Content-disposition', 'attachment; filename=%s' % result['filename']) return result['tarball'] security.declareProtected(ManagePortal, 'manage_snapshots') manage_snapshots = PageTemplateFile('sutSnapshots', _wwwdir) security.declareProtected(ManagePortal, 'listSnapshotInfo') def listSnapshotInfo(self): """ Return a list of mappings describing available snapshots. o Keys include: 'id' -- snapshot ID 'title' -- snapshot title or ID 'url' -- URL of the snapshot folder """ result = [] snapshots = self._getOb('snapshots', None) if snapshots: for id, folder in snapshots.objectItems('Folder'): result.append({ 'id': id, 'title': folder.title_or_id(), 'url': folder.absolute_url() }) return result security.declareProtected(ManagePortal, 'listProfileInfo') def listProfileInfo(self): """ Return a list of mappings describing registered profiles. o Keys include: 'id' -- profile ID 'title' -- profile title or ID 'description' -- description of the profile 'path' -- path to the profile within its product 'product' -- name of the registering product """ return _profile_registry.listProfileInfo() security.declareProtected(ManagePortal, 'listContextInfos') def listContextInfos(self): """ List registered profiles and snapshots. """ s_infos = [{ 'id': 'snapshot-%s' % info['id'], 'title': info['title'] } for info in self.listSnapshotInfo()] p_infos = [{ 'id': 'profile-%s' % info['id'], 'title': info['title'] } for info in self.listProfileInfo()] return tuple(s_infos + p_infos) security.declareProtected(ManagePortal, 'manage_createSnapshot') def manage_createSnapshot(self, RESPONSE, snapshot_id=None): """ Create a snapshot with the given ID. o If no ID is passed, generate one. """ if snapshot_id is None: timestamp = time.gmtime() snapshot_id = 'snapshot-%4d%02d%02d%02d%02d%02d' % timestamp[:6] self.createSnapshot(snapshot_id) RESPONSE.redirect('%s/manage_snapshots?manage_tabs_message=%s' % (self.absolute_url(), 'Snapshot+created.')) security.declareProtected(ManagePortal, 'manage_showDiff') manage_showDiff = PageTemplateFile('sutCompare', _wwwdir) def manage_downloadDiff(self, lhs, rhs, missing_as_empty, ignore_blanks, RESPONSE): """ Crack request vars and call compareConfigurations. o Return the result as a 'text/plain' stream, suitable for framing. """ comparison = self.manage_compareConfigurations(lhs, rhs, missing_as_empty, ignore_blanks) RESPONSE.setHeader('Content-Type', 'text/plain') return _PLAINTEXT_DIFF_HEADER % (lhs, rhs, comparison) security.declareProtected(ManagePortal, 'manage_compareConfigurations') def manage_compareConfigurations(self, lhs, rhs, missing_as_empty, ignore_blanks): """ Crack request vars and call compareConfigurations. """ lhs_context = self._getImportContext(lhs) rhs_context = self._getImportContext(rhs) return self.compareConfigurations(lhs_context, rhs_context, missing_as_empty, ignore_blanks) # # Helper methods # security.declarePrivate('_getProductPath') def _getProductPath(self, product_name): """ Return the absolute path of the product's directory. """ try: product = __import__('Products.%s' % product_name, globals(), {}, ['initialize']) except ImportError: raise ValueError, 'Not a valid product name: %s' % product_name return product.__path__[0] security.declarePrivate('_getImportContext') def _getImportContext(self, context_id, should_purge=None): """ Crack ID and generate appropriate import context. """ encoding = self.getEncoding() if context_id.startswith('profile-'): context_id = context_id[len('profile-'):] info = _profile_registry.getProfileInfo(context_id) if info.get('product'): path = os.path.join(self._getProductPath(info['product']), info['path']) else: path = info['path'] if should_purge is None: should_purge = (info.get('type') != EXTENSION) return DirectoryImportContext(self, path, should_purge, encoding) # else snapshot context_id = context_id[len('snapshot-'):] if should_purge is None: should_purge = True return SnapshotImportContext(self, context_id, should_purge, encoding) security.declarePrivate('_updateImportStepsRegistry') def _updateImportStepsRegistry(self, encoding): """ Update our import steps registry from our profile. """ context = self._getImportContext(self._import_context_id) xml = context.readDataFile(IMPORT_STEPS_XML) if xml is None: return info_list = self._import_registry.parseXML(xml, encoding) for step_info in info_list: id = step_info['id'] version = step_info['version'] handler = _resolveDottedName(step_info['handler']) dependencies = tuple(step_info.get('dependencies', ())) title = step_info.get('title', id) description = ''.join(step_info.get('description', [])) self._import_registry.registerStep(id=id, version=version, handler=handler, dependencies=dependencies, title=title, description=description) security.declarePrivate('_updateExportStepsRegistry') def _updateExportStepsRegistry(self, encoding): """ Update our export steps registry from our profile. """ context = self._getImportContext(self._import_context_id) xml = context.readDataFile(EXPORT_STEPS_XML) if xml is None: return info_list = self._export_registry.parseXML(xml, encoding) for step_info in info_list: id = step_info['id'] handler = _resolveDottedName(step_info['handler']) title = step_info.get('title', id) description = ''.join(step_info.get('description', [])) self._export_registry.registerStep(id=id, handler=handler, title=title, description=description) security.declarePrivate('_updateToolsetRegistry') def _updateToolsetRegistry(self, encoding): """ Update our toolset registry from our profile. """ context = self._getImportContext(self._import_context_id) xml = context.readDataFile(TOOLSET_XML) if xml is None: return self._toolset_registry.parseXML(xml, encoding) security.declarePrivate('_doRunImportStep') def _doRunImportStep(self, step_id, context): """ Run a single import step, using a pre-built context. """ handler = self._import_registry.getStep(step_id) if handler is None: raise ValueError('Invalid import step: %s' % step_id) return handler(context) security.declarePrivate('_doRunExportSteps') def _doRunExportSteps(self, steps): """ See ISetupTool. """ context = TarballExportContext(self) messages = {} for step_id in steps: handler = self._export_registry.getStep(step_id) if handler is None: raise ValueError('Invalid export step: %s' % step_id) messages[step_id] = handler(context) return { 'steps': steps, 'messages': messages, 'tarball': context.getArchive(), 'filename': context.getArchiveFilename() }
class SetupTool(UniqueObject, Folder): """ Profile-based site configuration manager. """ __implements__ = (ISetupTool, ) + Folder.__implements__ id = 'portal_setup' meta_type = 'Portal Setup Tool' _product_name = None _profile_directory = None _root_directory = None security = ClassSecurityInfo() def __init__(self): self._import_registry = ImportStepRegistry() self._export_registry = ExportStepRegistry() self._export_registry.registerStep('step_registries', exportStepRegistries, 'Export import / export steps.') self._toolset_registry = ToolsetRegistry() # # ISetupTool API # security.declareProtected(ManagePortal, 'getProfileProduct') def getProfileProduct(self): """ See ISetupTool. """ return self._product_name security.declareProtected(ManagePortal, 'getProfileDirectory') def getProfileDirectory(self, relative_to_product=False): """ See ISetupTool. """ return (relative_to_product and self._profile_directory or self._getFullyQualifiedProfileDirectory()) security.declareProtected(ManagePortal, 'setProfileDirectory') def setProfileDirectory(self, path, product_name=None, encoding=None): """ See ISetupTool. """ if product_name is not None: try: product = __import__('Products.%s' % product_name, globals(), {}, ['initialize']) except ImportError: raise ValueError, 'Not a valid product name: %s' % product_name root = self._root_directory = product.__path__[0] if not os.path.exists(os.path.join(root, path)): raise ValueError, 'Invalid path: %s' % path else: if not os.path.exists(path): raise ValueError, 'Invalid path: %s' % path self._root_directory = None self._profile_directory = path self._product_name = product_name self._updateImportStepsRegistry(encoding) self._updateExportStepsRegistry(encoding) self._updateToolsetRegistry(encoding) security.declareProtected(ManagePortal, 'getImportStepRegistry') def getImportStepRegistry(self): """ See ISetupTool. """ return self._import_registry security.declareProtected(ManagePortal, 'getImportStepRegistry') def getExportStepRegistry(self): """ See ISetupTool. """ return self._export_registry security.declareProtected(ManagePortal, 'getToolsetRegistry') def getToolsetRegistry(self): """ See ISetupTool. """ return self._toolset_registry security.declareProtected(ManagePortal, 'executeStep') def runImportStep(self, step_id, run_dependencies=True, purge_old=True): """ See ISetupTool. """ profile_path = self._getFullyQualifiedProfileDirectory() context = ImportContext(self, profile_path, purge_old) info = self._import_registry.getStepMetadata(step_id) if info is None: raise ValueError, 'No such import step: %s' % step_id dependencies = info.get('dependencies', ()) messages = {} steps = [] if run_dependencies: for dependency in dependencies: if dependency not in steps: message = self._doRunImportStep(dependency, context) messages[dependency] = message steps.append(dependency) message = self._doRunImportStep(step_id, context) messages[step_id] = message steps.append(step_id) return {'steps': steps, 'messages': messages} security.declareProtected(ManagePortal, 'runAllSetupSteps') def runAllImportSteps(self, purge_old=True): """ See ISetupTool. """ profile_path = self._getFullyQualifiedProfileDirectory() context = ImportContext(self, profile_path, purge_old) steps = self._import_registry.sortSteps() messages = {} for step in steps: message = self._doRunImportStep(step, context) messages[step] = message return {'steps': steps, 'messages': messages} security.declareProtected(ManagePortal, 'runExportStep') def runExportStep(self, step_id): """ See ISetupTool. """ return self._doRunExportSteps([step_id]) security.declareProtected(ManagePortal, 'runAllExportSteps') def runAllExportSteps(self): """ See ISetupTool. """ return self._doRunExportSteps(self._export_registry.listSteps()) security.declareProtected(ManagePortal, 'createSnapshot') def createSnapshot(self, snapshot_id): """ See ISetupTool. """ context = SnapshotExportContext(self, snapshot_id) messages = {} steps = self._export_registry.listSteps() for step_id in steps: handler = self._export_registry.getStep(step_id) if handler is None: raise ValueError('Invalid export step: %s' % step_id) messages[step_id] = handler(context) return { 'steps': steps, 'messages': messages, 'url': context.getSnapshotURL(), 'snapshot': context.getSnapshotFolder() } security.declareProtected(ManagePortal, 'compareConfigurations') def compareConfigurations(self, source1, source2, missing_as_empty=False, ignore_whitespace=False): """ See ISetupTool. """ raise NotImplementedError security.declareProtected(ManagePortal, 'markupComparison') def markupComparison(self, lines): """ See ISetupTool. """ raise NotImplementedError # # ZMI # manage_options = ( Folder.manage_options[:1] + ({ 'label': 'Properties', 'action': 'manage_tool' }, { 'label': 'Import', 'action': 'manage_importSteps' }, { 'label': 'Export', 'action': 'manage_exportSteps' }, { 'label': 'Snapshots', 'action': 'manage_snapshots' }) + Folder.manage_options[3:] # skip "View", "Properties" ) security.declareProtected(ManagePortal, 'manage_tool') manage_tool = PageTemplateFile('sutProperties', _wwwdir) security.declareProtected(ManagePortal, 'manage_updateToolProperties') def manage_updateToolProperties(self, profile_directory, profile_product, RESPONSE): """ Update the tool's settings. """ profile_directory = profile_directory.strip() profile_product = profile_product.strip() if profile_directory.startswith('.'): raise ValueError("Directories begining with '.' are not allowed.") if profile_product and profile_directory.startswith('/'): raise ValueError( "Product may not be specified with absolute directories") self.setProfileDirectory(profile_directory, profile_product) RESPONSE.redirect('%s/manage_tool?manage_tabs_message=%s' % (self.absolute_url(), 'Properties+updated.')) security.declareProtected(ManagePortal, 'manage_importSteps') manage_importSteps = PageTemplateFile('sutImportSteps', _wwwdir) security.declareProtected(ManagePortal, 'manage_importSelectedSteps') def manage_importSelectedSteps(self, ids, run_dependencies, purge_old, RESPONSE): """ Import the steps selected by the user. """ if not ids: message = 'No+steps+selected.' else: steps_run = [] for step_id in ids: result = self.runImportStep(step_id, run_dependencies, purge_old) steps_run.extend(result['steps']) message = 'Steps+run:%s' % '+,'.join(steps_run) RESPONSE.redirect('%s/manage_importSteps?manage_tabs_message=%s' % (self.absolute_url(), message)) security.declareProtected(ManagePortal, 'manage_importSelectedSteps') def manage_importAllSteps(self, purge_old, RESPONSE): """ Import all steps. """ result = self.runAllImportSteps(purge_old) message = 'Steps+run:%s' % '+,'.join(result['steps']) RESPONSE.redirect('%s/manage_importSteps?manage_tabs_message=%s' % (self.absolute_url(), message)) security.declareProtected(ManagePortal, 'manage_exportSteps') manage_exportSteps = PageTemplateFile('sutExportSteps', _wwwdir) security.declareProtected(ManagePortal, 'manage_exportSelectedSteps') def manage_exportSelectedSteps(self, ids, RESPONSE): """ Export the steps selected by the user. """ if not ids: RESPONSE.redirect('%s/manage_exportSteps?manage_tabs_message=%s' % (self.absolute_url(), 'No+steps+selected.')) result = self._doRunExportSteps(ids) RESPONSE.setHeader('Content-type', 'application/x-gzip') RESPONSE.setHeader('Content-disposition', 'attachment; filename=%s' % result['filename']) return result['tarball'] security.declareProtected(ManagePortal, 'manage_exportAllSteps') def manage_exportAllSteps(self, RESPONSE): """ Export all steps. """ result = self.runAllExportSteps() RESPONSE.setHeader('Content-type', 'application/x-gzip') RESPONSE.setHeader('Content-disposition', 'attachment; filename=%s' % result['filename']) return result['tarball'] security.declareProtected(ManagePortal, 'manage_snapshots') manage_snapshots = PageTemplateFile('sutSnapshots', _wwwdir) security.declareProtected(ManagePortal, 'listSnapshotInfo') def listSnapshotInfo(self): """ Return a list of mappings describing available snapshots. o Keys include: 'id' -- snapshot ID 'title' -- snapshot title or ID 'url' -- URL of the snapshot folder o ZMI support. """ result = [] snapshots = self._getOb('snapshots', None) if snapshots: for id, folder in snapshots.objectItems('Folder'): result.append({ 'id': id, 'title': folder.title_or_id(), 'url': folder.absolute_url() }) return result security.declareProtected(ManagePortal, 'manage_createSnapshot') def manage_createSnapshot(self, RESPONSE, snapshot_id=None): """ Create a snapshot with the given ID. o If no ID is passed, generate one. """ if snapshot_id is None: timestamp = time.gmtime() snapshot_id = 'snapshot-%4d%02d%02d%02d%02d%02d' % timestamp[:6] self.createSnapshot(snapshot_id) RESPONSE.redirect('%s/manage_snapshots?manage_tabs_message=%s' % (self.absolute_url(), 'Snapshot+created.')) # # Helper methods # security.declarePrivate('_getFullyQualifiedProfileDirectory') def _getFullyQualifiedProfileDirectory(self): """ Return the fully-qualified directory path of our profile. """ if self._root_directory is not None: return os.path.join(self._root_directory, self._profile_directory) return self._profile_directory security.declarePrivate('_updateImportStepsRegistry') def _updateImportStepsRegistry(self, encoding): """ Update our import steps registry from our profile. """ fq = self._getFullyQualifiedProfileDirectory() f = open(os.path.join(fq, IMPORT_STEPS_XML), 'r') xml = f.read() f.close() info_list = self._import_registry.parseXML(xml, encoding) for step_info in info_list: id = step_info['id'] version = step_info['version'] handler = _resolveDottedName(step_info['handler']) dependencies = tuple(step_info.get('dependencies', ())) title = step_info.get('title', id) description = ''.join(step_info.get('description', [])) self._import_registry.registerStep(id=id, version=version, handler=handler, dependencies=dependencies, title=title, description=description) security.declarePrivate('_updateExportStepsRegistry') def _updateExportStepsRegistry(self, encoding): """ Update our export steps registry from our profile. """ fq = self._getFullyQualifiedProfileDirectory() f = open(os.path.join(fq, EXPORT_STEPS_XML), 'r') xml = f.read() f.close() info_list = self._export_registry.parseXML(xml, encoding) for step_info in info_list: id = step_info['id'] handler = _resolveDottedName(step_info['handler']) title = step_info.get('title', id) description = ''.join(step_info.get('description', [])) self._export_registry.registerStep(id=id, handler=handler, title=title, description=description) security.declarePrivate('_updateToolsetRegistry') def _updateToolsetRegistry(self, encoding): """ Update our toolset registry from our profile. """ fq = self._getFullyQualifiedProfileDirectory() f = open(os.path.join(fq, TOOLSET_XML), 'r') xml = f.read() f.close() self._toolset_registry.parseXML(xml, encoding) security.declarePrivate('_doRunImportStep') def _doRunImportStep(self, step_id, context): """ Run a single import step, using a pre-built context. """ handler = self._import_registry.getStep(step_id) if handler is None: raise ValueError('Invalid import step: %s' % step_id) return handler(context) security.declarePrivate('_doRunExportSteps') def _doRunExportSteps(self, steps): """ See ISetupTool. """ context = TarballExportContext(self) messages = {} for step_id in steps: handler = self._export_registry.getStep(step_id) if handler is None: raise ValueError('Invalid export step: %s' % step_id) messages[step_id] = handler(context) return { 'steps': steps, 'messages': messages, 'tarball': context.getArchive(), 'filename': context.getArchiveFilename() }