class UserTokens(SimpleItem):
    def __init__(self):
        self.user_tokens = PersistentMapping()

    def generateToken(self, userId):
        import random
        token = str(random.random()).split('.')[1]
        self.user_tokens[token] = userId
        return token

    def getUserId(self, userToken):
        if self.user_tokens.has_key(token):
            return self.user_tokens[token]
        return None

    def getUserToken(self, userId):
        for token, user_id in self.user_tokens.iteritems():
            if user_id == userId:
                return token
        return None

    def getUserTokensList(self):
        tokens = []
        for token, user_id in self.user_tokens.iteritems():
            tokens.append({'userId' : user_id, 'userToken' : token})
        return tokens

    def removeToken(self, userToken):
        """
        Remove user token.
        """
        if self.user_tokens.has_key(userToken):
            del self.user_tokens[userToken]
        else:
            LOG('', WARNING, 'Tried to remove unexisting token : %s' % userToken)
class Federations(SimpleItem):
    def __init__(self):
        self.federations = PersistentMapping()

    def getUserId(self, nameIdentifier):
        return self.federations.get(nameIdentifier, {}).get('user_id')

    def getIdentityDump(self, nameIdentifier):
        return self.federations.get(nameIdentifier, {}).get('identity_dump')

    def setFederation(self, nameIdentifier, userId, identityDump):
        self.federations[nameIdentifier] = {'user_id': userId, 'identity_dump': identityDump}

    def removeFederation(self, nameIdentifier):
        if self.federations.has_key(nameIdentifier):
            del self.federations[nameIdentifier]
Ejemplo n.º 3
0
class MetadataTool( UniqueObject, SimpleItem, ActionProviderBase ):

    __implements__ = (IMetadataTool, ActionProviderBase.__implements__)

    id = 'portal_metadata'
    meta_type = 'Default Metadata Tool'
    _actions = ()

    #
    #   Default values.
    #
    publisher           = ''
    element_specs       = None
    #initial_values_hook = None
    #validation_hook     = None

    security = ClassSecurityInfo()

    def __init__( self
                , publisher=None
               #, initial_values_hook=None
               #, validation_hook=None
                , element_specs=DEFAULT_ELEMENT_SPECS
                ):

        self.editProperties( publisher
                          #, initial_values_hook
                          #, validation_hook
                           )

        self.element_specs = PersistentMapping()

        for name, is_multi_valued in element_specs:
            self.element_specs[ name ] = ElementSpec( is_multi_valued )

    #
    #   ZMI methods
    #
    manage_options = ( ActionProviderBase.manage_options +
                     ( { 'label'      : 'Overview'
                         , 'action'     : 'manage_overview'
                         }
                       , { 'label'      : 'Properties'
                         , 'action'     : 'propertiesForm'
                         }
                       , { 'label'      : 'Elements'
                         , 'action'     : 'elementPoliciesForm'
                         }
            # TODO     , { 'label'      : 'Types'
            #            , 'action'     : 'typesForm'
            #            }
                       )
                     + SimpleItem.manage_options
                     )

    security.declareProtected(ManagePortal, 'manage_overview')
    manage_overview = DTMLFile( 'explainMetadataTool', _dtmldir )

    security.declareProtected(ManagePortal, 'propertiesForm')
    propertiesForm = DTMLFile( 'metadataProperties', _dtmldir )

    security.declareProtected(ManagePortal, 'editProperties')
    def editProperties( self
                      , publisher=None
               # TODO , initial_values_hook=None
               # TODO , validation_hook=None
                      , REQUEST=None
                      ):
        """
            Form handler for "tool-wide" properties (including list of
            metadata elements).
        """
        if publisher is not None:
            self.publisher = publisher

        # TODO self.initial_values_hook = initial_values_hook
        # TODO self.validation_hook = validation_hook

        if REQUEST is not None:
            REQUEST[ 'RESPONSE' ].redirect( self.absolute_url()
                                        + '/propertiesForm'
                                        + '?manage_tabs_message=Tool+updated.'
                                        )

    security.declareProtected(ManagePortal, 'elementPoliciesForm')
    elementPoliciesForm = DTMLFile( 'metadataElementPolicies', _dtmldir )

    security.declareProtected(ManagePortal, 'addElementPolicy')
    def addElementPolicy( self
                        , element
                        , content_type
                        , is_required
                        , supply_default
                        , default_value
                        , enforce_vocabulary
                        , allowed_vocabulary
                        , REQUEST=None
                        ):
        """
            Add a type-specific policy for one of our elements.
        """
        if content_type == '<default>':
            content_type = None

        spec = self.getElementSpec( element )
        spec.addPolicy( content_type )
        policy = spec.getPolicy( content_type )
        policy.edit( is_required
                   , supply_default
                   , default_value
                   , enforce_vocabulary
                   , allowed_vocabulary
                   )
        if REQUEST is not None:
            REQUEST[ 'RESPONSE' ].redirect( self.absolute_url()
               + '/elementPoliciesForm'
               + '?element=' + element
               + '&manage_tabs_message=Policy+added.'
               )

    security.declareProtected(ManagePortal, 'removeElementPolicy')
    def removeElementPolicy( self
                           , element
                           , content_type
                           , REQUEST=None
                           ):
        """
            Remvoe a type-specific policy for one of our elements.
        """
        if content_type == '<default>':
            content_type = None

        spec = self.getElementSpec( element )
        spec.removePolicy( content_type )
        if REQUEST is not None:
            REQUEST[ 'RESPONSE' ].redirect( self.absolute_url()
               + '/elementPoliciesForm'
               + '?element=' + element
               + '&manage_tabs_message=Policy+removed.'
               )

    security.declareProtected(ManagePortal, 'updateElementPolicy')
    def updateElementPolicy( self
                           , element
                           , content_type
                           , is_required
                           , supply_default
                           , default_value
                           , enforce_vocabulary
                           , allowed_vocabulary
                           , REQUEST=None
                           ):
        """
            Update a policy for one of our elements ('content_type'
            will be '<default>' when we edit the default).
        """
        if content_type == '<default>':
            content_type = None
        spec = self.getElementSpec( element )
        policy = spec.getPolicy( content_type )
        policy.edit( is_required
                   , supply_default
                   , default_value
                   , enforce_vocabulary
                   , allowed_vocabulary
                   )
        if REQUEST is not None:
            REQUEST[ 'RESPONSE' ].redirect( self.absolute_url()
               + '/elementPoliciesForm'
               + '?element=' + element
               + '&manage_tabs_message=Policy+updated.'
               )


    #
    #   Element spec manipulation.
    #
    security.declareProtected(ManagePortal, 'listElementSpecs')
    def listElementSpecs( self ):
        """
            Return a list of ElementSpecs representing
            the elements managed by the tool.
        """
        res = []
        for k, v in self.element_specs.items():
            res.append((k, v.__of__(self)))
        return res

    security.declareProtected(ManagePortal, 'getElementSpec')
    def getElementSpec( self, element ):
        """
            Return an ElementSpec representing the tool's knowledge
            of 'element'.
        """
        return self.element_specs[ element ].__of__( self )

    security.declareProtected(ManagePortal, 'addElementSpec')
    def addElementSpec( self, element, is_multi_valued, REQUEST=None ):
        """
            Add 'element' to our list of managed elements.
        """
        # Don't replace.
        if self.element_specs.has_key( element ):
           return

        self.element_specs[ element ] = ElementSpec( is_multi_valued )

        if REQUEST is not None:
            REQUEST[ 'RESPONSE' ].redirect( self.absolute_url()
               + '/propertiesForm'
               + '?manage_tabs_message=Element+' + element + '+added.'
               )

    security.declareProtected(ManagePortal, 'removeElementSpec')
    def removeElementSpec( self, element, REQUEST=None ):
        """
            Remove 'element' from our list of managed elements.
        """
        del self.element_specs[ element ]

        if REQUEST is not None:
            REQUEST[ 'RESPONSE' ].redirect( self.absolute_url()
               + '/propertiesForm'
               + '?manage_tabs_message=Element+' + element + '+removed.'
               )

    security.declareProtected(ManagePortal, 'listPolicies')
    def listPolicies( self, typ=None ):
        """
            Show all policies for a given content type, or the default
            if None.
        """
        result = []
        for element, spec in self.listElementSpecs():
            result.append( ( element, spec.getPolicy( typ ) ) )
        return result

    #
    #   'portal_metadata' interface
    #
    security.declarePrivate( 'getFullName' )
    def getFullName( self, userid ):
        """
            Convert an internal userid to a "formal" name, if
            possible, perhaps using the 'portal_membership' tool.

            Used to map userid's for Creator, Contributor DCMI
            queries.
        """
        return userid   # TODO: do lookup here

    security.declarePublic( 'getPublisher' )
    def getPublisher( self ):
        """
            Return the "formal" name of the publisher of the
            portal.
        """
        return self.publisher

    security.declarePublic( 'listAllowedVocabulary' )
    def listAllowedVocabulary( self, element, content=None, content_type=None ):
        """
            List allowed keywords for a given portal_type, or all
            possible keywords if none supplied.
        """
        spec = self.getElementSpec( element )
        if content_type is None and content:
            content_type = content.getPortalTypeName()
        return spec.getPolicy( content_type ).allowedVocabulary()

    security.declarePublic( 'listAllowedSubjects' )
    def listAllowedSubjects( self, content=None, content_type=None ):
        """
            List allowed keywords for a given portal_type, or all
            possible keywords if none supplied.
        """
        return self.listAllowedVocabulary( 'Subject', content, content_type )

    security.declarePublic( 'listAllowedFormats' )
    def listAllowedFormats( self, content=None, content_type=None ):
        """
            List the allowed 'Content-type' values for a particular
            portal_type, or all possible formats if none supplied.
        """
        return self.listAllowedVocabulary( 'Format', content, content_type )

    security.declarePublic( 'listAllowedLanguages' )
    def listAllowedLanguages( self, content=None, content_type=None ):
        """
            List the allowed language values.
        """
        return self.listAllowedVocabulary( 'Language', content, content_type )

    security.declarePublic( 'listAllowedRights' )
    def listAllowedRights( self, content=None, content_type=None ):
        """
            List the allowed values for a "Rights"
            selection list;  this gets especially important where
            syndication is involved.
        """
        return self.listAllowedVocabulary( 'Rights', content, content_type )

    security.declareProtected(ModifyPortalContent, 'setInitialMetadata')
    def setInitialMetadata( self, content ):
        """
            Set initial values for content metatdata, supplying
            any site-specific defaults.
        """
        for element, policy in self.listPolicies(content.getPortalTypeName()):

            if not getattr( content, element )():

                if policy.supplyDefault():
                    setter = getattr( content, 'set%s' % element )
                    setter( policy.defaultValue() )
                elif policy.isRequired():
                    raise MetadataError, \
                          'Metadata element %s is required.' % element

        # TODO:  Call initial_values_hook, if present


    security.declareProtected(View, 'validateMetadata')
    def validateMetadata( self, content ):
        """
            Enforce portal-wide policies about DCI, e.g.,
            requiring non-empty title/description, etc.  Called
            by the CMF immediately before saving changes to the
            metadata of an object.
        """
        for element, policy in self.listPolicies(content.getPortalTypeName()):

            value = getattr( content, element )()
            if not value and policy.isRequired():
                raise MetadataError, \
                        'Metadata element %s is required.' % element

            if value and policy.enforceVocabulary():
                values = policy.isMultiValued() and value or [ value ]
                for value in values:
                    if not value in policy.allowedVocabulary():
                        raise MetadataError, \
                        'Value %s is not in allowed vocabulary for ' \
                        'metadata element %s.' % ( value, element )
Ejemplo n.º 4
0
class ElementSpec( SimpleItem ):
    """
        Represent all the tool knows about a single metadata element.
    """
    security = ClassSecurityInfo()

    #
    #   Default values.
    #
    is_multi_valued = 0

    def __init__( self, is_multi_valued ):
        self.is_multi_valued  = is_multi_valued
        self.policies         = PersistentMapping()
        self.policies[ None ] = self._makePolicy()  # set default policy

    security.declarePrivate( '_makePolicy' )
    def _makePolicy( self ):
        return MetadataElementPolicy( self.is_multi_valued )

    security.declareProtected(View , 'isMultiValued')
    def isMultiValued( self ):
        """
            Is this element multi-valued?
        """
        return self.is_multi_valued

    security.declareProtected(View , 'getPolicy')
    def getPolicy( self, typ=None ):
        """
            Find the policy this element for objects whose type
            object name is 'typ';  return a default, if none found.
        """
        try:
            return self.policies[ typ ].__of__(self)
        except KeyError:
            return self.policies[ None ]

    security.declareProtected(View , 'listPolicies')
    def listPolicies( self ):
        """
            Return a list of all policies for this element.
        """
        res = []
        for k, v in self.policies.items():
            res.append((k, v.__of__(self)))
        return res

    security.declareProtected(ManagePortal , 'addPolicy')
    def addPolicy( self, typ ):
        """
            Add a policy to this element for objects whose type
            object name is 'typ'.
        """
        if typ is None:
            raise MetadataError, "Can't replace default policy."

        if self.policies.has_key( typ ):
            raise MetadataError, "Existing policy for content type:" + typ

        self.policies[ typ ] = self._makePolicy()

    security.declareProtected(ManagePortal, 'removePolicy')
    def removePolicy( self, typ ):
        """
            Remove the policy from this element for objects whose type
            object name is 'typ' (*not* the default, however).
        """
        if typ is None:
            raise MetadataError, "Can't remove default policy."
        del self.policies[ typ ]
Ejemplo n.º 5
0
class DiscussionItemContainer( Persistent, Implicit, Traversable ):
    """
        Store DiscussionItem objects. Discussable content that
        has DiscussionItems associated with it will have an
        instance of DiscussionItemContainer injected into it to
        hold the discussion threads.
    """

    __implements__ = Discussable

    # for the security machinery to allow traversal
    #__roles__ = None

    security = ClassSecurityInfo()

    def __init__(self):
        self.id = 'talkback'
        self._container = PersistentMapping()

    security.declareProtected(View, 'getId')
    def getId( self ):
        return self.id

    security.declareProtected(View, 'getReply')
    def getReply( self, reply_id ):
        """
            Return a discussion item, given its ID;  raise KeyError
            if not found.
        """
        return self._container.get( reply_id ).__of__(self)

    # Is this right?
    security.declareProtected(View, '__bobo_traverse__')
    def __bobo_traverse__(self, REQUEST, name):
        """
        This will make this container traversable
        """
        target = getattr(self, name, None)
        if target is not None:
            return target

        else:
            try:
                return self.getReply(name)
            except:
                parent = aq_parent( aq_inner( self ) )
                if parent.getId() == name:
                    return parent
                else:
                    REQUEST.RESPONSE.notFoundError("%s\n%s" % (name, ''))

    security.declarePrivate('manage_afterAdd')
    def manage_afterAdd(self, item, container):
        """
            We have juste been added or moved.
            Add the contained items to the catalog.
        """
        if aq_base(container) is not aq_base(self):
            for obj in self.objectValues():
                obj.__of__(self).manage_afterAdd(item, container)

    security.declarePrivate('manage_afterClone')
    def manage_afterClone(self, item):
        """
            We have just been cloned.
            Notify the workflow about the contained items.
        """
        for obj in self.objectValues():
            obj.__of__(self).manage_afterClone(item)

    security.declarePrivate( 'manage_beforeDelete' )
    def manage_beforeDelete(self, item, container):
        """
            Remove the contained items from the catalog.
        """
        if aq_base(container) is not aq_base(self):
            for obj in self.objectValues():
                obj.__of__( self ).manage_beforeDelete( item, container )

    #
    #   OFS.ObjectManager query interface.
    #
    security.declareProtected(AccessContentsInformation, 'objectIds')
    def objectIds( self, spec=None ):
        """
            Return a list of the ids of our DiscussionItems.
        """
        if spec and spec is not DiscussionItem.meta_type:
            return []
        return self._container.keys()


    security.declareProtected(AccessContentsInformation, 'objectItems')
    def objectItems(self, spec=None):
        """
            Return a list of (id, subobject) tuples for our DiscussionItems.
        """
        r=[]
        a=r.append
        g=self._container.get
        for id in self.objectIds(spec):
            a( (id, g( id ) ) )
        return r


    security.declareProtected(AccessContentsInformation, 'objectValues')
    def objectValues(self):
        """
            Return a list of our DiscussionItems.
        """
        return self._container.values()

    #
    #   Discussable interface
    #
    security.declareProtected(ReplyToItem, 'createReply')
    def createReply( self, title, text, Creator=None, text_format='structured-text' ):
        """
            Create a reply in the proper place
        """
        container = self._container

        id = int(DateTime().timeTime())
        while self._container.get( str(id), None ) is not None:
            id = id + 1
        id = str( id )

        item = DiscussionItem( id, title=title, description=title )
        item._edit( text_format=text_format, text=text )
        item.__of__(self).addCreator(Creator)
        item.__of__(self).indexObject()

        item.setReplyTo( self._getDiscussable() )
        item.__of__(self).notifyWorkflowCreated()

        self._container[ id ] = item

        return id

    security.declareProtected(ManagePortal, 'deleteReply')
    def deleteReply( self, reply_id ):
        """ Remove a reply from this container """
        if self._container.has_key( reply_id ):
            reply = self._container.get( reply_id ).__of__( self )
            my_replies = reply.talkback.getReplies()
            for my_reply in my_replies:
                my_reply_id = my_reply.getId()
                if hasattr( my_reply, 'unindexObject' ):
                    my_reply.unindexObject()

                del self._container[my_reply_id]

            if hasattr( reply, 'unindexObject' ):
                reply.unindexObject()

            del self._container[reply_id]


    security.declareProtected(View, 'hasReplies')
    def hasReplies( self, content_obj ):
        """
            Test to see if there are any dicussion items
        """
        outer = self._getDiscussable( outer=1 )
        if content_obj == outer:
            return not not len( self._container )
        else:
            return not not len( content_obj.talkback._getReplyResults() )

    security.declareProtected(View, 'replyCount')
    def replyCount( self, content_obj ):
        """ How many replies do i have? """
        outer = self._getDiscussable( outer=1 )
        if content_obj == outer:
            return len( self._container )
        else:
            replies = content_obj.talkback.getReplies()
            return self._repcount( replies )

    security.declarePrivate('_repcount')
    def _repcount( self, replies ):
        """  counts the total number of replies by recursing thru the various levels
        """
        count = 0

        for reply in replies:
            count = count + 1

            #if there is at least one reply to this reply
            replies = reply.talkback.getReplies()
            if replies:
                count = count + self._repcount( replies )

        return count

    security.declareProtected(View, 'getReplies')
    def getReplies( self ):
        """
            Return a sequence of the DiscussionResponse objects which are
            associated with this Discussable
        """
        objects = []
        a = objects.append
        result_ids = self._getReplyResults()

        for id in result_ids:
            a( self._container.get( id ).__of__( self ) )

        return objects

    security.declareProtected(View, 'quotedContents')
    def quotedContents(self):
        """
            Return this object's contents in a form suitable for inclusion
            as a quote in a response.
        """

        return ""

    #
    #   Utility methods
    #
    security.declarePrivate( '_getReplyParent' )
    def _getReplyParent( self, in_reply_to ):
        """
            Return the object indicated by the 'in_reply_to', where
            'None' represents the "outer" content object.
        """
        outer = self._getDiscussable( outer=1 )
        if in_reply_to is None:
            return outer
        parent = self._container[ in_reply_to ].__of__( aq_inner( self ) )
        return parent.__of__( outer )

    security.declarePrivate( '_getDiscussable' )
    def _getDiscussable( self, outer=0 ):
        """
        """
        tb = outer and aq_inner( self ) or self
        return getattr( tb, 'aq_parent', None )

    security.declarePrivate( '_getReplyResults' )
    def _getReplyResults( self ):
        """
           Get a list of ids of DiscussionItems which are replies to
           our Discussable.
        """
        discussable = self._getDiscussable()
        outer = self._getDiscussable( outer=1 )

        if discussable == outer:
            in_reply_to = None
        else:
            in_reply_to = discussable.getId()

        result = []
        a = result.append
        for key, value in self._container.items():
            if value.in_reply_to == in_reply_to:
                a( ( key, value ) )

        result.sort( lambda a, b: cmp(a[1].creation_date, b[1].creation_date) )

        return [ x[0] for x in result ]
Ejemplo n.º 6
0
class MetadataSchema( SimpleItem ):
    """ Describe a metadata schema.
    """
    security = ClassSecurityInfo()

    meta_type = 'Metadata Schema'
    publisher = ''

    def __init__( self, id, element_specs=() ):
        self._setId( id )
        self.element_specs = PersistentMapping()
        for name, is_multi_valued in element_specs:
            self.element_specs[ name ] = ElementSpec( is_multi_valued )


    #
    #   ZMI methods
    #
    manage_options = ( ( { 'label'      : 'Elements'
                         , 'action'     : 'elementPoliciesForm'
                         }
                       ,
                       )
                     + SimpleItem.manage_options
                     )

    security.declareProtected(ManagePortal, 'elementPoliciesForm')
    elementPoliciesForm = DTMLFile( 'metadataElementPolicies', _dtmldir )

    security.declareProtected(ManagePortal, 'addElementPolicy')
    def addElementPolicy( self
                        , element
                        , content_type
                        , is_required
                        , supply_default
                        , default_value
                        , enforce_vocabulary
                        , allowed_vocabulary
                        , REQUEST=None
                        ):
        """ Add a type-specific policy for one of our elements.
        """
        if content_type == '<default>':
            content_type = None

        spec = self.getElementSpec( element )
        spec.addPolicy( content_type )
        policy = spec.getPolicy( content_type )
        policy.edit( is_required
                   , supply_default
                   , default_value
                   , enforce_vocabulary
                   , allowed_vocabulary
                   )
        if REQUEST is not None:
            REQUEST[ 'RESPONSE' ].redirect( self.absolute_url()
               + '/elementPoliciesForm'
               + '?element=' + element
               + '&manage_tabs_message=Policy+added.'
               )

    security.declareProtected(ManagePortal, 'removeElementPolicy')
    def removeElementPolicy( self
                           , element
                           , content_type
                           , REQUEST=None
                           ):
        """ Remvoe a type-specific policy for one of our elements.
        """
        if content_type == '<default>':
            content_type = None

        spec = self.getElementSpec( element )
        spec.removePolicy( content_type )
        if REQUEST is not None:
            REQUEST[ 'RESPONSE' ].redirect( self.absolute_url()
               + '/elementPoliciesForm'
               + '?element=' + element
               + '&manage_tabs_message=Policy+removed.'
               )

    security.declareProtected(ManagePortal, 'updateElementPolicy')
    def updateElementPolicy( self
                           , element
                           , content_type
                           , is_required
                           , supply_default
                           , default_value
                           , enforce_vocabulary
                           , allowed_vocabulary
                           , REQUEST=None
                           ):
        """ Update a policy for one of our elements 
        
        o 'content_type' will be '<default>' when we edit the default.
        """
        if content_type == '<default>':
            content_type = None
        spec = self.getElementSpec( element )
        policy = spec.getPolicy( content_type )
        policy.edit( is_required
                   , supply_default
                   , default_value
                   , enforce_vocabulary
                   , allowed_vocabulary
                   )
        if REQUEST is not None:
            REQUEST[ 'RESPONSE' ].redirect( self.absolute_url()
               + '/elementPoliciesForm'
               + '?element=' + element
               + '&manage_tabs_message=Policy+updated.'
               )


    #
    #   Element spec manipulation.
    #
    security.declareProtected(ManagePortal, 'listElementSpecs')
    def listElementSpecs( self ):
        """ Return a list of ElementSpecs representing the elements we manage.
        """
        res = []
        for k, v in self.element_specs.items():
            res.append((k, v.__of__(self)))
        return res

    security.declareProtected(ManagePortal, 'getElementSpec')
    def getElementSpec( self, element ):
        """ Return an ElementSpec for the given 'element'.
        """
        return self.element_specs[ element ].__of__( self )

    security.declareProtected(ManagePortal, 'addElementSpec')
    def addElementSpec( self, element, is_multi_valued, REQUEST=None ):
        """ Add 'element' to our list of managed elements.
        """
        # Don't replace.
        if self.element_specs.has_key( element ):
           return

        self.element_specs[ element ] = ElementSpec( is_multi_valued )

        if REQUEST is not None:
            REQUEST[ 'RESPONSE' ].redirect( self.absolute_url()
               + '/propertiesForm'
               + '?manage_tabs_message=Element+' + element + '+added.'
               )

    security.declareProtected(ManagePortal, 'removeElementSpec')
    def removeElementSpec( self, element, REQUEST=None ):
        """ Remove 'element' from our list of managed elements.
        """
        del self.element_specs[ element ]

        if REQUEST is not None:
            REQUEST[ 'RESPONSE' ].redirect( self.absolute_url()
               + '/propertiesForm'
               + '?manage_tabs_message=Element+' + element + '+removed.'
               )

    security.declareProtected(ManagePortal, 'listPolicies')
    def listPolicies( self, typ=None ):
        """ Show all policies for a given content type
        
        o If 'typ' is none, return the list of default policies.
        """
        result = []
        for element, spec in self.listElementSpecs():
            result.append( ( element, spec.getPolicy( typ ) ) )
        return result
Ejemplo n.º 7
0
class WorklistDefinition(SimpleItem):
    """Worklist definiton"""

    meta_type = 'Worklist'

    security = ClassSecurityInfo()
    security.declareObjectProtected(ManagePortal)

    description = ''
    var_matches = None  # Compared with catalog when set.
    actbox_name = ''
    actbox_url = ''
    actbox_category = 'global'
    guard = None

    manage_options = (
        {'label': 'Properties', 'action': 'manage_properties'},
        )

    def __init__(self, id):
        self.id = id

    def getGuard(self):
        if self.guard is not None:
            return self.guard
        else:
            return Guard().__of__(self)  # Create a temporary guard.

    def getGuardSummary(self):
        res = None
        if self.guard is not None:
            res = self.guard.getSummary()
        return res

    def getWorkflow(self):
        return aq_parent(aq_inner(aq_parent(aq_inner(self))))

    def getAvailableCatalogVars(self):
        res = []
        res.append(self.getWorkflow().state_var)
        for id, vdef in self.getWorkflow().variables.items():
            if vdef.for_catalog:
                res.append(id)
        res.sort()
        return res

    def getVarMatchKeys(self):
        if self.var_matches:
            return self.var_matches.keys()
        else:
            return []

    def getVarMatch(self, id):
        if self.var_matches:
            matches = self.var_matches.get(id, ())
            if not isinstance(matches, tuple):
                # Old version, convert it.
                matches = (matches,)
                self.var_matches[id] = matches
            return matches
        else:
            return ()

    def getVarMatchText(self, id):
        values = self.getVarMatch(id)
        return '; '.join(values)

    _properties_form = DTMLFile('worklist_properties', _dtmldir)

    def manage_properties(self, REQUEST, manage_tabs_message=None):
        '''
        '''
        return self._properties_form(REQUEST,
                                     management_view='Properties',
                                     manage_tabs_message=manage_tabs_message,
                                     )

    def setProperties(self, description,
                      actbox_name='', actbox_url='', actbox_category='global',
                      props=None, REQUEST=None):
        '''
        '''
        if props is None:
            props = REQUEST
        self.description = str(description)
        for key in self.getAvailableCatalogVars():
            # Populate var_matches.
            fieldname = 'var_match_%s' % key
            v = props.get(fieldname, '')
            if v:
                if not self.var_matches:
                    self.var_matches = PersistentMapping()
                v = [ var.strip() for var in v.split(';') ]
                self.var_matches[key] = tuple(v)
            else:
                if self.var_matches and self.var_matches.has_key(key):
                    del self.var_matches[key]
        self.actbox_name = str(actbox_name)
        self.actbox_url = str(actbox_url)
        self.actbox_category = str(actbox_category)
        g = Guard()
        if g.changeFromProperties(props or REQUEST):
            self.guard = g
        else:
            self.guard = None
        if REQUEST is not None:
            return self.manage_properties(REQUEST, 'Properties changed.')
Ejemplo n.º 8
0
class ElementSpec( Persistent ):
    """
        Represent all the tool knows about a single metadata element.
    """
    #
    #   Default values.
    #
    is_multi_valued = 0

    def __init__( self, is_multi_valued ):
        self.is_multi_valued  = is_multi_valued
        self.policies         = PersistentMapping()
        self.policies[ None ] = self._makePolicy()  # set default policy
        
    
    def _makePolicy( self ):
        return MetadataElementPolicy( self.is_multi_valued )

    def isMultiValued( self ):
        """
            Is this element multi-valued?
        """
        return self.is_multi_valued

    def getPolicy( self, typ=None ):
        """
            Find the policy this element for objects whose type
            object name is 'typ';  return a default, if none found.
        """
        try:
            return self.policies[ typ ]
        except KeyError:
            return self.policies[ None ]

    def listPolicies( self ):
        """
            Return a list of all policies for this element.
        """
        return self.policies.items()

    def addPolicy( self, typ ):
        """
            Add a policy to this element for objects whose type
            object name is 'typ'.
        """
        if typ is None:
            raise MetadataError, "Can't replace default policy."

        if self.policies.has_key( typ ):
            raise MetadataError, "Existing policy for content type:" + typ

        self.policies[ typ ] = self._makePolicy()

    def removePolicy( self, typ ):
        """
            Remove the policy from this element for objects whose type
            object name is 'typ' (*not* the default, however).
        """
        if typ is None:
            raise MetadataError, "Can't remove default policy."
        del self.policies[ typ ]
Ejemplo n.º 9
0
class MessageCatalog(LanguageManager, ObjectManager, SimpleItem):
    """Stores messages and their translations...
    """

    meta_type = "MessageCatalog"

    security = ClassSecurityInfo()

    def __init__(self, id, title, sourcelang, languages):
        self.id = id

        self.title = title

        # Language Manager data
        self._languages = tuple(languages)
        self._default_language = sourcelang

        # Here the message translations are stored
        self._messages = PersistentMapping()

        # Data for the PO files headers
        self._po_headers = PersistentMapping()
        for lang in self._languages:
            self._po_headers[lang] = empty_po_header

    #######################################################################
    # Private API
    #######################################################################
    def get_message_key(self, message):
        if message in self._messages:
            return message
        # A message may be stored as unicode or byte string
        encoding = HTTPRequest.default_encoding
        if isinstance(message, unicode):
            message = message.encode(encoding)
        else:
            message = unicode(message, encoding)
        if message in self._messages:
            return message

    def get_translations(self, message):
        message = self.get_message_key(message)
        return self._messages[message]

    #######################################################################
    # Public API
    #######################################################################
    security.declarePublic("message_exists")

    def message_exists(self, message):
        """ """
        return self._messages.has_key(message)

    security.declareProtected("Manage messages", "message_edit")

    def message_edit(self, message, language, translation, note):
        """ """
        self._messages[message][language] = translation
        self._messages[message]["note"] = note

    security.declareProtected("Manage messages", "message_del")

    def message_del(self, message):
        """ """
        del self._messages[message]

    security.declarePublic("gettext")

    def gettext(self, message, lang=None, add=1, default=None):
        """Returns the message translation from the database if available.

        If add=1, add any unknown message to the database.
        If a default is provided, use it instead of the message id
        as a translation for unknown messages.
        """
        if not isinstance(message, (str, unicode)):
            raise TypeError, "only strings can be translated."

        message = message.strip()
        # assume empty message is always translated as empty message
        if not message:
            return message

        if default is None:
            default = message

        # Add it if it's not in the dictionary
        if add and not self._messages.has_key(message) and message:
            update_transaction_note()
            self._messages[message] = PersistentMapping()

        if message and not self._messages[message].has_key("en"):
            self._messages[message]["en"] = default

        # Get the string
        if self._messages.has_key(message):
            m = self._messages[message]

            if lang is None:
                # Builds the list of available languages
                # should the empty translations be filtered?
                available_languages = list(self._languages)

                # Imagine that the default language is 'en'. There is no
                # translation from 'en' to 'en' in the message catalog
                # The user has the preferences 'en' and 'nl' in that order
                # The next two lines make certain 'en' is shown, not 'nl'
                if not self._default_language in available_languages:
                    available_languages.append(self._default_language)

                # Get the language!
                lang = lang_negotiator(available_languages)

                # Is it None? use the default
                if lang is None:
                    lang = self._default_language

            if lang is not None:
                return m.get(lang) or default

        return default

    __call__ = gettext

    def translate(self, domain, msgid, *args, **kw):
        """This method is required to get the i18n namespace from ZPT working.
        """

        default = kw.get("default")
        if default is not None and not default.strip():
            default = None
        msgstr = self.gettext(msgid, default=default)
        mapping = kw.get("mapping")
        return interpolate(msgstr, mapping)

    #######################################################################
    # Management screens
    #######################################################################
    def manage_options(self):
        """ """
        options = (
            (
                {"label": u"Messages", "action": "manage_messages", "help": ("Localizer", "MC_messages.stx")},
                {"label": u"Properties", "action": "manage_propertiesForm"},
                {"label": u"Import", "action": "manage_Import_form", "help": ("Localizer", "MC_importExport.stx")},
                {"label": u"Export", "action": "manage_Export_form", "help": ("Localizer", "MC_importExport.stx")},
            )
            + LanguageManager.manage_options
            + SimpleItem.manage_options
        )

        r = []
        for option in options:
            option = option.copy()
            option["label"] = _(option["label"])
            r.append(option)

        return r

    #######################################################################
    # Management screens -- Messages
    #######################################################################
    security.declareProtected("Manage messages", "manage_messages")
    manage_messages = LocalDTMLFile("ui/MC_messages", globals())

    security.declarePublic("get_namespace")

    def get_namespace(self, REQUEST):
        """For the management interface, allows to filter the messages to
        show.
        """
        # Check whether there are languages or not
        languages = self.get_languages_mapping()
        if not languages:
            return {}

        # Input
        batch_start = REQUEST.get("batch_start", 0)
        batch_size = REQUEST.get("batch_size", 15)
        empty = REQUEST.get("empty", 0)
        regex = REQUEST.get("regex", "")
        message = REQUEST.get("msg", None)

        # Build the namespace
        namespace = {}
        namespace["batch_size"] = batch_size
        namespace["empty"] = empty
        namespace["regex"] = regex

        # The language
        lang = REQUEST.get("lang", None) or languages[0]["code"]
        namespace["language"] = lang

        # Filter the messages
        query = regex.strip()
        try:
            query = compile(query)
        except:
            query = compile("")

        messages = []
        for m, t in self._messages.items():
            if query.search(m) and (not empty or not t.get(lang, "").strip()):
                messages.append(m)
        messages.sort(filter_sort)
        # How many messages
        n = len(messages)
        namespace["n_messages"] = n

        # Calculate the start
        while batch_start >= n:
            batch_start = batch_start - batch_size
        if batch_start < 0:
            batch_start = 0
        namespace["batch_start"] = batch_start
        # Select the batch to show
        batch_end = batch_start + batch_size
        messages = messages[batch_start:batch_end]
        # Batch links
        namespace["previous"] = get_url(REQUEST.URL, batch_start - batch_size, batch_size, regex, lang, empty)
        namespace["next"] = get_url(REQUEST.URL, batch_start + batch_size, batch_size, regex, lang, empty)

        # Get the message
        message_encoded = None
        translations = {}
        if message is None:
            if messages:
                message = messages[0]
                translations = self.get_translations(message)
                message = to_unicode(message)
                message_encoded = message_encode(message)
        else:
            message_encoded = message
            message = message_decode(message_encoded)
            translations = self.get_translations(message)
            message = to_unicode(message)
        namespace["message"] = message
        namespace["message_encoded"] = message_encoded
        namespace["translations"] = translations
        namespace["translation"] = translations.get(lang, "")
        namespace["note"] = translations.get("note", "")

        # Calculate the current message
        namespace["messages"] = []
        for x in messages:
            x = to_unicode(x)
            x_encoded = message_encode(x)
            url = get_url(REQUEST.URL, batch_start, batch_size, regex, lang, empty, msg=x_encoded)
            namespace["messages"].append(
                {"message": x, "message_encoded": x_encoded, "current": x == message, "url": url}
            )

        # The languages
        for language in languages:
            code = language["code"]
            language["name"] = _(language["name"], language=code)
            language["url"] = get_url(REQUEST.URL, batch_start, batch_size, regex, code, empty, msg=message_encoded)
        namespace["languages"] = languages

        return namespace

    security.declareProtected("Manage messages", "manage_editMessage")

    def manage_editMessage(self, message, language, translation, note, REQUEST, RESPONSE):
        """Modifies a message.
        """
        message_encoded = message
        message = message_decode(message_encoded)
        message_key = self.get_message_key(message)
        self.message_edit(message_key, language, translation, note)

        url = get_url(
            REQUEST.URL1 + "/manage_messages",
            REQUEST["batch_start"],
            REQUEST["batch_size"],
            REQUEST["regex"],
            REQUEST.get("lang", ""),
            REQUEST.get("empty", 0),
            msg=message_encoded,
            manage_tabs_message=_(u"Saved changes."),
        )
        RESPONSE.redirect(url)

    security.declareProtected("Manage messages", "manage_delMessage")

    def manage_delMessage(self, message, REQUEST, RESPONSE):
        """ """
        message = message_decode(message)
        message_key = self.get_message_key(message)
        self.message_del(message_key)

        url = get_url(
            REQUEST.URL1 + "/manage_messages",
            REQUEST["batch_start"],
            REQUEST["batch_size"],
            REQUEST["regex"],
            REQUEST.get("lang", ""),
            REQUEST.get("empty", 0),
            manage_tabs_message=_(u"Saved changes."),
        )
        RESPONSE.redirect(url)

    #######################################################################
    # Management screens -- Properties
    # Management screens -- Import/Export
    # FTP access
    #######################################################################
    security.declareProtected("View management screens", "manage_propertiesForm")
    manage_propertiesForm = LocalDTMLFile("ui/MC_properties", globals())

    security.declareProtected("View management screens", "manage_properties")

    def manage_properties(self, title, REQUEST=None, RESPONSE=None):
        """Change the Message Catalog properties.
        """
        self.title = title

        if RESPONSE is not None:
            RESPONSE.redirect("manage_propertiesForm")

    # Properties management screen
    security.declareProtected("View management screens", "get_po_header")

    def get_po_header(self, lang):
        """ """
        # For backwards compatibility
        if not hasattr(aq_base(self), "_po_headers"):
            self._po_headers = PersistentMapping()

        return self._po_headers.get(lang, empty_po_header)

    security.declareProtected("View management screens", "update_po_header")

    def update_po_header(
        self,
        lang,
        last_translator_name=None,
        last_translator_email=None,
        language_team=None,
        charset=None,
        REQUEST=None,
        RESPONSE=None,
    ):
        """ """
        header = self.get_po_header(lang)

        if last_translator_name is None:
            last_translator_name = header["last_translator_name"]

        if last_translator_email is None:
            last_translator_email = header["last_translator_email"]

        if language_team is None:
            language_team = header["language_team"]

        if charset is None:
            charset = header["charset"]

        header = {
            "last_translator_name": last_translator_name,
            "last_translator_email": last_translator_email,
            "language_team": language_team,
            "charset": charset,
        }

        self._po_headers[lang] = header

        if RESPONSE is not None:
            RESPONSE.redirect("manage_propertiesForm")

    security.declareProtected("View management screens", "manage_Import_form")
    manage_Import_form = LocalDTMLFile("ui/MC_Import_form", globals())

    security.declarePublic("get_charsets")

    def get_charsets(self):
        """ """
        return charsets[:]

    security.declarePublic("manage_export")

    def manage_export(self, x, REQUEST=None, RESPONSE=None):
        """Exports the content of the message catalog either to a template
        file (locale.pot) or to an language specific PO file (<x>.po).
        """
        # Get the PO header info
        header = self.get_po_header(x)
        last_translator_name = header["last_translator_name"]
        last_translator_email = header["last_translator_email"]
        language_team = header["language_team"]
        charset = header["charset"]

        # PO file header, empty message.
        po_revision_date = strftime("%Y-%m-%d %H:%m+%Z", gmtime(time()))
        pot_creation_date = po_revision_date
        last_translator = "%s <%s>" % (last_translator_name, last_translator_email)

        if x == "locale.pot":
            language_team = "LANGUAGE <*****@*****.**>"
        else:
            language_team = "%s <%s>" % (x, language_team)

        r = [
            'msgid ""',
            'msgstr "Project-Id-Version: %s\\n"' % self.title,
            '"POT-Creation-Date: %s\\n"' % pot_creation_date,
            '"PO-Revision-Date: %s\\n"' % po_revision_date,
            '"Last-Translator: %s\\n"' % last_translator,
            '"Language-Team: %s\\n"' % language_team,
            '"MIME-Version: 1.0\\n"',
            '"Content-Type: text/plain; charset=%s\\n"' % charset,
            '"Content-Transfer-Encoding: 8bit\\n"',
            "",
            "",
        ]

        # Get the messages, and perhaps its translations.
        d = {}
        if x == "locale.pot":
            filename = x
            for k in self._messages.keys():
                d[k] = ""
        else:
            filename = "%s.po" % x
            for k, v in self._messages.items():
                try:
                    d[k] = v[x]
                except KeyError:
                    d[k] = ""

        # Generate the file
        def backslashescape(x):
            quote_esc = compile(r'"')
            x = quote_esc.sub('\\"', x)

            trans = [("\n", "\\n"), ("\r", "\\r"), ("\t", "\\t")]
            for a, b in trans:
                x = x.replace(a, b)

            return x

        # Generate sorted msgids to simplify diffs
        dkeys = d.keys()
        dkeys.sort()
        for k in dkeys:
            r.append('msgid "%s"' % backslashescape(k))
            v = d[k]
            r.append('msgstr "%s"' % backslashescape(v))
            r.append("")

        if RESPONSE is not None:
            RESPONSE.setHeader("Content-type", "application/data")
            RESPONSE.setHeader("Content-Disposition", "inline;filename=%s" % filename)

        r2 = []
        for x in r:
            if isinstance(x, unicode):
                r2.append(x.encode(charset))
            else:
                r2.append(x)

        return "\n".join(r2)

    security.declareProtected("Manage messages", "po_import")

    def po_import(self, lang, data):
        """ """
        messages = self._messages

        # Load the data
        po = POFile(string=data)
        for msgid in po.get_msgids():
            if msgid:
                msgstr = po.get_msgstr(msgid) or ""
                if not messages.has_key(msgid):
                    messages[msgid] = PersistentMapping()
                messages[msgid][lang] = msgstr

        # Set the encoding (the full header should be loaded XXX)
        self.update_po_header(lang, charset=po.get_encoding())

    security.declareProtected("Manage messages", "manage_import")

    def manage_import(self, lang, file, REQUEST=None, RESPONSE=None):
        """ """
        # XXX For backwards compatibility only, use "po_import" instead.
        if isinstance(file, str):
            content = file
        else:
            content = file.read()

        self.po_import(lang, content)

        if RESPONSE is not None:
            RESPONSE.redirect("manage_messages")

    def objectItems(self, spec=None):
        """ """
        for lang in self._languages:
            if not hasattr(aq_base(self), lang):
                self._setObject(lang, POFile(lang))

        r = MessageCatalog.inheritedAttribute("objectItems")(self, spec)
        return r

    #######################################################################
    # TMX support
    security.declareProtected("View management screens", "manage_Export_form")
    manage_Export_form = LocalDTMLFile("ui/MC_Export_form", globals())

    security.declareProtected("Manage messages", "tmx_export")

    def tmx_export(self, REQUEST, RESPONSE=None):
        """Exports the content of the message catalog to a TMX file
        """
        orglang = self._default_language

        # Get the header info
        header = self.get_po_header(orglang)
        charset = header["charset"]

        # build data structure for the xml header
        xml_header = {}
        xml_header["standalone"] = -1
        xml_header["xml_version"] = u"1.0"
        xml_header["document_type"] = (u"tmx", u"http://www.lisa.org/tmx/tmx14.dtd")
        # build data structure for the tmx header
        version = u"1.4"
        tmx_header = {}
        tmx_header["creationtool"] = u"Localizer"
        tmx_header["creationtoolversion"] = u"1.x"
        tmx_header["datatype"] = u"plaintext"
        tmx_header["segtype"] = u"paragraph"
        tmx_header["adminlang"] = u"%s" % orglang
        tmx_header["srclang"] = u"%s" % orglang
        tmx_header["o-encoding"] = u"%s" % charset.lower()

        # handle messages
        d = {}
        filename = "%s.tmx" % self.id
        for msgkey, transunit in self._messages.items():
            sentences = {}
            for lang in transunit.keys():
                if lang != "note":
                    s = Sentence(transunit[lang], {"lang": "%s" % lang})
                    sentences[lang] = s

            if orglang not in transunit.keys():
                s = Sentence(msgkey, {"lang": "%s" % orglang})
                sentences[orglang] = s

            if transunit.has_key("note"):
                d[msgkey] = Message(sentences, {}, [Note(transunit.get("note"))])
            else:
                d[msgkey] = Message(sentences)

        tmx = TMX()
        tmx.build(xml_header, version, tmx_header, d)

        if RESPONSE is not None:
            RESPONSE.setHeader("Content-type", "application/data")
            RESPONSE.setHeader("Content-Disposition", 'attachment; filename="%s"' % filename)

        return tmx.to_str()

    security.declareProtected("Manage messages", "tmx_import")

    def tmx_import(self, howmuch, file, REQUEST=None, RESPONSE=None):
        """Imports a TMX level 1 file.
        """
        try:
            data = file.read()
            tmx = TMX(string=data)
        except:
            return MessageDialog(
                title="Parse error", message=_("impossible to parse the file"), action="manage_Import_form"
            )

        num_notes = 0
        num_trans = 0

        if howmuch == "clear":
            # Clear the message catalogue prior to import
            self._messages = {}
            self._languages = ()
            self._default_language = tmx.get_srclang()

        for (id, msg) in tmx.state.messages.items():
            if not self._messages.has_key(id) and howmuch == "existing":
                pass
            else:
                msg.msgstr.pop(self._default_language)
                if not self._messages.has_key(id):
                    self._messages[id] = {}
                for lang in msg.msgstr.keys():
                    # normalize the languageTag and extract the core
                    (core, local) = LanguageTag.decode(lang)
                    lang = LanguageTag.encode((core, local))
                    if lang not in self._languages:
                        self._languages += (lang,)
                    if msg.msgstr[lang].text:
                        self._messages[id][lang] = msg.msgstr[lang].text
                        if core != lang and core != self._default_language:
                            if core not in self._languages:
                                self._languages += (core,)
                            if not msg.msgstr.has_key(core):
                                self._messages[id][core] = msg.msgstr[lang].text
                if msg.notes:
                    ns = [m.text for m in msg.notes]
                    self._messages[id]["note"] = u" ".join(ns)
                    num_notes += 1
                num_trans += 1

        if REQUEST is not None:
            return MessageDialog(
                title=_(u"Messages imported"),
                message=_(u"Imported %d messages and %d notes") % (num_trans, num_notes),
                action="manage_messages",
            )

    #######################################################################
    # Backwards compatibility (XXX)
    #######################################################################

    hasmsg = message_exists
    hasLS = message_exists  # CMFLocalizer uses it

    security.declareProtected("Manage messages", "xliff_export")

    def xliff_export(self, x, export_all=1, REQUEST=None, RESPONSE=None):
        """Exports the content of the message catalog to an XLIFF file
        """
        orglang = self._default_language
        export_all = int(export_all)
        from DateTime import DateTime

        # Generate the XLIFF file header
        RESPONSE.setHeader("Content-Type", "text/xml; charset=UTF-8")
        RESPONSE.setHeader("Content-Disposition", 'attachment; filename="%s_%s_%s.xlf"' % (self.id, orglang, x))
        # build data structure for the xml header
        xml_header = {}
        xml_header["standalone"] = -1
        xml_header["xml_version"] = u"1.0"
        xml_header["document_type"] = (u"xliff", u"http://www.oasis-open.org/committees/xliff/documents/xliff.dtd")

        version = u"1.0"

        # build the data-stucture for the File tag
        attributes = {}
        attributes["original"] = u"/%s" % self.absolute_url(1)
        attributes["product-name"] = u"Localizer"
        attributes["product-version"] = u"1.1.x"
        attributes["data-type"] = u"plaintext"
        attributes["source-language"] = orglang
        attributes["target-language"] = x
        attributes["date"] = DateTime().HTML4()

        # Get the messages, and perhaps its translations.
        d = {}
        for msgkey, transunit in self._messages.items():
            target = transunit.get(x, "")
            # if export_all=1 export all messages otherwise export
            # only untranslated messages
            if export_all or not target:
                id = md5text(msgkey)
                notes = []
                if transunit.has_key("note") and transunit["note"]:
                    notes = [xliff_Note(transunit["note"])]
                if target:
                    t = Translation(msgkey, target, {"id": id}, notes)
                else:
                    t = Translation(msgkey, msgkey, {"id": id}, notes)
                d[msgkey] = t

        files = [xliff_File(d, attributes)]

        xliff = XLIFF()
        xliff.build(xml_header, version, files)

        return xliff.to_str()

    security.declareProtected("Manage messages", "xliff_import")

    def xliff_import(self, howmuch, file, REQUEST=None):
        """XLIFF is the XML Localization Interchange File Format designed by a
        group of software providers.  It is specified by www.oasis-open.org
        """
        try:
            data = file.read()
            xliff = XLIFF(string=data)
        except:
            return MessageDialog(
                title="Parse error", message=_("impossible to parse the file"), action="manage_Import_form"
            )

        num_notes = 0
        num_trans = 0
        (file_ids, sources, targets) = xliff.get_languages()

        if howmuch == "clear":
            # Clear the message catalogue prior to import
            self._messages = {}
            self._languages = ()
            self._default_language = sources[0]

        # update languages
        if len(sources) > 1 or sources[0] != self._default_language:
            return MessageDialog(
                title="Language error", message=_("incompatible language sources"), action="manage_Import_form"
            )
        for lang in targets:
            if lang != self._default_language and lang not in self._languages:
                self._languages += (lang,)

        # get messages
        for file in xliff.state.files:
            cur_target = file.attributes.get("target-language", "")
            for msg in file.body.keys():
                if not self._messages.has_key(msg) and howmuch == "existing":
                    pass
                else:
                    if not self._messages.has_key(msg):
                        self._messages[msg] = {}

                    if cur_target and file.body[msg].target:
                        self._messages[msg][cur_target] = file.body[msg].target
                        num_trans += 1
                    if file.body[msg].notes:
                        ns = [n.text for n in file.body[msg].notes]
                        comment = " ".join(ns)
                        self._messages[msg]["note"] = comment
                        num_notes += 1

        if REQUEST is not None:
            return MessageDialog(
                title=_(u"Messages imported"),
                message=(_(u"Imported %d messages and %d notes to %s") % (num_trans, num_notes, " ".join(targets))),
                action="manage_messages",
            )
Ejemplo n.º 10
0
class MessageCatalog(LanguageManager, ObjectManager, SimpleItem):
    """
    Stores messages and their translations...
    """

    meta_type = 'MessageCatalog'

    security = ClassSecurityInfo()


    def __init__(self, id, title, sourcelang, languages):
        self.id = id

        self.title = title

        # Language Manager data
        self._languages = tuple(languages)
        self._default_language = sourcelang

        # Here the message translations are stored
        self._messages = PersistentMapping()

        # Data for the PO files headers
        self._po_headers = PersistentMapping()
        for lang in self._languages:
            self._po_headers[lang] = empty_po_header


    #######################################################################
    # Public API
    #######################################################################
    security.declarePublic('message_encode')
    def message_encode(self, message):
        """
        Encodes a message to an ASCII string.
        To be used in the user interface, to avoid problems with the
        encodings, HTML entities, etc..
        """
        if type(message) is UnicodeType:
            msg = 'u' + message.encode('utf8')
        else:
            msg = 'n' + message

        return base64.encodestring(msg)


    security.declarePublic('message_decode')
    def message_decode(self, message):
        """
        Decodes a message from an ASCII string.
        To be used in the user interface, to avoid problems with the
        encodings, HTML entities, etc..
        """
        message = base64.decodestring(message)
        type = message[0]
        message = message[1:]
        if type == 'u':
            return unicode(message, 'utf8')
        return message


    security.declarePublic('message_exists')
    def message_exists(self, message):
        """ """
        return self._messages.has_key(message)


    security.declareProtected('Manage messages', 'message_edit')
    def message_edit(self, message, language, translation, note):
        """ """
        self._messages[message][language] = translation
        self._messages[message]['note'] = note


    security.declareProtected('Manage messages', 'message_del')
    def message_del(self, message):
        """ """
        del self._messages[message]


    security.declarePublic('gettext')
    def gettext(self, message, lang=None, add=1, default=_marker):
        """Returns the message translation from the database if available.

        If add=1, add any unknown message to the database.
        If a default is provided, use it instead of the message id
        as a translation for unknown messages.
        """

        if type(message) not in (StringType, UnicodeType):
            raise TypeError, 'only strings can be translated.'

        message = message.strip()

        if default is _marker:
            default = message

        # Add it if it's not in the dictionary
        if add and not self._messages.has_key(message) and message:
            self._messages[message] = PersistentMapping()

        # Get the string
        if self._messages.has_key(message):
            m = self._messages[message]

            if lang is None:
                # Builds the list of available languages
                # should the empty translations be filtered?
                available_languages = list(self._languages)

                # Imagine that the default language is 'en'. There is no
                # translation from 'en' to 'en' in the message catalog
                # The user has the preferences 'en' and 'nl' in that order
                # The next two lines make certain 'en' is shown, not 'nl'
                if not self._default_language in available_languages:
                    available_languages.append(self._default_language)

                # Get the language!
                lang = lang_negotiator(available_languages)

                # Is it None? use the default
                if lang is None:
                    lang = self._default_language

            if lang is not None:
                return m.get(lang) or default

        return default


    __call__ = gettext


    def translate(self, domain, msgid, *args, **kw):
        """
        This method is required to get the i18n namespace from ZPT working.
        """
        return self.gettext(msgid)


    #######################################################################
    # Management screens
    #######################################################################
    def manage_options(self):
        """ """
        options = (
            {'label': N_('Messages'), 'action': 'manage_messages',
             'help': ('Localizer', 'MC_messages.stx')},
            {'label': N_('Properties'), 'action': 'manage_propertiesForm'},
            {'label': N_('Import/Export'), 'action': 'manage_importExport',
             'help': ('Localizer', 'MC_importExport.stx')},
            {'label': N_('TMX'), 'action': 'manage_tmx'}) \
            + LanguageManager.manage_options \
            + SimpleItem.manage_options

        r = []
        for option in options:
            option = option.copy()
            option['label'] = _(option['label'])
            r.append(option)

        return r


    #######################################################################
    # Management screens -- Messages
    #######################################################################
    security.declareProtected('Manage messages', 'manage_messages')
    manage_messages = LocalDTMLFile('ui/MC_messages', globals())


    security.declareProtected('Manage messages', 'get_translations')
    def get_translations(self, message):
        """ """
        return self._messages[message]


    security.declarePublic('get_url')
    def get_url(self, url, batch_start, batch_size, regex, lang, empty, **kw):
        """ """
        params = []
        for key, value in kw.items():
            if value is not None:
                params.append('%s=%s' % (key, quote(value)))

        params.extend(['batch_start:int=%d' % batch_start,
                       'batch_size:int=%d' % batch_size,
                       'regex=%s' % quote(regex),
                       'empty=%s' % (empty and 'on' or '')])

        if lang:
            params.append('lang=%s' % lang)

        return url + '?' + '&amp;'.join(params)

    def to_unicode(self, x):
        """
        In Zope the ISO-8859-1 encoding has an special status, normal strings
        are considered to be in this encoding by default.
        """
        if type(x) is StringType:
            x = unicode(x, 'iso-8859-1')
        return x


    def filter_sort(self, x, y):
        x = self.to_unicode(x)
        y = self.to_unicode(y)
        return cmp(x, y)


    security.declarePublic('filter')
    def filter(self, message, lang, empty, regex, batch_start, batch_size=15):
        """
        For the management interface, allows to filter the messages to show.
        """
        # Filter the messages
        regex = regex.strip()

        try:
            regex = re.compile(regex)
        except:
            regex = re.compile('')

        messages = []
        for m, t in self._messages.items():
            if regex.search(m) and (not empty or not t.get(lang, '').strip()):
                messages.append(m)
        messages.sort(self.filter_sort)

        # How many messages
        n = len(messages)

        # Calculate the start
        while batch_start >= n:
            batch_start = batch_start - batch_size

        if batch_start < 0:
            batch_start = 0

        # Select the batch to show
        batch_end = batch_start + batch_size
        messages = messages[batch_start:batch_end]

        # Get the message
        message_encoded = None
        if message is None:
            if messages:
                message = messages[0]
                message_encoded = self.message_encode(message)
        else:
            message_encoded = message
            message = self.message_decode(message)

        # Calculate the current message
        aux = []
        for x in messages:
            current = type(x) is type(message) \
                      and self.to_unicode(x) == self.to_unicode(message)
            aux.append({'message': x, 'current': current})

        return {'messages': aux,
                'n_messages': n,
                'batch_start': batch_start,
                'message': message,
                'message_encoded': message_encoded}


    security.declareProtected('Manage messages', 'manage_editMessage')
    def manage_editMessage(self, message, language, translation, note,
                           REQUEST, RESPONSE):
        """Modifies a message."""
        message_encoded = message
        message = self.message_decode(message_encoded)
        self.message_edit(message, language, translation, note)

        url = self.get_url(REQUEST.URL1 + '/manage_messages',
                           REQUEST['batch_start'], REQUEST['batch_size'],
                           REQUEST['regex'], REQUEST.get('lang', ''),
                           REQUEST.get('empty', 0),
                           msg=message_encoded,
                           manage_tabs_message=_('Saved changes.'))
        RESPONSE.redirect(url)


    security.declareProtected('Manage messages', 'manage_delMessage')
    def manage_delMessage(self, message, REQUEST, RESPONSE):
        """ """
        message = self.message_decode(message)
        self.message_del(message)

        url = self.get_url(REQUEST.URL1 + '/manage_messages',
                           REQUEST['batch_start'], REQUEST['batch_size'],
                           REQUEST['regex'], REQUEST.get('lang', ''),
                           REQUEST.get('empty', 0),
                           manage_tabs_message=_('Saved changes.'))
        RESPONSE.redirect(url)



    #######################################################################
    # Management screens -- Properties
    # Management screens -- Import/Export
    # FTP access
    #######################################################################
    security.declareProtected('View management screens',
                              'manage_propertiesForm')
    manage_propertiesForm = LocalDTMLFile('ui/MC_properties', globals())


    security.declareProtected('View management screens', 'manage_properties')
    def manage_properties(self, title, REQUEST=None, RESPONSE=None):
        """Change the Message Catalog properties."""
        self.title = title

        if RESPONSE is not None:
            RESPONSE.redirect('manage_propertiesForm')


    # Properties management screen
    security.declareProtected('View management screens', 'get_po_header')
    def get_po_header(self, lang):
        """ """
        # For backwards compatibility
        if not hasattr(aq_base(self), '_po_headers'):
            self._po_headers = PersistentMapping()

        return self._po_headers.get(lang, empty_po_header)


    security.declareProtected('View management screens', 'update_po_header')
    def update_po_header(self, lang,
                         last_translator_name=None,
                         last_translator_email=None,
                         language_team=None,
                         charset=None,
                         REQUEST=None, RESPONSE=None):
        """ """
        header = self.get_po_header(lang)

        if last_translator_name is None:
            last_translator_name = header['last_translator_name']

        if last_translator_email is None:
            last_translator_email = header['last_translator_email']

        if language_team is None:
            language_team = header['language_team']

        if charset is None:
            charset = header['charset']

        header = {'last_translator_name': last_translator_name,
                  'last_translator_email': last_translator_email,
                  'language_team': language_team,
                  'charset': charset}

        self._po_headers[lang] = header

        if RESPONSE is not None:
            RESPONSE.redirect('manage_propertiesForm')



    security.declareProtected('View management screens', 'manage_importExport')
    manage_importExport = LocalDTMLFile('ui/MC_importExport', globals())


    security.declarePublic('get_charsets')
    def get_charsets(self):
        """ """
        return charsets[:]


    security.declarePublic('manage_export')
    def manage_export(self, x, REQUEST=None, RESPONSE=None):
        """
        Exports the content of the message catalog either to a template
        file (locale.pot) or to an language specific PO file (<x>.po).
        """
        # Get the PO header info
        header = self.get_po_header(x)
        last_translator_name = header['last_translator_name']
        last_translator_email = header['last_translator_email']
        language_team = header['language_team']
        charset = header['charset']

        # PO file header, empty message.
        po_revision_date = time.strftime('%Y-%m-%d %H:%m+%Z',
                                         time.gmtime(time.time()))
        pot_creation_date = po_revision_date
        last_translator = '%s <%s>' % (last_translator_name,
                                       last_translator_email)

        if x == 'locale.pot':
            language_team = 'LANGUAGE <*****@*****.**>'
        else:
            language_team = '%s <%s>' % (x, language_team)

        r = ['msgid ""',
             'msgstr "Project-Id-Version: %s\\n"' % self.title,
             '"POT-Creation-Date: %s\\n"' % pot_creation_date,
             '"PO-Revision-Date: %s\\n"' % po_revision_date,
             '"Last-Translator: %s\\n"' % last_translator,
             '"Language-Team: %s\\n"' % language_team,
             '"MIME-Version: 1.0\\n"',
             '"Content-Type: text/plain; charset=%s\\n"' % charset,
             '"Content-Transfer-Encoding: 8bit\\n"',
             '', '']


        # Get the messages, and perhaps its translations.
        d = {}
        if x == 'locale.pot':
            filename = x
            for k in self._messages.keys():
                d[k] = ""
        else:
            filename = '%s.po' % x
            for k, v in self._messages.items():
                try:
                    d[k] = v[x]
                except KeyError:
                    d[k] = ""

        # Generate the file
        def backslashescape(x):
            quote_esc = re.compile(r'"')
            x = quote_esc.sub('\\"', x)

            trans = [('\n', '\\n'), ('\r', '\\r'), ('\t', '\\t')]
            for a, b in trans:
                x = x.replace(a, b)

            return x

        # Generate sorted msgids to simplify diffs
        dkeys = d.keys()
        dkeys.sort()
        for k in dkeys:
            r.append('msgid "%s"' % backslashescape(k))
            v = d[k]
            r.append('msgstr "%s"' % backslashescape(v))
            r.append('')

        if RESPONSE is not None:
            RESPONSE.setHeader('Content-type','application/data')
            RESPONSE.setHeader('Content-Disposition',
                               'inline;filename=%s' % filename)

        r2 = []
        for x in r:
            if type(x) is UnicodeType:
                r2.append(x.encode(charset))
            else:
                r2.append(x)

        return '\n'.join(r2)


    security.declareProtected('Manage messages', 'po_import')
    def po_import(self, lang, data):
        """ """
        messages = self._messages

        resource = memory.File(data)
        po = PO.PO(resource)

        # Load the data
        for msgid in po.get_msgids():
            if msgid:
                msgstr = po.get_msgstr(msgid) or ''
                if not messages.has_key(msgid):
                    messages[msgid] = PersistentMapping()
                messages[msgid][lang] = msgstr

        # Set the encoding (the full header should be loaded XXX)
        self.update_po_header(lang, charset=po.get_encoding())


    security.declareProtected('Manage messages', 'manage_import')
    def manage_import(self, lang, file, REQUEST=None, RESPONSE=None):
        """ """
        # XXX For backwards compatibility only, use "po_import" instead.
        if isinstance(file, str):
            content = file
        else:
            content = file.read()

        self.po_import(lang, content)

        if RESPONSE is not None:
            RESPONSE.redirect('manage_messages')


    def objectItems(self, spec=None):
        """ """
        for lang in self._languages:
            if not hasattr(aq_base(self), lang):
                self._setObject(lang, POFile(lang))

        r = MessageCatalog.inheritedAttribute('objectItems')(self, spec)
        return r


    #######################################################################
    # TMX support
    security.declareProtected('View management screens', 'manage_tmx')
    manage_tmx = LocalDTMLFile('ui/MC_tmx', globals())


    security.declareProtected('Manage messages', 'tmx_export')
    def tmx_export(self, REQUEST, RESPONSE=None):
        """
        Exports the content of the message catalog to a TMX file
        """
        orglang = self._default_language
#       orglang = orglang.lower()

        # Get the header info
        header = self.get_po_header(orglang)
        charset = header['charset']

        r = []

        # Generate the TMX file header
        r.append('<?xml version="1.0" encoding="utf-8"?>')
        r.append('<!DOCTYPE tmx SYSTEM "http://www.lisa.org/tmx/tmx14.dtd">')
        r.append('<tmx version="1.4">')
        r.append('<header')
        r.append('creationtool="Localizer"')
        r.append('creationtoolversion="1.x"')
        r.append('datatype="plaintext"')
        r.append('segtype="paragraph"')
        r.append('adminlang="%s"' % orglang)
        r.append('srclang="%s"' % orglang)
        r.append('o-encoding="%s"' % charset.lower())

        r.append('>')
        r.append('</header>')
        r.append('')

        # Get the messages, and perhaps its translations.
        d = {}
        filename = '%s.tmx' % self.id
        for msgkey, transunit in self._messages.items():
            try:
                d[msgkey] = transunit[orglang]
            except KeyError:
                d[msgkey] = ""

        # Generate sorted msgids to simplify diffs
        dkeys = d.keys()
        dkeys.sort()
        r.append('<body>')
        for msgkey in dkeys:
            r.append('<tu>')
            transunit = self._messages.get(msgkey)
            if transunit.has_key('note') and transunit['note']:
                r.append('<note>%s</note>' % escape(transunit['note']))
            r.append('<tuv xml:lang="%s">' % orglang)
            # The key is the message
            r.append('<seg>%s</seg></tuv>' % escape(msgkey))

            for tlang in self._languages:
                if tlang != orglang:
                    v = transunit.get(tlang,'')
                    v = escape(v)
                    r.append('<tuv xml:lang="%s">' % tlang)
                    r.append('<seg>%s</seg></tuv>' % v)

            r.append('</tu>')
            r.append('')

        r.append('</body>')
        r.append('</tmx>')

        if RESPONSE is not None:
            RESPONSE.setHeader('Content-type','application/data')
            RESPONSE.setHeader('Content-Disposition',
                               'attachment; filename="%s"' % filename)

        r2 = []
        for x in r:
            if type(x) is UnicodeType:
                r2.append(x.encode('utf-8'))
            else:
                r2.append(x)

        return '\r\n'.join(r2)

    def _normalize_lang(self,langcode):
        """ Get the core language (The part before the '-') and return it
            in lowercase. If there is a local part, return it in uppercase.
        """
        dash = langcode.find('-')
        if dash == -1:
            la = langcode.lower()
            return (la,la)
        else:
            la = langcode[:dash].lower()
            lo = langcode[dash+1:].upper()
            return (la+'-'+lo,la)

    def _tmx_header(self, attrs):
        """ Works on a header (<header>)
        """
        if attrs.has_key('srclang'):
            self._v_srclang = attrs['srclang']

    def _tmx_tu(self, unit):
        """ Works on a translation unit (<tu>)
        """
        src_lang = self._default_language
        # We look up the message for the language we have chosen to be our
        # working language
        if unit.has_key(self._default_language):
            src_lang = self._default_language
        else:
            # This MUST exist. Otherwise the TMX file is bad
            src_lang = self._v_srclang
        key = unit[src_lang]
        if key == u'':
            return # Don't add empty messages
        keysum = md5text(key) # For future indexing on md5 sums

        messages = self._messages
        languages = list(self._languages)
        if not messages.has_key(key):
            if self._v_howmuch == 'clear' or self._v_howmuch == 'all':
                messages[key] = PersistentMapping()
            else:
                return # Don't add unknown messages

        self._v_num_translations = self._v_num_translations + 1

        for lang in unit.keys():
            # Since the messagecatalog's default language overrides our
            # source language anyway, we handle "*all*" correctly already.
            # In the test below "*all*" should not be allowed.
            # Languages that start with '_' are other properties
            if lang == '_note':
                messages[key]['note'] = unit[lang]
                self._v_num_notes = self._v_num_notes + 1
                continue
            if lang[0] == '_':  # Unknown special property
                continue
            if lang == '*all*' or lang == '*none*':
                lang = self._v_srclang
            (target_lang, core_lang) = self._normalize_lang(lang)
            # If the core language is not seen before then add it
            if core_lang != src_lang and core_lang not in languages:
                languages.append(core_lang)
            # If the language+locality is not seen before then add it
            if target_lang != src_lang and target_lang not in languages:
                languages.append(target_lang)
            # Add message for language+locality
            if target_lang != src_lang:
                messages[key][target_lang] = unit[target_lang]
            # Add message for core language
            if not (unit.has_key(core_lang) or core_lang == src_lang):
                messages[key][core_lang] = unit[target_lang]

        self._languages = tuple(languages)
        self._messages = messages

    security.declareProtected('Manage messages', 'tmx_import')
    def tmx_import(self, howmuch, file, REQUEST=None, RESPONSE=None):
        """ Imports a TMX level 1 file.
            We use the SAX parser. It has the benefit that it internally
            converts everything to python unicode strings.
        """
        if howmuch == 'clear':
            # Clear the message catalogue prior to import
            self._messages = {}
            self._languages = ()

        self._v_howmuch = howmuch
        self._v_srclang = self._default_language
        self._v_num_translations = 0
        self._v_num_notes = 0
        # Create a parser
        parser = make_parser()
        chandler = HandleTMXParsing(self._tmx_tu, self._tmx_header)
        # Tell the parser to use our handler
        parser.setContentHandler(chandler)
        # Don't load the DTD from the Internet
        parser.setFeature(handler.feature_external_ges, 0)
        inputsrc = InputSource()

        if type(file) is StringType:
            inputsrc.setByteStream(StringIO(file))
        else:
            content = file.read()
            inputsrc.setByteStream(StringIO(content))
        parser.parse(inputsrc)

        num_translations = self._v_num_translations
        num_notes = self._v_num_notes
        del self._v_srclang
        del self._v_howmuch
        del self._v_num_translations
        del self._v_num_notes

        if REQUEST is not None:
            return MessageDialog(
                title = _('Messages imported'),
                message = _('Imported %d messages and %d notes')
                          % (num_translations, num_notes),
                action = 'manage_messages')


    #######################################################################
    # Backwards compatibility (XXX)
    #######################################################################

    hasmsg = message_exists
    hasLS = message_exists  # CMFLocalizer uses it

    security.declareProtected('Manage messages', 'xliff_export')
    def xliff_export(self, x, export_all=1, REQUEST=None, RESPONSE=None):
        """ Exports the content of the message catalog to an XLIFF file
        """
        orglang = self._default_language
        from DateTime import DateTime
        r = []
        # alias for append function. For optimization purposes
        r_append = r.append
        # Generate the XLIFF file header
        RESPONSE.setHeader('Content-Type', 'text/xml; charset=UTF-8')
        RESPONSE.setHeader('Content-Disposition',
                           'attachment; filename="%s_%s_%s.xlf"' % (self.id,
                                                                    orglang,
                                                                    x))

        r_append('<?xml version="1.0" encoding="UTF-8"?>')
        # Version 1.1 of the DTD is not yet available - use version 1.0
        r_append('<!DOCTYPE xliff SYSTEM "http://www.oasis-open.org/committees/xliff/documents/xliff.dtd">')
        # Force a UTF-8 char in the start
        r_append(u'<!-- XLIFF Format Copyright \xa9 OASIS Open 2001-2003 -->')
        r_append('<xliff version="1.0">')
        r_append('<file')
        r_append('original="/%s"' % self.absolute_url(1))
        r_append('product-name="Localizer"')
        r_append('product-version="1.1.x"')
        r_append('datatype="plaintext"')
        r_append('source-language="%s"' % orglang)
        r_append('target-language="%s"' % x)
        r_append('date="%s"' % DateTime().HTML4())
        r_append('>')
        r_append('<header>')
#       r_append('<phase-group>')
#       r_append('<phase ')
#       r_append('phase-name="%s"' % REQUEST.get('phase_name', ''))
#       r_append('process-name="Export"')
#       r_append('tool="Localizer"')
#       r_append('date="%s"' % DateTime().HTML4())
#       r_append('company-name="%s"' % REQUEST.get('company_name', ''))
#       r_append('job-id="%s"' % REQUEST.get('job_id', ''))
#       r_append('contact-name="%s"' % REQUEST.get('contact_name', ''))
#       r_append('contact-email="%s"' % REQUEST.get('contact_email', ''))
#       r_append('/>')
#       r_append('</phase-group>')
        r_append('</header>')
        r_append('<body>')

        # Get the messages, and perhaps its translations.
        d = {}
        for msgkey, transunit in self._messages.items():
            try:
                # if export_all=1 export all messages otherwise export
                # only untranslated messages
                if int(export_all) == 1 \
                       or (int(export_all) == 0 and transunit[x] == ''):
                    d[msgkey] = transunit[x]
            except KeyError:
                d[msgkey] = ""
            if d[msgkey] == "":
                d[msgkey] = msgkey
        # Generate sorted msgids to simplify diffs
        dkeys = d.keys()
        dkeys.sort()
        for msgkey in dkeys:
            transunit = self._messages[msgkey]
            r_append('<trans-unit id="%s">' % md5text(msgkey))
            r_append(' <source>%s</source>' % escape(msgkey))
            r_append(' <target>%s</target>' % escape(d[msgkey]))
            if transunit.has_key('note') and transunit['note']:
                r_append(' <note>%s</note>' % escape(transunit['note']))
            r_append('</trans-unit>')

        r_append('</body>')
        r_append('</file>')
        r_append('</xliff>')

        r2 = []
        for x in r:
            if type(x) is UnicodeType:
                r2.append(x.encode('utf-8'))
            else:
                r2.append(x)

        return '\r\n'.join(r2)

    security.declareProtected('Manage messages', 'xliff_import')
    def xliff_import(self, file, REQUEST=None):
        """ XLIFF is the XML Localization Interchange File Format
            designed by a group of software providers.
            It is specified by www.oasis-open.org
        """

        messages = self._messages

        # Build a table of messages hashed on the md5 sum of the message
        # This is because sometimes the xliff file has the sources translated,
        # not the targets
        md5hash = {}
        for mes in messages.keys():
            hash = md5text(mes)
            md5hash[hash] = mes

        parser = HandleXliffParsing()

        # parse the xliff information
        chandler = parser.parseXLIFFFile(file)
        if chandler is None:
            return MessageDialog(title = 'Parse error',
             message = 'Unable to parse XLIFF file' ,
             action = 'manage_main',)

        header_info = chandler.getFileTag()
        #get the target language
        lang = [x for x in header_info if x[0]=='target-language'][0][1]
        (targetlang, core_lang) = self._normalize_lang(lang)

        # return a dictionary {id: (source, target)}
        body_info = chandler.getBody()

        num_notes = 0
        num_translations = 0
        # load the data
        for msgkey, transunit in body_info.items():
            # If message is not in catalog, then it is new in xliff file
            # -- not legal
            if md5hash.has_key(msgkey):
                # Normal add
                srcmsg = md5hash[msgkey]
                if transunit['note'] != messages[srcmsg].get('note',u''):
                    messages[srcmsg]['note'] = transunit['note']
                    num_notes = num_notes + 1
                if srcmsg == transunit['target']:
                    # No translation was done
                    continue
                num_translations = num_translations + 1
                if transunit['target'] == u'' and transunit['source'] != srcmsg:
                # The source was translated. Happens sometimes
                    messages[srcmsg][targetlang] = transunit['source']
                else:
                    messages[srcmsg][targetlang] = transunit['target']

        if REQUEST is not None:
            return MessageDialog(title = _('Messages imported'),
             message = _('Imported %d messages and %d notes to %s') % \
                (num_translations, num_notes, targetlang) ,
             action = 'manage_messages',)
Ejemplo n.º 11
0
class ElementSpec(SimpleItem):
    """
        Represent all the tool knows about a single metadata element.
    """
    security = ClassSecurityInfo()

    #
    #   Default values.
    #
    is_multi_valued = 0

    def __init__(self, is_multi_valued):
        self.is_multi_valued = is_multi_valued
        self.policies = PersistentMapping()
        self.policies[None] = self._makePolicy()  # set default policy

    security.declarePrivate('_makePolicy')

    def _makePolicy(self):
        return MetadataElementPolicy(self.is_multi_valued)

    security.declareProtected(View, 'isMultiValued')

    def isMultiValued(self):
        """
            Is this element multi-valued?
        """
        return self.is_multi_valued

    security.declareProtected(View, 'getPolicy')

    def getPolicy(self, typ=None):
        """
            Find the policy this element for objects whose type
            object name is 'typ';  return a default, if none found.
        """
        try:
            return self.policies[typ].__of__(self)
        except KeyError:
            return self.policies[None]

    security.declareProtected(View, 'listPolicies')

    def listPolicies(self):
        """
            Return a list of all policies for this element.
        """
        res = []
        for k, v in self.policies.items():
            res.append((k, v.__of__(self)))
        return res

    security.declareProtected(ManagePortal, 'addPolicy')

    def addPolicy(self, typ):
        """
            Add a policy to this element for objects whose type
            object name is 'typ'.
        """
        if typ is None:
            raise MetadataError, "Can't replace default policy."

        if self.policies.has_key(typ):
            raise MetadataError, "Existing policy for content type:" + typ

        self.policies[typ] = self._makePolicy()

    security.declareProtected(ManagePortal, 'removePolicy')

    def removePolicy(self, typ):
        """
            Remove the policy from this element for objects whose type
            object name is 'typ' (*not* the default, however).
        """
        if typ is None:
            raise MetadataError, "Can't remove default policy."
        del self.policies[typ]
Ejemplo n.º 12
0
class DiscussionItemContainer( Persistent, Implicit, Traversable ):
    """
        Store DiscussionItem objects. Discussable content that
        has DiscussionItems associated with it will have an
        instance of DiscussionItemContainer injected into it to
        hold the discussion threads.
    """

    __implements__ = Discussable

    # for the security machinery to allow traversal
    #__roles__ = None

    security = ClassSecurityInfo()

    def __init__(self):
        self.id = 'talkback'
        self._container = PersistentMapping()

    security.declareProtected(View, 'getId')
    def getId( self ):
        return self.id

    security.declareProtected(View, 'getReply')
    def getReply( self, reply_id ):
        """
            Return a discussion item, given its ID;  raise KeyError
            if not found.
        """
        return self._container.get( reply_id ).__of__(self)

    # Is this right?
    security.declareProtected(View, '__bobo_traverse__')
    def __bobo_traverse__(self, REQUEST, name):
        """
        This will make this container traversable
        """
        target = getattr(self, name, None)
        if target is not None:
            return target

        else:
            try:
                return self.getReply(name)
            except:
                parent = aq_parent( aq_inner( self ) )
                if parent.getId() == name:
                    return parent
                else:
                    REQUEST.RESPONSE.notFoundError("%s\n%s" % (name, ''))

    security.declarePrivate('manage_afterAdd')
    def manage_afterAdd(self, item, container):
        """
            We have juste been added or moved.
            Add the contained items to the catalog.
        """
        if aq_base(container) is not aq_base(self):
            for obj in self.objectValues():
                obj.__of__(self).manage_afterAdd(item, container)

    security.declarePrivate('manage_afterClone')
    def manage_afterClone(self, item):
        """
            We have just been cloned.
            Notify the workflow about the contained items.
        """
        for obj in self.objectValues():
            obj.__of__(self).manage_afterClone(item)

    security.declarePrivate( 'manage_beforeDelete' )
    def manage_beforeDelete(self, item, container):
        """
            Remove the contained items from the catalog.
        """
        if aq_base(container) is not aq_base(self):
            for obj in self.objectValues():
                obj.__of__( self ).manage_beforeDelete( item, container )

    #
    #   OFS.ObjectManager query interface.
    #
    security.declareProtected(AccessContentsInformation, 'objectIds')
    def objectIds( self, spec=None ):
        """
            Return a list of the ids of our DiscussionItems.
        """
        if spec and spec is not DiscussionItem.meta_type:
            return []
        return self._container.keys()


    security.declareProtected(AccessContentsInformation, 'objectItems')
    def objectItems(self, spec=None):
        """
            Return a list of (id, subobject) tuples for our DiscussionItems.
        """
        r=[]
        a=r.append
        g=self._container.get
        for id in self.objectIds(spec):
            a( (id, g( id ) ) )
        return r


    security.declareProtected(AccessContentsInformation, 'objectValues')
    def objectValues(self):
        """
            Return a list of our DiscussionItems.
        """
        return self._container.values()

    #
    #   Discussable interface
    #
    security.declareProtected(ReplyToItem, 'createReply')
    def createReply( self, title, text, Creator=None ):
        """
            Create a reply in the proper place
        """
        container = self._container

        id = int(DateTime().timeTime())
        while self._container.get( str(id), None ) is not None:
            id = id + 1
        id = str( id )

        item = DiscussionItem( id, title=title, description=title )
        item._edit( text_format='structured-text', text=text )

        if Creator:
            item.creator = Creator

        item.__of__( self ).indexObject()

        item.setReplyTo( self._getDiscussable() )
 
        self._container[ id ] = item

        return id

    security.declareProtected(ManagePortal, 'deleteReply')
    def deleteReply( self, reply_id ):
        """ Remove a reply from this container """
        if self._container.has_key( reply_id ):
            reply = self._container.get( reply_id ).__of__( self )
            my_replies = reply.talkback.getReplies()
            for my_reply in my_replies:
                my_reply_id = my_reply.getId()
                if hasattr( my_reply, 'unindexObject' ):
                    my_reply.unindexObject()

                del self._container[my_reply_id]

            if hasattr( reply, 'unindexObject' ):
                reply.unindexObject()

            del self._container[reply_id]


    security.declareProtected(View, 'hasReplies')
    def hasReplies( self, content_obj ):
        """
            Test to see if there are any dicussion items
        """
        outer = self._getDiscussable( outer=1 )
        if content_obj == outer: 
            return not not len( self._container )
        else:
            return not not len( content_obj.talkback._getReplyResults() ) 

    security.declareProtected(View, 'replyCount')
    def replyCount( self, content_obj ):
        """ How many replies do i have? """
        outer = self._getDiscussable( outer=1 )
        if content_obj == outer:
            return len( self._container )
        else:
            replies = content_obj.talkback.getReplies()
            return self._repcount( replies )

    security.declarePrivate('_repcount')
    def _repcount( self, replies ):
        """  counts the total number of replies by recursing thru the various levels
        """
        count = 0

        for reply in replies:
            count = count + 1

            #if there is at least one reply to this reply
            replies = reply.talkback.getReplies()
            if replies:
                count = count + self._repcount( replies )

        return count

    security.declareProtected(View, 'getReplies')
    def getReplies( self ):
        """
            Return a sequence of the DiscussionResponse objects which are
            associated with this Discussable
        """
        objects = []
        a = objects.append
        result_ids = self._getReplyResults()

        for id in result_ids:
            a( self._container.get( id ).__of__( self ) )

        return objects

    security.declareProtected(View, 'quotedContents')
    def quotedContents(self):
        """
            Return this object's contents in a form suitable for inclusion
            as a quote in a response.
        """
 
        return ""
    
    #
    #   Utility methods
    #
    security.declarePrivate( '_getReplyParent' )
    def _getReplyParent( self, in_reply_to ):
        """
            Return the object indicated by the 'in_reply_to', where
            'None' represents the "outer" content object.
        """
        outer = self._getDiscussable( outer=1 )
        if in_reply_to is None:
            return outer
        parent = self._container[ in_reply_to ].__of__( aq_inner( self ) )
        return parent.__of__( outer )
        

    security.declarePrivate( '_getDiscussable' )
    def _getDiscussable( self, outer=0 ):
        """
        """
        tb = outer and aq_inner( self ) or self
        return getattr( tb, 'aq_parent', None )

    security.declarePrivate( '_getReplyResults' )
    def _getReplyResults( self ):
        """
           Get a list of ids of DiscussionItems which are replies to
           our Discussable.
        """
        discussable = self._getDiscussable()
        outer = self._getDiscussable( outer=1 )

        if discussable == outer:
            in_reply_to = None
        else:
            in_reply_to = discussable.getId()

        result = []
        a = result.append
        for key, value in self._container.items():
            if value.in_reply_to == in_reply_to:
                a( key )

        return result
Ejemplo n.º 13
0
class MetadataTool(UniqueObject, SimpleItem, ActionProviderBase):

    id = 'portal_metadata'
    meta_type = 'Default Metadata Tool'

    _actions = []

    security = ClassSecurityInfo()

    #
    #   Default values.
    #
    publisher = ''
    element_specs = None

    #initial_values_hook = None
    #validation_hook     = None

    def __init__(
            self,
            publisher=None
        #, initial_values_hook=None
        #, validation_hook=None
        ,
            element_specs=DEFAULT_ELEMENT_SPECS):

        self.editProperties(publisher
                            #, initial_values_hook
                            #, validation_hook
                            )

        self.element_specs = PersistentMapping()

        for name, is_multi_valued in element_specs:
            self.element_specs[name] = ElementSpec(is_multi_valued)

    #
    #   ZMI methods
    #
    manage_options = (
        ActionProviderBase.manage_options +
        ({
            'label': 'Overview',
            'action': 'manage_overview'
        }, {
            'label': 'Properties',
            'action': 'propertiesForm'
        }, {
            'label': 'Elements',
            'action': 'elementPoliciesForm'
        }
         # TODO     , { 'label'      : 'Types'
         #            , 'action'     : 'typesForm'
         #            }
         ) + SimpleItem.manage_options)

    security.declareProtected(CMFCorePermissions.ManagePortal,
                              'manage_overview')
    manage_overview = DTMLFile('explainMetadataTool', _dtmldir)

    security.declareProtected(CMFCorePermissions.ManagePortal,
                              'propertiesForm')
    propertiesForm = DTMLFile('metadataProperties', _dtmldir)

    security.declarePrivate('listActions')

    def listActions(self, info=None):
        """
        Return actions provided via tool.
        """
        return self._actions

    security.declareProtected(CMFCorePermissions.ManagePortal,
                              'editProperties')

    def editProperties(
            self,
            publisher=None
        # TODO , initial_values_hook=None
        # TODO , validation_hook=None
        ,
            REQUEST=None):
        """
            Form handler for "tool-wide" properties (including list of
            metadata elements).
        """
        if publisher is not None:
            self.publisher = publisher

        # TODO self.initial_values_hook = initial_values_hook
        # TODO self.validation_hook = validation_hook

        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url() +
                                         '/propertiesForm' +
                                         '?manage_tabs_message=Tool+updated.')

    security.declareProtected(CMFCorePermissions.ManagePortal,
                              'elementPoliciesForm')
    elementPoliciesForm = DTMLFile('metadataElementPolicies', _dtmldir)

    security.declareProtected(CMFCorePermissions.ManagePortal,
                              'addElementPolicy')

    def addElementPolicy(self,
                         element,
                         content_type,
                         is_required,
                         supply_default,
                         default_value,
                         enforce_vocabulary,
                         allowed_vocabulary,
                         REQUEST=None):
        """
            Add a type-specific policy for one of our elements.
        """
        if content_type == '<default>':
            content_type = None

        spec = self.getElementSpec(element)
        spec.addPolicy(content_type)
        policy = spec.getPolicy(content_type)
        policy.edit(is_required, supply_default, default_value,
                    enforce_vocabulary, allowed_vocabulary)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url() +
                                         '/elementPoliciesForm' + '?element=' +
                                         element +
                                         '&manage_tabs_message=Policy+added.')

    security.declareProtected(CMFCorePermissions.ManagePortal,
                              'removeElementPolicy')

    def removeElementPolicy(self, element, content_type, REQUEST=None):
        """
            Remvoe a type-specific policy for one of our elements.
        """
        if content_type == '<default>':
            content_type = None

        spec = self.getElementSpec(element)
        spec.removePolicy(content_type)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(
                self.absolute_url() + '/elementPoliciesForm' + '?element=' +
                element + '&manage_tabs_message=Policy+removed.')

    security.declareProtected(CMFCorePermissions.ManagePortal,
                              'updateElementPolicy')

    def updateElementPolicy(self,
                            element,
                            content_type,
                            is_required,
                            supply_default,
                            default_value,
                            enforce_vocabulary,
                            allowed_vocabulary,
                            REQUEST=None):
        """
            Update a policy for one of our elements ('content_type'
            will be '<default>' when we edit the default).
        """
        if content_type == '<default>':
            content_type = None
        spec = self.getElementSpec(element)
        policy = spec.getPolicy(content_type)
        policy.edit(is_required, supply_default, default_value,
                    enforce_vocabulary, allowed_vocabulary)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(
                self.absolute_url() + '/elementPoliciesForm' + '?element=' +
                element + '&manage_tabs_message=Policy+updated.')

    #
    #   Element spec manipulation.
    #
    security.declareProtected(CMFCorePermissions.ManagePortal,
                              'listElementSpecs')

    def listElementSpecs(self):
        """
            Return a list of ElementSpecs representing
            the elements managed by the tool.
        """
        return tuple(self.element_specs.items())

    security.declareProtected(CMFCorePermissions.ManagePortal,
                              'getElementSpec')

    def getElementSpec(self, element):
        """
            Return an ElementSpec representing the tool's knowledge
            of 'element'.
        """
        return self.element_specs[element]

    security.declareProtected(CMFCorePermissions.ManagePortal,
                              'addElementSpec')

    def addElementSpec(self, element, is_multi_valued, REQUEST=None):
        """
            Add 'element' to our list of managed elements.
        """
        # Don't replace.
        if self.element_specs.has_key(element):
            return

        self.element_specs[element] = ElementSpec(is_multi_valued)

        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url() +
                                         '/propertiesForm' +
                                         '?manage_tabs_message=Element+' +
                                         element + '+added.')

    security.declareProtected(CMFCorePermissions.ManagePortal,
                              'removeElementSpec')

    def removeElementSpec(self, element, REQUEST=None):
        """
            Remove 'element' from our list of managed elements.
        """
        del self.element_specs[element]

        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url() +
                                         '/propertiesForm' +
                                         '?manage_tabs_message=Element+' +
                                         element + '+removed.')

    security.declareProtected(CMFCorePermissions.ManagePortal, 'listPolicies')

    def listPolicies(self, typ=None):
        """
            Show all policies for a given content type, or the default
            if None.
        """
        result = []
        for element, spec in self.listElementSpecs():
            result.append((element, spec.getPolicy(typ)))
        return result

    #
    #   'portal_metadata' interface
    #
    security.declarePrivate('getFullName')

    def getFullName(self, userid):
        """
            Convert an internal userid to a "formal" name, if
            possible, perhaps using the 'portal_membership' tool.

            Used to map userid's for Creator, Contributor DCMI
            queries.
        """
        return userid  # TODO: do lookup here

    security.declarePublic('getPublisher')

    def getPublisher(self):
        """
            Return the "formal" name of the publisher of the
            portal.
        """
        return self.publisher

    security.declarePublic('listAllowedVocabulary')

    def listAllowedVocabulary(self, element, content=None):
        """
            List allowed keywords for a given meta_type, or all
            possible keywords if none supplied.
        """
        spec = self.getElementSpec(element)
        typ = content and content.Type() or None
        return spec.getPolicy(typ).allowedVocabulary()

    security.declarePublic('listAllowedSubjects')

    def listAllowedSubjects(self, content=None):
        """
            List allowed keywords for a given meta_type, or all
            possible keywords if none supplied.
        """
        return self.listAllowedVocabulary('Subject', content)

    security.declarePublic('listAllowedFormats')

    def listAllowedFormats(self, content=None):
        """
            List the allowed 'Content-type' values for a particular
            meta_type, or all possible formats if none supplied.
        """
        return self.listAllowedVocabulary('Format', content)

    security.declarePublic('listAllowedLanguages')

    def listAllowedLanguages(self, content=None):
        """
            List the allowed language values.
        """
        return self.listAllowedVocabulary('Language', content)

    security.declarePublic('listAllowedRights')

    def listAllowedRights(self, content=None):
        """
            List the allowed values for a "Rights"
            selection list;  this gets especially important where
            syndication is involved.
        """
        return self.listAllowedVocabulary('Rights', content)

    security.declareProtected(CMFCorePermissions.ModifyPortalContent,
                              'setInitialMetadata')

    def setInitialMetadata(self, content):
        """
            Set initial values for content metatdata, supplying
            any site-specific defaults.
        """
        for element, policy in self.listPolicies(content.Type()):

            if not getattr(content, element)():

                if policy.supplyDefault():
                    setter = getattr(content, 'set%s' % element)
                    setter(policy.defaultValue())
                elif policy.isRequired():
                    raise MetadataError, \
                          'Metadata element %s is required.' % element

        # TODO:  Call initial_values_hook, if present

    security.declareProtected(CMFCorePermissions.View, 'validateMetadata')

    def validateMetadata(self, content):
        """
            Enforce portal-wide policies about DCI, e.g.,
            requiring non-empty title/description, etc.  Called
            by the CMF immediately before saving changes to the
            metadata of an object.
        """
        for element, policy in self.listPolicies(content.Type()):

            value = getattr(content, element)()
            if not value and policy.isRequired():
                raise MetadataError, \
                        'Metadata element %s is required.' % element

            if policy.enforceVocabulary():
                values = policy.isMultiValued() and value or [value]
                for value in values:
                    if not value in policy.allowedVocabulary():
                        raise MetadataError, \
                        'Value %s is not in allowed vocabulary for' \
                        'metadata element %s.' % ( value, element )
Ejemplo n.º 14
0
class MimeTypesRegistry(UniqueObject, ActionProviderBase, Folder):
    """Mimetype registry that deals with
    a) registering types
    b) wildcarding of rfc-2046 types
    c) classifying data into a given type
    """

    __implements__ = (IMimetypesRegistry, ISourceAdapter)
    implements(IMimetypesRegistryTool)

    id        = 'mimetypes_registry'
    meta_type = 'MimeTypes Registry'
    isPrincipiaFolderish = 1 # Show up in the ZMI

    meta_types = all_meta_types = (
        { 'name'   : 'MimeType',
          'action' : 'manage_addMimeTypeForm'},
        )

    manage_options = (
        ( { 'label'   : 'MimeTypes',
            'action' : 'manage_main'},) +
        Folder.manage_options[2:]
        )

    manage_addMimeTypeForm = PageTemplateFile('addMimeType', _www)
    manage_main = PageTemplateFile('listMimeTypes', _www)
    manage_editMimeTypeForm = PageTemplateFile('editMimeType', _www)

    security = ClassSecurityInfo()

    # FIXME
    __allow_access_to_unprotected_subobjects__ = 1

    def __init__(self,):
        self.encodings_map = encodings_map.copy()
        self.suffix_map = suffix_map.copy()
        # Major key -> minor IMimetype objects
        self._mimetypes  = PersistentMapping()
        # ext -> IMimetype mapping
        self.extensions = PersistentMapping()
        # glob -> (regex, mimetype) mapping
        self.globs = OOBTree()
        self.manage_addProperty('defaultMimetype', 'text/plain', 'string')
        self.manage_addProperty('unicodePolicies', 'strict ignore replace',
                                'tokens')
        self.manage_addProperty('unicodePolicy', 'unicodePolicies', 'selection')
        self.manage_addProperty('fallbackEncoding', 'latin1', 'string')

        # initialize mime types
        initialize(self)
        self._new_style_mtr = 1

    security.declareProtected(ManagePortal, 'register')
    def register(self, mimetype):
        """ Register a new mimetype

        mimetype must implement IMimetype
        """
        mimetype = aq_base(mimetype)
        assert IMimetype.isImplementedBy(mimetype)
        for t in mimetype.mimetypes:
            self.register_mimetype(t, mimetype)
        for extension in mimetype.extensions:
            self.register_extension(extension, mimetype)
        for glob in mimetype.globs:
            self.register_glob(glob, mimetype)

    security.declareProtected(ManagePortal, 'register_mimetype')
    def register_mimetype(self, mt, mimetype):
        major, minor = split(mt)
        if not major or not minor or minor == '*':
            raise MimeTypeException('Can\'t register mime type %s' % mt)
        group = self._mimetypes.setdefault(major, PersistentMapping())
        if group.has_key(minor):
            if group.get(minor) != mimetype:
                log('Warning: redefining mime type %s (%s)' % (
                    mt, mimetype.__class__))
        group[minor] = mimetype

    security.declareProtected(ManagePortal, 'register_extension')
    def register_extension(self, extension, mimetype):
        """ Associate a file's extension to a IMimetype

        extension is a string representing a file extension (not
        prefixed by a dot) mimetype must implement IMimetype
        """
        mimetype = aq_base(mimetype)
        if self.extensions.has_key(extension):
            if self.extensions.get(extension) != mimetype:
                log('Warning: redefining extension %s from %s to %s' % (
                    extension, self.extensions[extension], mimetype))
        # we don't validate fmt yet, but its ["txt", "html"]
        self.extensions[extension] = mimetype

    security.declareProtected(ManagePortal, 'register_glob')
    def register_glob(self, glob, mimetype):
        """ Associate a glob to a IMimetype

        glob is a shell-like glob that will be translated to a regex
        to match against whole filename.
        mimetype must implement IMimetype.
        """
        globs = getattr(self, 'globs', None)
        if globs is None:
            self.globs = globs = OOBTree()
        mimetype = aq_base(mimetype)
        existing = globs.get(glob)
        if existing is not None:
            regex, mt = existing
            if mt != mimetype:
                log('Warning: redefining glob %s from %s to %s' % (
                    glob, mt, mimetype))
        # we don't validate fmt yet, but its ["txt", "html"]
        pattern = re.compile(fnmatch.translate(glob))
        globs[glob] = (pattern, mimetype)

    security.declareProtected(ManagePortal, 'unregister')
    def unregister(self, mimetype):
        """ Unregister a new mimetype

        mimetype must implement IMimetype
        """
        assert IMimetype.isImplementedBy(mimetype)
        for t in mimetype.mimetypes:
            major, minor = split(t)
            group = self._mimetypes.get(major, {})
            if group.get(minor) == mimetype:
                del group[minor]
        for e in mimetype.extensions:
            if self.extensions.get(e) == mimetype:
                del self.extensions[e]
        globs = getattr(self, 'globs', None)
        if globs is not None:
            for glob in mimetype.globs:
                existing = globs.get(glob)
                if existing is None:
                    continue
                regex, mt = existing
                if mt == mimetype:
                    del globs[glob]

    security.declarePublic('mimetypes')
    def mimetypes(self):
        """Return all defined mime types, each one implements at least
        IMimetype
        """
        res = {}
        for g in self._mimetypes.values():
            for mt in g.values():
                res[mt] =1
        return [aq_base(mtitem) for mtitem in res.keys()]


    security.declarePublic('list_mimetypes')
    def list_mimetypes(self):
        """Return all defined mime types, as string"""
        return [str(mt) for mt in self.mimetypes()]

    security.declarePublic('lookup')
    def lookup(self, mimetypestring):
        """Lookup for IMimetypes object matching mimetypestring

        mimetypestring may have an empty minor part or containing a
        wildcard (*) mimetypestring may and IMimetype object (in this
        case it will be returned unchanged

        Return a list of mimetypes objects associated with the
        RFC-2046 name return an empty list if no one is known.
        """
        if IMimetype.isImplementedBy(mimetypestring):
            return (aq_base(mimetypestring), )
        __traceback_info__ = (repr(mimetypestring), str(mimetypestring))
        major, minor = split(str(mimetypestring))
        group = self._mimetypes.get(major, {})
        if not minor or minor == '*':
            res = group.values()
        else:
            res = group.get(minor)
            if res:
                res = (res,)
            else:
                return ()
        return tuple([aq_base(mtitem) for mtitem in res])

    security.declarePublic('lookupExtension')
    def lookupExtension(self, filename):
        """Lookup for IMimetypes object matching filename

        Filename maybe a file name like 'content.txt' or an extension
        like 'rest'

        Return an IMimetype object associated with the file's
        extension or None
        """
        if filename.find('.') != -1:
            base, ext = os.path.splitext(filename)
            ext = ext[1:] # remove the dot
            while self.suffix_map.has_key(ext):
                base, ext = os.path.splitext(base + self.suffix_map[ext])
                ext = ext[1:] # remove the dot
        else:
            ext = filename
            base = None

        # XXX This code below make no sense and may break because base
        # isn't defined.
        if self.encodings_map.has_key(ext) and base:
            encoding = self.encodings_map[ext]
            base, ext = os.path.splitext(base)
            ext = ext[1:] # remove the dot
        else:
            encoding = None
        return aq_base(self.extensions.get(ext))

    security.declarePublic('globFilename')
    def globFilename(self, filename):
        """Lookup for IMimetypes object matching filename

        Filename must be a complete filename with extension.

        Return an IMimetype object associated with the glob's or None
        """
        globs = getattr(self, 'globs', None)
        if globs is None:
            return None
        for key in globs.keys():
            glob, mimetype = globs[key]
            if glob.match(filename):
                return aq_base(mimetype)
        return None

    security.declarePublic('lookupGlob')
    def lookupGlob(self, glob):
        globs = getattr(self, 'globs', None)
        if globs is None:
            return None
        return aq_base(globs.get(glob))

    def _classifiers(self):
        return [mt for mt in self.mimetypes() if IClassifier.isImplementedBy(mt)]

    security.declarePublic('classify')
    def classify(self, data, mimetype=None, filename=None):
        """Classify works as follows:
        1) you tell me the rfc-2046 name and I give you an IMimetype
           object
        2) the filename includes an extension from which we can guess
           the mimetype
        3) we can optionally introspect the data
        4) default to self.defaultMimetype if no data was provided
           else to application/octet-stream of no filename was provided,
           else to text/plain

        Return an IMimetype object or None 
        """
        mt = None
        if mimetype:
            mt = self.lookup(mimetype)
            if mt:
                mt = mt[0]
        elif filename:
            mt = self.lookupExtension(filename)
            if mt is None:
                mt = self.globFilename(filename)
        if data and not mt:
            for c in self._classifiers():
                if c.classify(data):
                    mt = c
                    break
            if not mt:
                mstr = magic.guessMime(data)
                if mstr:
                    mt = self.lookup(mstr)[0]
        if not mt:
            if not data:
                mtlist = self.lookup(self.defaultMimetype)
            elif filename:
                mtlist = self.lookup('application/octet-stream')
            else:
                failed = 'text/x-unknown-content-type'
                filename = filename or ''
                data = data or ''
                ct, enc = guess_content_type(filename, data, None)
                if ct == failed:
                    ct = 'text/plain'
                mtlist = self.lookup(ct)
            if len(mtlist)>0:
                mt = mtlist[0]
            else:
                return None

        # Remove acquisition wrappers
        return aq_base(mt)

    def __call__(self, data, **kwargs):
        """ Return a triple (data, filename, mimetypeobject) given
        some raw data and optional paramters

        method from the isourceAdapter interface
        """
        mimetype = kwargs.get('mimetype', None)
        filename = kwargs.get('filename', None)
        encoding = kwargs.get('encoding', None)
        mt = None
        if hasattr(data, 'filename'):
            filename = os.path.basename(data.filename)
        elif hasattr(data, 'name'):
            filename = os.path.basename(data.name)

        if hasattr(data, 'read'):
            _data = data.read()
            if hasattr(data, 'seek'):
                data.seek(0)
            data = _data

        # We need to figure out if data is binary and skip encoding if
        # it is
        mt = self.classify(data, mimetype=mimetype, filename=filename)

        if not mt.binary and not type(data) is UnicodeType:
            # if no encoding specified, try to guess it from data
            if encoding is None:
                encoding = self.guess_encoding(data)

            # ugly workaround for
            # https://sourceforge.net/tracker/?func=detail&aid=1068001&group_id=75272&atid=543430
            # covered by
            # https://sourceforge.net/tracker/?func=detail&atid=355470&aid=843590&group_id=5470
            # dont remove this code unless python is fixed.
            if encoding is "macintosh":
                encoding = 'mac_roman'

            try:
                try:
                    data = unicode(data, encoding, self.unicodePolicy)
                except (ValueError, LookupError):
                    # wrong unicodePolicy
                    data = unicode(data, encoding)
            except:
                data = unicode(data, self.fallbackEncoding)

        return (data, filename, aq_base(mt))

    security.declarePublic('guess_encoding')
    def guess_encoding(self, data):
        """ Try to guess encoding from a text value if no encoding
        guessed, used the default charset from site properties (Zope)
        with a fallback to UTF-8 (should never happen with correct
        site_properties, but always raise Attribute error without
        Zope)
        """
        if type(data) is type(u''):
            # data maybe unicode but with another encoding specified
            data = data.encode('UTF-8')
        encoding = guess_encoding(data)
        if encoding is None:
            try:
                site_props = self.portal_properties.site_properties
                encoding = site_props.getProperty('default_charset', 'UTF-8')
            except:
                encoding = 'UTF-8'
        return encoding

    security.declareProtected(ManagePortal, 'manage_delObjects')
    def manage_delObjects(self, ids, REQUEST=None):
        """ delete the selected mime types """
        for id in ids:
            self.unregister(self.lookup(id)[0])
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main')

    security.declareProtected(ManagePortal, 'manage_addMimeType')
    def manage_addMimeType(self, id, mimetypes, extensions, icon_path,
                           binary=0, globs=None, REQUEST=None):
        """add a mime type to the tool"""
        mt = MimeTypeItem(id, mimetypes, extensions=extensions,
                          binary=binary, icon_path=icon_path, globs=globs)
        self.register(mt)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main')

    security.declareProtected(ManagePortal, 'manage_editMimeType')
    def manage_editMimeType(self, name, new_name, mimetypes, extensions,
                            icon_path, binary=0, globs=None, REQUEST=None):
        """Edit a mime type by name
        """
        mt = self.lookup(name)[0]
        self.unregister(mt)
        mt.edit(new_name, mimetypes, extensions, icon_path=icon_path,
                binary=binary, globs=globs)
        self.register(mt)
        if REQUEST is not None:
            REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main')
Ejemplo n.º 15
0
class WorklistDefinition(SimpleItem):
    """Worklist definiton"""

    meta_type = 'Worklist'

    security = ClassSecurityInfo()
    security.declareObjectProtected(ManagePortal)

    description = ''
    var_matches = None  # Compared with catalog when set.
    actbox_name = ''
    actbox_url = ''
    actbox_category = 'global'
    guard = None

    manage_options = ({'label': 'Properties', 'action': 'manage_properties'}, )

    def __init__(self, id):
        self.id = id

    def getGuard(self):
        if self.guard is not None:
            return self.guard
        else:
            return Guard().__of__(self)  # Create a temporary guard.

    def getGuardSummary(self):
        res = None
        if self.guard is not None:
            res = self.guard.getSummary()
        return res

    def getWorkflow(self):
        return aq_parent(aq_inner(aq_parent(aq_inner(self))))

    def getAvailableCatalogVars(self):
        res = []
        res.append(self.getWorkflow().state_var)
        for id, vdef in self.getWorkflow().variables.items():
            if vdef.for_catalog:
                res.append(id)
        res.sort()
        return res

    def getVarMatchKeys(self):
        if self.var_matches:
            return self.var_matches.keys()
        else:
            return []

    def getVarMatch(self, id):
        if self.var_matches:
            matches = self.var_matches.get(id, ())
            if not isinstance(matches, TupleType):
                # Old version, convert it.
                matches = (matches, )
                self.var_matches[id] = matches
            return matches
        else:
            return ()

    def getVarMatchText(self, id):
        values = self.getVarMatch(id)
        return '; '.join(values)

    _properties_form = DTMLFile('worklist_properties', _dtmldir)

    def manage_properties(self, REQUEST, manage_tabs_message=None):
        '''
        '''
        return self._properties_form(
            REQUEST,
            management_view='Properties',
            manage_tabs_message=manage_tabs_message,
        )

    def setProperties(self,
                      description,
                      actbox_name='',
                      actbox_url='',
                      actbox_category='global',
                      props=None,
                      REQUEST=None):
        '''
        '''
        if props is None:
            props = REQUEST
        self.description = str(description)
        for key in self.getAvailableCatalogVars():
            # Populate var_matches.
            fieldname = 'var_match_%s' % key
            v = props.get(fieldname, '')
            if v:
                if not self.var_matches:
                    self.var_matches = PersistentMapping()
                v = [var.strip() for var in v.split(';')]
                self.var_matches[key] = tuple(v)
            else:
                if self.var_matches and self.var_matches.has_key(key):
                    del self.var_matches[key]
        self.actbox_name = str(actbox_name)
        self.actbox_url = str(actbox_url)
        self.actbox_category = str(actbox_category)
        g = Guard()
        if g.changeFromProperties(props or REQUEST):
            self.guard = g
        else:
            self.guard = None
        if REQUEST is not None:
            return self.manage_properties(REQUEST, 'Properties changed.')