Esempio n. 1
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
    """

    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, id=None):
        if id is not None:
            assert id == self.id
        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.providedBy(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 minor in group:
            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 extension in self.extensions:
            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.providedBy(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.providedBy(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 ext in self.suffix_map:
                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 ext in self.encodings_map and base:
            base, ext = os.path.splitext(base)
            ext = ext[1:]  # remove the dot

        result = aq_base(self.extensions.get(ext))
        if result is None:
            result = aq_base(self.extensions.get(ext.lower()))
        return result

    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.providedBy(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)
                    if len(_mt) > 0:
                        mt = _mt[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 isinstance(data, 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 can be guessed, fall back to utf-8.
        """
        if isinstance(data, type(u'')):
            # data maybe unicode but with another encoding specified
            data = data.encode('UTF-8')
        encoding = guess_encoding(data)
        if encoding is None:
            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')
Esempio n. 2
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(IDiscussable, ICallableOpaqueItemEvents)

    # 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).indexObject()

    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).notifyWorkflowCreated()

    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).unindexObject()

    #
    #   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()

    #
    #   IDiscussable interface
    #
    security.declareProtected(ReplyToItem, 'createReply')

    def createReply(self,
                    title,
                    text,
                    Creator=None,
                    text_format='structured-text'):
        """
            Create a reply in the proper place
        """
        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)
        self._container[id] = item
        item = item.__of__(self)

        item.setFormat(text_format)
        item._edit(text)
        item.addCreator(Creator)
        item.setReplyTo(self._getDiscussable())

        item.indexObject()
        item.notifyWorkflowCreated()

        return id

    security.declareProtected(ManagePortal, 'deleteReply')

    def deleteReply(self, reply_id):
        """ Remove a reply from this container """
        if reply_id in self._container:
            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()
                self.deleteReply(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 bool(len(self._container))
        else:
            return bool(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 IDiscussionResponse 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]
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
    """

    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, id=None):
        if id is not None:
            assert id == self.id
        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.protected(ManagePortal)
    def register(self, mimetype):
        """ Register a new mimetype

        mimetype must implement IMimetype
        """
        mimetype = aq_base(mimetype)
        assert IMimetype.providedBy(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.protected(ManagePortal)
    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 minor in group:
            if group.get(minor) != mimetype:
                logger.warning(
                    'Redefining mime type {0} ({1})'.format(
                        mt,
                        mimetype.__class__
                    )
                )
        group[minor] = mimetype

    @security.protected(ManagePortal)
    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 extension in self.extensions:
            if self.extensions.get(extension) != mimetype:
                logger.warning(
                    'Redefining extension {0} from {1} to {2}'.format(
                        extension,
                        self.extensions[extension],
                        mimetype
                    )
                )
        # we don't validate fmt yet, but its ["txt", "html"]
        self.extensions[extension] = mimetype

    @security.protected(ManagePortal)
    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:
                logger.warning(
                    'Redefining glob {0} from {1} to {2}'.format(
                        glob,
                        mt,
                        mimetype
                    )
                )
        # we don't validate fmt yet, but its ["txt", "html"]
        pattern = re.compile(fnmatch.translate(glob))
        globs[glob] = (pattern, mimetype)

    @security.protected(ManagePortal)
    def unregister(self, mimetype):
        """ Unregister a new mimetype

        mimetype must implement IMimetype
        """
        assert IMimetype.providedBy(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.public
    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.public
    def list_mimetypes(self):
        """Return all defined mime types, as string"""
        return [str(mt) for mt in self.mimetypes()]

    @security.public
    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.providedBy(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.public
    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
        """
        base = None
        if filename.find('.') != -1:
            base, ext = os.path.splitext(filename)
            ext = ext[1:]  # remove the dot
            while ext in self.suffix_map:
                base, ext = os.path.splitext(base + self.suffix_map[ext])
                ext = ext[1:]  # remove the dot
        else:
            ext = filename

        if base is not None and ext in self.encodings_map:
            base, ext = os.path.splitext(base)
            ext = ext[1:]  # remove the dot

        result = aq_base(self.extensions.get(ext))
        if result is None:
            result = aq_base(self.extensions.get(ext.lower()))
        return result

    @security.public
    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', {})
        for key in globs:
            glob, mimetype = globs[key]
            if glob.match(filename):
                return aq_base(mimetype)
        return None

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

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

    @security.public
    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)
                    if len(_mt) > 0:
                        mt = _mt[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 ''
                if six.PY3:
                    data = data.encode()
                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 isinstance(data, six.text_type):
            # 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 = six.text_type(data, encoding, self.unicodePolicy)
                except (ValueError, LookupError):
                    # wrong unicodePolicy
                    data = six.text_type(data, encoding)
            except:
                data = six.text_type(data, self.fallbackEncoding)

        return (data, filename, aq_base(mt))

    @security.public
    def guess_encoding(self, data):
        """ Try to guess encoding from a text value.

        If no encoding can be guessed, fall back to utf-8.
        """
        if isinstance(data, six.text_type):
            # data maybe unicode but with another encoding specified
            data = data.encode('UTF-8')
        encoding = guess_encoding(data)
        if encoding is None:
            encoding = 'utf-8'
        return encoding

    @security.protected(ManagePortal)
    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.protected(ManagePortal)
    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.protected(ManagePortal)
    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')
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(IDiscussable, ICallableOpaqueItemEvents)

    # 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).indexObject()

    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).notifyWorkflowCreated()

    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).unindexObject()

    #
    #   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()

    #
    #   IDiscussable interface
    #
    security.declareProtected(ReplyToItem, 'createReply')
    def createReply(self, title, text, Creator=None,
                    text_format='structured-text'):
        """
            Create a reply in the proper place
        """
        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)
        self._container[id] = item
        item = item.__of__(self)

        item.setFormat(text_format)
        item._edit(text)
        item.addCreator(Creator)
        item.setReplyTo(self._getDiscussable())

        item.indexObject()
        item.notifyWorkflowCreated()

        return id

    security.declareProtected(ManagePortal, 'deleteReply')
    def deleteReply(self, reply_id):
        """ Remove a reply from this container """
        if reply_id in self._container:
            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()
                self.deleteReply(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 bool(len(self._container))
        else:
            return bool(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 IDiscussionResponse 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 not in_reply_to:
            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 = ''
        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 ]
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)

    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, id=None):
        if id is not None:
            assert id == self.id
        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.providedBy(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 minor in group:
            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 extension in self.extensions:
            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.providedBy(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.providedBy(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 ext in self.suffix_map:
                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 ext in self.encodings_map and base:
            base, ext = os.path.splitext(base)
            ext = ext[1:]  # remove the dot

        result = aq_base(self.extensions.get(ext))
        if result is None:
            result = aq_base(self.extensions.get(ext.lower()))
        return result

    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.providedBy(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)
                    if len(_mt) > 0:
                        mt = _mt[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 isinstance(data, 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 isinstance(data, type(u"")):
            # data maybe unicode but with another encoding specified
            data = data.encode("UTF-8")
        encoding = guess_encoding(data)
        if encoding is None:
            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")