示例#1
0
class ImportStepRegistry( Implicit ):

    """ Manage knowledge about steps to create / configure site.

    o Steps are composed together to define a site profile.
    """
    implements(IImportStepRegistry)

    security = ClassSecurityInfo()

    def __init__( self ):

        self.clear()

    security.declareProtected( ManagePortal, 'listSteps' )
    def listSteps( self ):

        """ Return a sequence of IDs of registered steps.

        o Order is not significant.
        """
        return self._registered.keys()

    security.declareProtected( ManagePortal, 'sortSteps' )
    def sortSteps( self ):

        """ Return a sequence of registered step IDs

        o Sequence is sorted topologically by dependency, with the dependent
          steps *after* the steps they depend on.
        """
        return self._computeTopologicalSort()

    security.declareProtected( ManagePortal, 'checkComplete' )
    def checkComplete( self ):

        """ Return a sequence of ( node, edge ) tuples for unsatisifed deps.
        """
        result = []
        seen = {}

        graph = self._computeTopologicalSort()

        for node in graph:

            dependencies = self.getStepMetadata( node )[ 'dependencies' ]

            for dependency in dependencies:

                if seen.get( dependency ) is None:
                    result.append( ( node, dependency ) )

            seen[ node ] = 1

        return result

    security.declareProtected( ManagePortal, 'getStepMetadata' )
    def getStepMetadata( self, key, default=None ):

        """ Return a mapping of metadata for the step identified by 'key'.

        o Return 'default' if no such step is registered.

        o The 'handler' metadata is available via 'getStep'.
        """
        result = {}

        info = self._registered.get( key )

        if info is None:
            return default

        return info.copy()

    security.declareProtected( ManagePortal, 'listStepMetadata' )
    def listStepMetadata( self ):

        """ Return a sequence of mappings describing registered steps.

        o Mappings will be ordered alphabetically.
        """
        step_ids = self.listSteps()
        step_ids.sort()
        return [ self.getStepMetadata( x ) for x in step_ids ]

    security.declareProtected( ManagePortal, 'generateXML' )
    def generateXML( self ):

        """ Return a round-trippable XML representation of the registry.

        o 'handler' values are serialized using their dotted names.
        """
        return self._exportTemplate()

    security.declarePrivate( 'getStep' )
    def getStep( self, key, default=None ):

        """ Return the IImportPlugin registered for 'key'.

        o Return 'default' if no such step is registered.
        """
        marker = object()
        info = self._registered.get( key, marker )

        if info is marker:
            return default

        return _resolveDottedName( info[ 'handler' ] )

    security.declarePrivate( 'registerStep' )
    def registerStep( self
                    , id
                    , version
                    , handler
                    , dependencies=()
                    , title=None
                    , description=None
                    ):
        """ Register a setup step.

        o 'id' is a unique name for this step,

        o 'version' is a string for comparing versions, it is preferred to
          be a yyyy/mm/dd-ii formatted string (date plus two-digit
          ordinal).  when comparing two version strings, the version with
          the lower sort order is considered the older version.

          - Newer versions of a step supplant older ones.

          - Attempting to register an older one after a newer one results
            in a KeyError.

        o 'handler' should implement IImportPlugin.

        o 'dependencies' is a tuple of step ids which have to run before
          this step in order to be able to run at all. Registration of
          steps that have unmet dependencies are deferred until the
          dependencies have been registered.

        o 'title' is a one-line UI description for this step.
          If None, the first line of the documentation string of the handler
          is used, or the id if no docstring can be found.

        o 'description' is a one-line UI description for this step.
          If None, the remaining line of the documentation string of
          the handler is used, or default to ''.
        """
        already = self.getStepMetadata( id )

        if already and already[ 'version' ] > version:
            raise KeyError( 'Existing registration for step %s, version %s'
                          % ( id, already[ 'version' ] ) )

        if title is None or description is None:

            t, d = _extractDocstring( handler, id, '' )

            title = title or t
            description = description or d

        info = { 'id'           : id
               , 'version'      : version
               , 'handler'      : _getDottedName( handler )
               , 'dependencies' : dependencies
               , 'title'        : title
               , 'description'  : description
               }

        self._registered[ id ] = info

    security.declarePrivate( 'parseXML' )
    def parseXML( self, text, encoding=None ):

        """ Parse 'text'.
        """
        reader = getattr( text, 'read', None )

        if reader is not None:
            text = reader()

        parser = _ImportStepRegistryParser( encoding )
        parseString( text, parser )

        return parser._parsed

    security.declarePrivate( 'clear' )
    def clear( self ):

        self._registered = {}

    #
    #   Helper methods
    #
    security.declarePrivate( '_computeTopologicalSort' )
    def _computeTopologicalSort( self ):

        result = []

        graph = [ ( x[ 'id' ], x[ 'dependencies' ] )
                    for x in self._registered.values() ]

        for node, edges in graph:

            after = -1

            for edge in edges:

                if edge in result:
                    after = max( after, result.index( edge ) )

            result.insert( after + 1, node )

        return result

    security.declarePrivate( '_exportTemplate' )
    _exportTemplate = PageTemplateResource( 'xml/isrExport.xml', globals() )
示例#2
0
class ToolsetRegistry( Implicit ):

    """ Track required / forbidden tools.
    """
    implements(IToolsetRegistry)

    security = ClassSecurityInfo()
    security.setDefaultAccess( 'allow' )

    def __init__( self ):

        self.clear()

    #
    #   Toolset API
    #
    security.declareProtected( ManagePortal, 'listForbiddenTools' )
    def listForbiddenTools( self ):

        """ See IToolsetRegistry.
        """
        result = list( self._forbidden )
        result.sort()
        return result

    security.declareProtected( ManagePortal, 'addForbiddenTool' )
    def addForbiddenTool( self, tool_id ):

        """ See IToolsetRegistry.
        """
        if tool_id in self._forbidden:
            return

        if self._required.get( tool_id ) is not None:
            raise ValueError, 'Tool %s is required!' % tool_id

        self._forbidden.append( tool_id )

    security.declareProtected( ManagePortal, 'listRequiredTools' )
    def listRequiredTools( self ):

        """ See IToolsetRegistry.
        """
        result = list( self._required.keys() )
        result.sort()
        return result

    security.declareProtected( ManagePortal, 'getRequiredToolInfo' )
    def getRequiredToolInfo( self, tool_id ):

        """ See IToolsetRegistry.
        """
        return self._required[ tool_id ]

    security.declareProtected( ManagePortal, 'listRequiredToolInfo' )
    def listRequiredToolInfo( self ):

        """ See IToolsetRegistry.
        """
        return [ self.getRequiredToolInfo( x )
                        for x in self.listRequiredTools() ]

    security.declareProtected( ManagePortal, 'addRequiredTool' )
    def addRequiredTool( self, tool_id, dotted_name ):

        """ See IToolsetRegistry.
        """
        if tool_id in self._forbidden:
            raise ValueError, "Forbidden tool ID: %s" % tool_id

        self._required[ tool_id ] = { 'id' : tool_id
                                    , 'class' : dotted_name
                                    }

    security.declareProtected( ManagePortal, 'generateXML' )
    def generateXML( self ):

        """ Pseudo API.
        """
        return self._toolsetConfig()

    security.declareProtected( ManagePortal, 'parseXML' )
    def parseXML( self, text, encoding=None ):

        """ Pseudo-API
        """
        reader = getattr( text, 'read', None )

        if reader is not None:
            text = reader()

        parser = _ToolsetParser( encoding )
        parseString( text, parser )

        for tool_id in parser._forbidden:
            self.addForbiddenTool( tool_id )

        for tool_id, dotted_name in parser._required.items():
            self.addRequiredTool( tool_id, dotted_name )

    security.declarePrivate( 'clear' )
    def clear( self ):

        self._forbidden = []
        self._required = {}

    #
    #   Helper methods.
    #
    security.declarePrivate( '_toolsetConfig' )
    _toolsetConfig = PageTemplateResource( 'xml/tscExport.xml'
                                         , globals()
                                         , __name__='toolsetConfig'
                                         )
示例#3
0
    def _getExportTemplate(self):

        return PageTemplateResource('xml/rmeExport.xml', globals())
示例#4
0
class ExportStepRegistry( Implicit ):

    """ Registry of known site-configuration export steps.

    o Each step is registered with a unique id.

    o When called, with the portal object passed in as an argument,
      the step must return a sequence of three-tuples,
      ( 'data', 'content_type', 'filename' ), one for each file exported
      by the step.

      - 'data' is a string containing the file data;

      - 'content_type' is the MIME type of the data;

      - 'filename' is a suggested filename for use when downloading.

    """
    implements(IExportStepRegistry)

    security = ClassSecurityInfo()

    def __init__( self ):

        self.clear()

    security.declareProtected( ManagePortal, 'listSteps' )
    def listSteps( self ):

        """ Return a list of registered step IDs.
        """
        return self._registered.keys()

    security.declareProtected( ManagePortal, 'getStepMetadata' )
    def getStepMetadata( self, key, default=None ):

        """ Return a mapping of metadata for the step identified by 'key'.

        o Return 'default' if no such step is registered.

        o The 'handler' metadata is available via 'getStep'.
        """
        info = self._registered.get( key )

        if info is None:
            return default

        return info.copy()

    security.declareProtected( ManagePortal, 'listStepMetadata' )
    def listStepMetadata( self ):

        """ Return a sequence of mappings describing registered steps.

        o Steps will be alphabetical by ID.
        """
        step_ids = self.listSteps()
        step_ids.sort()
        return [ self.getStepMetadata( x ) for x in step_ids ]

    security.declareProtected( ManagePortal, 'generateXML' )
    def generateXML( self ):

        """ Return a round-trippable XML representation of the registry.

        o 'handler' values are serialized using their dotted names.
        """
        return self._exportTemplate()

    security.declarePrivate( 'getStep' )
    def getStep( self, key, default=None ):

        """ Return the IExportPlugin registered for 'key'.

        o Return 'default' if no such step is registered.
        """
        marker = object()
        info = self._registered.get( key, marker )

        if info is marker:
            return default

        return _resolveDottedName( info[ 'handler' ] )

    security.declarePrivate( 'registerStep' )
    def registerStep( self, id, handler, title=None, description=None ):

        """ Register an export step.

        o 'id' is the unique identifier for this step

        o 'step' should implement IExportPlugin.

        o 'title' is a one-line UI description for this step.
          If None, the first line of the documentation string of the step
          is used, or the id if no docstring can be found.

        o 'description' is a one-line UI description for this step.
          If None, the remaining line of the documentation string of
          the step is used, or default to ''.
        """
        if title is None or description is None:

            t, d = _extractDocstring( handler, id, '' )

            title = title or t
            description = description or d

        info = { 'id'           : id
               , 'handler'      : _getDottedName( handler )
               , 'title'        : title
               , 'description'  : description
               }

        self._registered[ id ] = info

    security.declarePrivate( 'parseXML' )
    def parseXML( self, text, encoding=None ):

        """ Parse 'text'.
        """
        reader = getattr( text, 'read', None )

        if reader is not None:
            text = reader()

        parser = _ExportStepRegistryParser( encoding )
        parseString( text, parser )

        return parser._parsed

    security.declarePrivate( 'clear' )
    def clear( self ):

        self._registered = {}

    #
    #   Helper methods
    #
    security.declarePrivate( '_exportTemplate' )
    _exportTemplate = PageTemplateResource( 'xml/esrExport.xml', globals() )
示例#5
0
文件: tool.py 项目: bendavis78/zope
class SetupTool(Folder):
    """ Profile-based site configuration manager.
    """

    implements(ISetupTool)

    meta_type = 'Generic Setup Tool'

    _import_context_id = ''

    security = ClassSecurityInfo()

    def __init__(self, id):
        self.id = str(id)
        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 = PageTemplateResource('www/sutProperties.zpt', globals())

    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 = PageTemplateResource('www/sutImportSteps.zpt',
                                              globals())

    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 = PageTemplateResource('www/sutExportSteps.zpt',
                                              globals())

    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 = PageTemplateResource('www/sutSnapshots.zpt', globals())

    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 = PageTemplateResource('www/sutCompare.zpt', globals())

    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()
        }
示例#6
0
文件: tool.py 项目: bendavis78/zope
                raise ValueError('Invalid export step: %s' % step_id)

            messages[step_id] = handler(context)

        return {
            'steps': steps,
            'messages': messages,
            'tarball': context.getArchive(),
            'filename': context.getArchiveFilename()
        }


InitializeClass(SetupTool)

_PLAINTEXT_DIFF_HEADER = """\
Comparing configurations: '%s' and '%s'

%s"""

_TOOL_ID = 'setup_tool'

addSetupToolForm = PageTemplateResource('www/toolAdd.zpt', globals())


def addSetupTool(dispatcher, RESPONSE):
    """
    """
    dispatcher._setObject(_TOOL_ID, SetupTool(_TOOL_ID))

    RESPONSE.redirect('%s/manage_main' % dispatcher.absolute_url())
示例#7
0
class TypesTool(UniqueObject, IFAwareObjectManager, Folder,
                ActionProviderBase):
    """
        Provides a configurable registry of portal content types.
    """

    implements(ITypesTool)
    __implements__ = (z2ITypesTool, ActionProviderBase.__implements__)

    id = 'portal_types'
    meta_type = 'CMF Types Tool'
    _product_interfaces = (ITypeInformation, )

    security = ClassSecurityInfo()

    manage_options = (Folder.manage_options[:1] + ({
        'label': 'Aliases',
        'action': 'manage_aliases'
    }, ) + ActionProviderBase.manage_options + ({
        'label': 'Overview',
        'action': 'manage_overview'
    }, ) + Folder.manage_options[1:])

    #
    #   ZMI methods
    #
    security.declareProtected(ManagePortal, 'manage_overview')
    manage_overview = DTMLResource('dtml/explainTypesTool', globals())

    security.declareProtected(ManagePortal, 'manage_aliases')
    manage_aliases = PageTemplateResource('typesAliases.zpt', globals())

    #
    #   ObjectManager methods
    #
    def all_meta_types(self):
        # this is a workaround and should be removed again if allowedTypes
        # have an interface we can use in _product_interfaces
        all = TypesTool.inheritedAttribute('all_meta_types')(self)
        others = [
            mt for mt in Products.meta_types if mt['name'] in allowedTypes
        ]
        return tuple(all) + tuple(others)

    #
    #   other methods
    #
    security.declareProtected(ManagePortal, 'manage_addTypeInformation')

    def manage_addTypeInformation(self,
                                  add_meta_type,
                                  id=None,
                                  typeinfo_name=None,
                                  RESPONSE=None):
        """Create a TypeInformation in self.
        """
        # BBB: typeinfo_name is ignored
        if not id:
            raise BadRequest('An id is required.')
        for mt in Products.meta_types:
            if mt['name'] == add_meta_type:
                klass = mt['instance']
                break
        else:
            raise ValueError, ('Meta type %s is not a type class.' %
                               add_meta_type)
        id = str(id)
        ob = klass(id)
        self._setObject(id, ob)
        if RESPONSE is not None:
            RESPONSE.redirect('%s/manage_main' % self.absolute_url())

    security.declareProtected(ManagePortal, 'manage_setTIMethodAliases')

    def manage_setTIMethodAliases(self, REQUEST):
        """ Config method aliases.
        """
        form = REQUEST.form
        aliases = {}
        for k, v in form['aliases'].items():
            v = v.strip()
            if v:
                aliases[k] = v

        for ti in self.listTypeInfo():
            _dict = {}
            for k, v in form[ti.getId()].items():
                if aliases.has_key(k):
                    _dict[aliases[k]] = v
            ti.setMethodAliases(_dict)
        REQUEST.RESPONSE.redirect('%s/manage_aliases' % self.absolute_url())

    security.declareProtected(AccessContentsInformation, 'getTypeInfo')

    def getTypeInfo(self, contentType):
        """
            Return an instance which implements the
            TypeInformation interface, corresponding to
            the specified 'contentType'.  If contentType is actually
            an object, rather than a string, attempt to look up
            the appropriate type info using its portal_type.
        """
        if not isinstance(contentType, basestring):
            if hasattr(aq_base(contentType), 'getPortalTypeName'):
                contentType = contentType.getPortalTypeName()
                if contentType is None:
                    return None
            else:
                return None
        ob = getattr(self, contentType, None)
        if getattr(aq_base(ob), '_isTypeInformation', 0):
            return ob
        else:
            return None

    security.declareProtected(AccessContentsInformation, 'listTypeInfo')

    def listTypeInfo(self, container=None):
        """
            Return a sequence of instances which implement the
            TypeInformation interface, one for each content
            type registered in the portal.
        """
        rval = []
        for t in self.objectValues():
            # Filter out things that aren't TypeInformation and
            # types for which the user does not have adequate permission.
            if not getattr(aq_base(t), '_isTypeInformation', 0):
                continue
            if not t.getId():
                # XXX What's this used for ?
                # Not ready.
                continue
            # check we're allowed to access the type object
            if container is not None:
                if not t.isConstructionAllowed(container):
                    continue
            rval.append(t)
        return rval

    security.declareProtected(AccessContentsInformation, 'listContentTypes')

    def listContentTypes(self, container=None, by_metatype=0):
        """ List type info IDs.

        Passing 'by_metatype' is deprecated (type information may not
        correspond 1:1 to an underlying meta_type). This argument will be
        removed when CMFCore/dtml/catalogFind.dtml doesn't need it anymore.
        """
        typenames = {}
        for t in self.listTypeInfo(container):

            if by_metatype:
                warn(
                    'TypeInformation.listContentTypes(by_metatype=1) is '
                    'deprecated.', DeprecationWarning)
                name = t.Metatype()
            else:
                name = t.getId()

            if name:
                typenames[name] = None

        result = typenames.keys()
        result.sort()
        return result

    security.declarePublic('constructContent')

    def constructContent(self,
                         type_name,
                         container,
                         id,
                         RESPONSE=None,
                         *args,
                         **kw):
        """
            Build an instance of the appropriate content class in
            'container', using 'id'.
        """
        info = self.getTypeInfo(type_name)
        if info is None:
            raise ValueError('No such content type: %s' % type_name)

        ob = info.constructInstance(container, id, *args, **kw)

        if RESPONSE is not None:
            immediate_url = '%s/%s' % (ob.absolute_url(), info.immediate_view)
            RESPONSE.redirect(immediate_url)

        return ob.getId()

    security.declarePrivate('listActions')

    def listActions(self, info=None, object=None):
        """ List all the actions defined by a provider.
        """
        actions = list(self._actions)

        if object is None and info is not None:
            object = info.object
        if object is not None:
            type_info = self.getTypeInfo(object)
            if type_info is not None:
                actions.extend(type_info.listActions())

        return actions

    security.declareProtected(ManagePortal, 'listMethodAliasKeys')

    def listMethodAliasKeys(self):
        """ List all defined method alias names.
        """
        _dict = {}
        for ti in self.listTypeInfo():
            aliases = ti.getMethodAliases()
            for k, v in aliases.items():
                _dict[k] = 1
        rval = _dict.keys()
        rval.sort()
        return rval
示例#8
0
class TypeInformation(SimpleItemWithProperties, ActionProviderBase):
    """
    Base class for information about a content type.
    """

    _isTypeInformation = 1

    manage_options = (SimpleItemWithProperties.manage_options[:1] +
                      ({
                          'label': 'Aliases',
                          'action': 'manage_aliases'
                      }, ) + ActionProviderBase.manage_options +
                      SimpleItemWithProperties.manage_options[1:])

    security = ClassSecurityInfo()

    security.declareProtected(ManagePortal, 'manage_editProperties')
    security.declareProtected(ManagePortal, 'manage_changeProperties')
    security.declareProtected(ManagePortal, 'manage_propertiesForm')

    _basic_properties = (
        {
            'id': 'title',
            'type': 'string',
            'mode': 'w',
            'label': 'Title'
        },
        {
            'id': 'description',
            'type': 'text',
            'mode': 'w',
            'label': 'Description'
        },
        {
            'id': 'i18n_domain',
            'type': 'string',
            'mode': 'w',
            'label': 'I18n Domain'
        },
        {
            'id': 'content_icon',
            'type': 'string',
            'mode': 'w',
            'label': 'Icon'
        },
        {
            'id': 'content_meta_type',
            'type': 'string',
            'mode': 'w',
            'label': 'Product meta type'
        },
    )

    _advanced_properties = (
        {
            'id': 'immediate_view',
            'type': 'string',
            'mode': 'w',
            'label': 'Initial view name'
        },
        {
            'id': 'global_allow',
            'type': 'boolean',
            'mode': 'w',
            'label': 'Implicitly addable?'
        },
        {
            'id': 'filter_content_types',
            'type': 'boolean',
            'mode': 'w',
            'label': 'Filter content types?'
        },
        {
            'id': 'allowed_content_types',
            'type': 'multiple selection',
            'mode': 'w',
            'label': 'Allowed content types',
            'select_variable': 'listContentTypes'
        },
        {
            'id': 'allow_discussion',
            'type': 'boolean',
            'mode': 'w',
            'label': 'Allow Discussion?'
        },
    )

    title = ''
    description = ''
    i18n_domain = ''
    content_meta_type = ''
    content_icon = ''
    immediate_view = ''
    filter_content_types = True
    allowed_content_types = ()
    allow_discussion = False
    global_allow = True

    def __init__(self, id, **kw):

        self.id = id

        if not kw:
            return

        kw = kw.copy()  # Get a modifiable dict.

        if (not kw.has_key('content_meta_type') and kw.has_key('meta_type')):
            kw['content_meta_type'] = kw['meta_type']

        if (not kw.has_key('content_icon') and kw.has_key('icon')):
            kw['content_icon'] = kw['icon']

        self.manage_changeProperties(**kw)

        actions = kw.get('actions', ())
        # make sure we have a copy
        _actions = []
        for action in actions:
            _actions.append(action.copy())
        actions = tuple(_actions)
        # We don't know if actions need conversion, so we always add oldstyle
        # _actions and convert them.
        self._actions = actions
        self._convertActions()

        aliases = kw.get('aliases', _marker)
        if aliases is _marker:
            self._guessMethodAliases()
        else:
            self.setMethodAliases(aliases)

    #
    #   ZMI methods
    #
    security.declareProtected(ManagePortal, 'manage_aliases')
    manage_aliases = PageTemplateResource('www/typeinfoAliases.zpt', globals())

    security.declareProtected(ManagePortal, 'manage_setMethodAliases')

    def manage_setMethodAliases(self, REQUEST):
        """ Config method aliases.
        """
        form = REQUEST.form
        aliases = {}
        for k, v in form['aliases'].items():
            v = v.strip()
            if v:
                aliases[k] = v

        _dict = {}
        for k, v in form['methods'].items():
            if aliases.has_key(k):
                _dict[aliases[k]] = v
        self.setMethodAliases(_dict)
        REQUEST.RESPONSE.redirect('%s/manage_aliases' % self.absolute_url())

    #
    #   Accessors
    #
    security.declareProtected(View, 'Title')

    def Title(self):
        """
            Return the "human readable" type name (note that it
            may not map exactly to the 'portal_type', e.g., for
            l10n/i18n or where a single content class is being
            used twice, under different names.
        """
        if self.title and self.i18n_domain:
            return MessageID(self.title, self.i18n_domain)
        else:
            return self.title or self.getId()

    security.declareProtected(View, 'Description')

    def Description(self):
        """
            Textual description of the class of objects (intended
            for display in a "constructor list").
        """
        if self.description and self.i18n_domain:
            return MessageID(self.description, self.i18n_domain)
        else:
            return self.description

    security.declareProtected(View, 'Metatype')

    def Metatype(self):
        """
            Returns the Zope 'meta_type' for this content object.
            May be used for building the list of portal content
            meta types.
        """
        return self.content_meta_type

    security.declareProtected(View, 'getIcon')

    def getIcon(self):
        """
            Returns the icon for this content object.
        """
        return self.content_icon

    security.declarePublic('allowType')

    def allowType(self, contentType):
        """
            Can objects of 'contentType' be added to containers whose
            type object we are?
        """
        if not self.filter_content_types:
            ti = self.getTypeInfo(contentType)
            if ti is None or ti.globalAllow():
                return 1

        #If a type is enabled to filter and no content_types are allowed
        if not self.allowed_content_types:
            return 0

        if contentType in self.allowed_content_types:
            return 1

        # Backward compatibility for code that expected Type() to work.
        for ti in self.listTypeInfo():
            if ti.Title() == contentType:
                return ti.getId() in self.allowed_content_types

        return 0

    security.declarePublic('getId')

    def getId(self):
        return self.id

    security.declarePublic('allowDiscussion')

    def allowDiscussion(self):
        """
            Can this type of object support discussion?
        """
        return self.allow_discussion

    security.declarePublic('globalAllow')

    def globalAllow(self):
        """
        Should this type be implicitly addable anywhere?
        """
        return self.global_allow

    security.declarePublic('listActions')

    def listActions(self, info=None, object=None):
        """ Return a sequence of the action info objects for this type.
        """
        if self._actions and isinstance(self._actions[0], dict):
            self._convertActions()

        return self._actions or ()

    security.declarePrivate('_convertActions')

    def _convertActions(self):
        """ Upgrade dictionary-based actions.
        """
        aa, self._actions = self._actions, ()

        for action in aa:

            # Some backward compatibility stuff.
            if not 'id' in action:
                action['id'] = cookString(action['name'])

            if not 'title' in action:
                action['title'] = action.get('name', action['id'].capitalize())

            # historically, action['action'] is simple string
            actiontext = action.get('action').strip() or 'string:${object_url}'
            if actiontext[:7] not in ('python:', 'string:'):
                actiontext = 'string:${object_url}/%s' % actiontext

            self.addAction(id=action['id'],
                           name=action['title'],
                           action=actiontext,
                           condition=action.get('condition'),
                           permission=action.get('permissions', ()),
                           category=action.get('category', 'object'),
                           visible=action.get('visible', True))

    security.declarePublic('constructInstance')

    def constructInstance(self, container, id, *args, **kw):
        """Build an instance of the type.

        Builds the instance in 'container', using 'id' as its id.
        Returns the object.
        """
        if not self.isConstructionAllowed(container):
            raise AccessControl_Unauthorized('Cannot create %s' % self.getId())

        ob = self._constructInstance(container, id, *args, **kw)

        return self._finishConstruction(ob)

    security.declarePrivate('_finishConstruction')

    def _finishConstruction(self, ob):
        """
            Finish the construction of a content object.
            Set its portal_type, insert it into the workflows.
        """
        if hasattr(ob, '_setPortalTypeName'):
            ob._setPortalTypeName(self.getId())

        if hasattr(aq_base(ob), 'notifyWorkflowCreated'):
            ob.notifyWorkflowCreated()

        ob.reindexObject()
        return ob

    security.declareProtected(ManagePortal, 'getMethodAliases')

    def getMethodAliases(self):
        """ Get method aliases dict.
        """
        if not hasattr(self, '_aliases'):
            self._guessMethodAliases()
        aliases = self._aliases
        # for aliases created with CMF 1.5.0beta
        for key, method_id in aliases.items():
            if isinstance(method_id, tuple):
                aliases[key] = method_id[0]
                self._p_changed = True
        return aliases.copy()

    security.declareProtected(ManagePortal, 'setMethodAliases')

    def setMethodAliases(self, aliases):
        """ Set method aliases dict.
        """
        _dict = {}
        for k, v in aliases.items():
            v = v.strip()
            if v:
                _dict[k.strip()] = v
        if not getattr(self, '_aliases', None) == _dict:
            self._aliases = _dict
            return True
        else:
            return False

    security.declarePublic('queryMethodID')

    def queryMethodID(self, alias, default=None, context=None):
        """ Query method ID by alias.
        """
        if not hasattr(self, '_aliases'):
            self._guessMethodAliases()
        aliases = self._aliases
        method_id = aliases.get(alias, default)
        # for aliases created with CMF 1.5.0beta
        if isinstance(method_id, tuple):
            method_id = method_id[0]
        return method_id

    security.declarePrivate('_guessMethodAliases')

    def _guessMethodAliases(self):
        """ Guess and set Method Aliases. Used for upgrading old TIs.
        """
        context = getActionContext(self)
        actions = self.listActions()
        ordered = []
        _dict = {}
        viewmethod = ''

        # order actions and search 'mkdir' action
        for action in actions:
            if action.getId() == 'view':
                ordered.insert(0, action)
            elif action.getId() == 'mkdir':
                try:
                    mkdirmethod = action.action(context).strip()
                except AttributeError:
                    continue
                if mkdirmethod.startswith('/'):
                    mkdirmethod = mkdirmethod[1:]
                _dict['mkdir'] = mkdirmethod
            else:
                ordered.append(action)

        # search 'view' action
        for action in ordered:
            perms = action.getPermissions()
            if not perms or View in perms:
                try:
                    viewmethod = action.action(context).strip()
                except (AttributeError, TypeError):
                    break
                if viewmethod.startswith('/'):
                    viewmethod = viewmethod[1:]
                if not viewmethod:
                    viewmethod = '(Default)'
                break
        else:
            viewmethod = '(Default)'
        if viewmethod:
            _dict['view'] = viewmethod

        # search default action
        for action in ordered:
            try:
                defmethod = action.action(context).strip()
            except (AttributeError, TypeError):
                break
            if defmethod.startswith('/'):
                defmethod = defmethod[1:]
            if not defmethod:
                break
        else:
            if viewmethod:
                _dict['(Default)'] = viewmethod

        # correct guessed values if we know better
        if self.content_meta_type in ('Portal File', 'Portal Folder',
                                      'Portal Image'):
            _dict['(Default)'] = 'index_html'
            if viewmethod == '(Default)':
                _dict['view'] = 'index_html'
        if self.content_meta_type in ('Document', 'News Item'):
            _dict['gethtml'] = 'source_html'

        self.setMethodAliases(_dict)
        return 1
示例#9
0
        """
        constructor = self.restrictedTraverse(self.constructor_path)

        # make sure ownership is explicit before switching the context
        if not hasattr(aq_base(constructor), '_owner'):
            constructor._owner = aq_get(constructor, '_owner')
        #   Rewrap to get into container's context.
        constructor = aq_base(constructor).__of__(container)

        id = str(id)
        return constructor(container, id, *args, **kw)


InitializeClass(ScriptableTypeInformation)

_addTypeInfo_template = PageTemplateResource('addTypeInfo.zpt', globals())


def manage_addFactoryTIForm(dispatcher, REQUEST):
    """ Get the add form for factory-based type infos.
    """
    template = _addTypeInfo_template.__of__(dispatcher)
    meta_type = FactoryTypeInformation.meta_type
    return template(add_meta_type=meta_type,
                    profiles=_getProfileInfo(dispatcher, meta_type))


def manage_addScriptableTIForm(dispatcher, REQUEST):
    """ Get the add form for scriptable type infos.
    """
    template = _addTypeInfo_template.__of__(dispatcher)