Commit 9ce9f55f authored by Ivan Tyagov's avatar Ivan Tyagov
Browse files

Initial import of 'MimetypesRegistry' and 'PortalTransforms' products.

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@16694 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 896efdee
DONT USE ChangeLog use HISTORY.txt instead.
2004-07-24 Christian Heimes <heimes@faho.rwth-aachen.de>
* Changed version to stick to Archetypes version.
import os
from Products.CMFCore.DirectoryView import addDirectoryViews, registerDirectory, \
createDirectoryView, manage_listAvailableDirectories
from Products.CMFCore.utils import getToolByName, minimalpath
from Globals import package_home
from OFS.ObjectManager import BadRequestException
from Products.MimetypesRegistry import GLOBALS, skins_dir
from Products.MimetypesRegistry.interfaces import IMimetypesRegistry
from Acquisition import aq_base
from StringIO import StringIO
def install(self):
out = StringIO()
id = 'mimetypes_registry'
if hasattr(aq_base(self), id):
mtr = getattr(self, id)
if not IMimetypesRegistry.isImplementedBy(mtr) or \
not getattr(aq_base(mtr), '_new_style_mtr', None) == 1:
print >>out, 'Removing old mimetypes registry tool'
self.manage_delObjects([id,])
if not hasattr(self, id):
addTool = self.manage_addProduct['MimetypesRegistry'].manage_addTool
addTool('MimeTypes Registry')
print >>out, 'Installing mimetypes registry tool'
skinstool=getToolByName(self, 'portal_skins')
fullProductSkinsPath = os.path.join(package_home(GLOBALS), skins_dir)
productSkinsPath = minimalpath(fullProductSkinsPath)
registered_directories = manage_listAvailableDirectories()
if productSkinsPath not in registered_directories:
registerDirectory(skins_dir, GLOBALS)
try:
addDirectoryViews(skinstool, skins_dir, GLOBALS)
except BadRequestException, e:
pass # directory view has already been added
files = os.listdir(fullProductSkinsPath)
for productSkinName in files:
if os.path.isdir(os.path.join(fullProductSkinsPath, productSkinName)) \
and productSkinName != 'CVS':
for skinName in skinstool.getSkinSelections():
path = skinstool.getSkinPath(skinName)
path = [i.strip() for i in path.split(',')]
try:
if productSkinName not in path:
path.insert(path.index('custom') +1, productSkinName)
except ValueError:
if productSkinName not in path:
path.append(productSkinName)
path = ','.join(path)
skinstool.addSkinSelection(skinName, path)
return out.getvalue()
def fixUpSMIGlobs(self):
from Products.MimetypesRegistry.mime_types import smi_mimetypes
from Products.Archetypes.debug import log
mtr = getToolByName(self, 'mimetypes_registry')
smi_mimetypes.initialize(mtr)
# Now comes the fun part. For every glob, lookup a extension
# matching the glob and unregister it.
for glob in mtr.globs.keys():
if mtr.extensions.has_key(glob):
log('Found glob %s in extensions registry, removing.' % glob)
mti = mtr.extensions[glob]
del mtr.extensions[glob]
if glob in mti.extensions:
log('Found glob %s in mimetype %s extensions, '
'removing.' % (glob, mti))
exts = list(mti.extensions)
exts.remove(glob)
mti.extensions = tuple(exts)
mtr.register(mti)
#poof, you're a product
1.4.0-final - 2006-06-16
========================
* Use zope.contenttype in favor of zope.app.content_types if available.
[hannosch]
1.4.0-beta2 - 2006-05-12
========================
* Use zope.app.content_types in favor of OFS.content_types if available.
[stefan]
* Spring-cleaning of tests infrastructure.
[hannosch]
1.4.0-beta1 - 2006-03-26
========================
* fixed Plone #5027: MimeTypeRegistry.classify doesn't handle
"no mimetype" gracefully. Returns 'None' now.
[jensens]
* fixed http://dev.plone.org/archetypes/ticket/622
[jensens]
1.4.0-alpha02 - 2006-02-23
==========================
* ensured that the key gotten back from windows_mimetypes.py existed
mark says the best way is to examine each key to ensure its valid but
would be slower.
[runyaga]
* removed odd archetypes 1.3 style version checking
[jensens]
* Removed BBB code for CMFCorePermissions import location.
[hannosch]
* removed deprecation warning for ToolInit.
[jensens]
* skip backward compatibility to the times where MTR where part of
PortalTransforms.
[jensens]
1.3.8-final02 - 2006-01-15
==========================
* nothing - the odd version checking needs a version change to stick to
Archetypes version again.
[yenzenz]
1.3.8-RC1 - 2005-12-29
======================
* Split yet another part of register() into a separate
method. Cleanup smi_mimetypes initialize a little bit to to use
the new method when adding new mimetypes to a already-registered
entry.
[dreamcatcher]
* Include aliases in the list of mimetypes for a entry. Based on
patch by Jean Jordaan
[dreamcatcher]
* Use a SAX-based parser instead of minidom to improve Zope startup
time (by 17 seconds on my Pismo) and memory footprint.
[dreamcatcher]
* Augment known mimetypes with Windows mimetypes, if available.
[dreamcatcher]
1.3.7-final01 - 2005-10-11
==========================
* For the sake of sanity, include a 'mime.types' with
MimetypesRegistry to minimize the platform-specific differences in
mime detection when the python 'mimetypes' module is involved.
[dreamcatcher]
* globs from freedesktop.org shared-mime-info were incorrectly
mapped to 'extensions' and never really worked because the code
tried to strip a leading dot, where the globs normally had '*.'.
The side-effect of this is that in *nix, the Python 'mimetypes'
module would happily read '/etc/mime.types' and gracefully work
(/etc/mime.types has most of the extensions of shared-mime-info
but a few), where on Windows it would fail to detect mimetypes by
extension.
[dreamcatcher]
* Added support for real globs, using fnmatch.translate and
re.compile and a migration function that will be run from Plone
2.1.1 migration, with some tests specific for globs read from
shared-mime-info.
[dreamcatcher]
1.3.6-final01 - 2005-08-30
==========================
* after one night sleeping over it I removed the yesterday added method.
therefore I added according to some heuristics and OOo-Documentation
some magic bytes to magic.py and made better tests.
[yenzenz]
* added a method to detect mimetypes of zipped files,
here specialy for OOo now all Openofice files and zip
files are detected properly. my simple tests are working:
a OOo-Writer and a simpe zipfile are detected.
[yenzenz]
* updated freedesktop.org.xml file to latest CVS version rev 1.57 from
http://cvs.freedesktop.org/mime/shared-mime-info/freedesktop.org.xml
[yenzenz]
1.3.5-final03 - 2005-08-07
==========================
* nothing - the odd version checking needs a version change to stick to
Archetypes version again.
[yenzenz]
1.3.5-final02 - 2005-08-01
==========================
* nothing again, need to stick to Archetypes version
[yenzenz]
1.3.5-final - 2005-07-17
========================
* Added Five/Zope3 interface bridges and implements
[tiran]
1.3.4-final - 2005-07-06
========================
* added icons for openoffice.org files
[yenzenz]
1.3.3-final06 - 2005-05-20
==========================
* nothing (I hate to write this. But the odd version checking needs it).
[yenzenz]
1.3.3-final-02 - 2005-03-25
===========================
* nothing
1.3.3-final - 2005-03-05
========================
* More a workaround than a fix for [ 1056252 ] Content type algorithm
can be confused.
[tiran]
* workaround for [ 1068001 ] BaseUnit Encoding Error: macintosh
[yenzenz]
* In the case all else fails, try to resort to guess_content_type so
that at least we don't get 'text/plain' when the file is in fact a
binary file.
[dreamcatcher]
1.3.2-5 - 2004-09-30
====================
* nothing
1.3.2-4 - 2004-09-30
====================
* nothing
1.3.2-3 - 2004-09-25
====================
* nothing
1.3.2-2 - 2004-09-17
====================
* nothing
1.3.2-1 - 2004-09-04
====================
* Cleaned up major parts of PT by removing the python only implementation which
was broken anyway
[tiran]
1.3.1-1 - 2004-08-16
====================
* Added text/x-html-safe mime type for new transformation
[tiran]
* Don't return acquisition wrapped mimetype items beause they may lead to
memory leaks.
[tiran]
1.3.0-3 - 2004-08-06
====================
* Added text/wiki mime type
[tiran]
* Don't log redefine warning if the currrent and the new object are equal
[tiran]
* initialize() MTR on __setstate__ aka when the MTR is loaded from ZODB.
[tiran]
1.3.0-2 - 2004-07-29
====================
* Nothing changed
Copyright (c) 2002-2003, Benjamin Saller <bcsaller@ideasuite.com>, and
the respective authors.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Archetypes nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import os
from Acquisition import Explicit
from OFS.SimpleItem import Item
from AccessControl import ClassSecurityInfo
from Globals import Persistent, InitializeClass
from Products.CMFCore.permissions import ManagePortal
from Products.MimetypesRegistry.interfaces import IMimetype
from Products.MimetypesRegistry.common import MimeTypeException
class MimeTypeItem(Persistent, Explicit, Item):
security = ClassSecurityInfo()
__implements__ = (IMimetype,)
extensions = ()
globs = ()
def __init__(self, name='', mimetypes=None, extensions=None,
binary=None, icon_path='', globs=None):
if name:
self.__name__ = self.id = name
if mimetypes is not None:
self.mimetypes = mimetypes
if extensions is not None:
self.extensions = extensions
if binary is not None:
self.binary = binary
if globs is not None:
self.globs = globs
self.icon_path = icon_path or guess_icon_path(self)
def __str__(self):
return self.normalized()
def __repr__(self):
return "<mimetype %s>" % self.mimetypes[0]
def __cmp__(self, other):
try:
if isinstance(other, mimetype):
other = other.normalized()
except:
pass
return not (other in self.mimetypes)
def __hash__(self):
return hash(self.name())
security.declarePublic('name')
def name(self):
""" The name of this object """
return self.__name__
security.declarePublic('major')
def major(self):
""" return the major part of the RFC-2046 name for this mime type """
return self.normalized().split('/', 1)[0]
security.declarePublic('minor')
def minor(self):
""" return the minor part of the RFC-2046 name for this mime type """
return self.normalized().split('/', 1)[1]
security.declarePublic('normalized')
def normalized(self):
""" return the main RFC-2046 name for this mime type
e.g. if this object has names ('text/restructured', 'text-x-rst')
then self.normalized() will always return the first form.
"""
return self.mimetypes[0]
security.declareProtected(ManagePortal, 'edit')
def edit(self, name, mimetypes, extensions, icon_path,
binary=0, globs=None, REQUEST=None):
"""edit this mime type"""
# if mimetypes and extensions are string instead of lists,
# split them on new lines
if isinstance(mimetypes, basestring):
mimetypes = [mts.strip() for mts in mimetypes.split('\n')
if mts.strip()]
if isinstance(extensions, basestring):
extensions = [mts.strip() for mts in extensions.split('\n')
if mts.strip()]
if isinstance(globs, basestring):
globs = [glob.strip() for glob in globs.split('\n')
if glob.strip()]
self.__name__ = self.id = name
self.mimetypes = mimetypes
self.globs = globs
self.extensions = extensions
self.binary = binary
self.icon_path = icon_path
if REQUEST is not None:
REQUEST['RESPONSE'].redirect(self.absolute_url()+'/manage_main')
InitializeClass(MimeTypeItem)
ICONS_DIR = os.path.join(os.path.dirname(__file__), 'skins', 'mimetypes_icons')
def guess_icon_path(mimetype, icons_dir=ICONS_DIR, icon_ext='png'):
if mimetype.extensions:
for ext in mimetype.extensions:
icon_path = '%s.%s' % (ext, icon_ext)
if os.path.exists(os.path.join(icons_dir, icon_path)):
return icon_path
icon_path = '%s.png' % mimetype.major()
if os.path.exists(os.path.join(icons_dir, icon_path)):
return icon_path
return 'unknown.png'
import os
import re
import fnmatch
from types import UnicodeType
from OFS.Folder import Folder
from Globals import InitializeClass
from Acquisition import aq_parent
from Acquisition import aq_base
from Globals import PersistentMapping
from AccessControl import ClassSecurityInfo
from BTrees.OOBTree import OOBTree
from Products.CMFCore.permissions import ManagePortal
from Products.CMFCore.ActionProviderBase import ActionProviderBase
from Products.CMFCore.TypesTool import FactoryTypeInformation
from Products.CMFCore.utils import UniqueObject
from Products.PageTemplates.PageTemplateFile import PageTemplateFile
from Products.MimetypesRegistry.interfaces import ISourceAdapter
from Products.MimetypesRegistry.interfaces import IMimetypesRegistry
from Products.MimetypesRegistry.interfaces import IMimetype
from Products.MimetypesRegistry.interfaces import IClassifier
from Products.MimetypesRegistry.MimeTypeItem import MimeTypeItem
from Products.MimetypesRegistry.mime_types import initialize
from Products.MimetypesRegistry.mime_types import magic
from Products.MimetypesRegistry.common import log
from Products.MimetypesRegistry.common import MimeTypeException
from Products.MimetypesRegistry.common import STRING_TYPES
from Products.MimetypesRegistry.common import _www
from Products.MimetypesRegistry.encoding import guess_encoding
from Products.MimetypesRegistry.common import log
try:
from zope.contenttype import guess_content_type
except ImportError: # BBB: Zope < 2.10
try:
from zope.app.content_types import guess_content_type
except ImportError: # BBB: Zope < 2.9
from OFS.content_types import guess_content_type
suffix_map = {
'tgz': '.tar.gz',
'taz': '.tar.gz',
'tz': '.tar.gz',
}
encodings_map = {
'gz': 'gzip',
'Z': 'compress',
}
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,):
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')
InitializeClass(MimeTypesRegistry)
def split(name):
""" split a mime type in a (major / minor) 2-uple """
try:
major, minor = name.split('/', 1)
except:
raise MimeTypeException('Malformed MIME type (%s)' % name)
return major, minor
# backward compatibility
from Products.MimetypesRegistry.MimeTypesRegistry import MimeTypesRegistry as MimeTypesTool
Mimetypes Registry
=================
* mimetypes_registry (the mimetypes tool) : handle mime types information
* portal_transform (the transform tool) : handle transformation of data from a
mime type to another
Documentation
-------------
See the *docs* directory in this package.
Mailing-list
------------
Discussion about this products occurs to the archetypes mailing list :
http://sourceforge.net/mail/?group_id=75272
or on the #plone channel of irc.freenode.net.
Authors
-------
Benjamin Saller <bcsaller@yahoo.com>
Sidnei da Silva <sidnei@x3ng.com>
Sylvain Thénault <sylvain.thenault@logilab.fr>
Christian Heimes <tiran@cheimes.de>
import os.path
__version__ = open(os.path.join(__path__[0], 'version.txt')).read().strip()
from Products.MimetypesRegistry import MimeTypesRegistry
from Products.MimetypesRegistry.common import skins_dir
GLOBALS = globals()
PKG_NAME = 'MimetypesRegistry'
tools = (
MimeTypesRegistry.MimeTypesRegistry,
)
from Products.MimetypesRegistry import mime_types
# TODO: figure out if this is used/needed anywhere
import sys
from Products.MimetypesRegistry import MimeTypeItem
sys.modules['Products.MimetypesRegistry.zope.MimeTypeItem'] = MimeTypeItem
# end TODO
def initialize(context):
from Products.CMFCore.DirectoryView import registerDirectory
registerDirectory(skins_dir, GLOBALS)
from Products.CMFCore import utils
utils.ToolInit("%s Tool" % PKG_NAME,
tools=tools,
icon="tool.gif",
).initialize(context)
from Products import MimetypesRegistry as PRODUCT
import os.path
version=PRODUCT.__version__
modname=PRODUCT.__name__
# (major, minor, patchlevel, release info) where release info is:
# -99 for alpha, -49 for beta, -19 for rc and 0 for final
# increment the release info number by one e.g. -98 for alpha2
major, minor, bugfix = version.split('.')[:3]
bugfix, release = bugfix.split('-')[:2]
relinfo=-99 #alpha
if 'beta' in release:
relinfo=-49
if 'rc' in release:
relinfo=-19
if 'final' in release:
relinfo=0
numversion = (int(major), int(minor), int(bugfix), relinfo)
license = 'BSD like'
license_text = open(os.path.join(PRODUCT.__path__[0], 'LICENSE.txt')).read()
copyright = '''Copyright (c) 2003 LOGILAB S.A. (Paris, FRANCE)'''
author = "Archetypes developement team"
author_email = "archetypes-devel@lists.sourceforge.net"
short_desc = "MIME types registry for the CMF"
long_desc = """This package provides a new CMF tools in order to
make MIME types guessings. You will find more info in the package's
README and docs directory.
.
It's part of the Archetypes project, but the only requirement to use it
is to have a CMF based site. If you are using Archetypes, this package
replaces the transform package.
.
Notice this package can also be used as a standalone Python package. If
you've downloaded the Python distribution, you can't make it a Zope
product since Zope files have been removed from this distribution.
"""
web = "http://plone.org/products/archetypes"
ftp = ""
mailing_list = "archetypes-devel@lists.sourceforge.net"
debian_name = "zope-cmfmtr"
debian_maintainer = "Christian Heimes (?)"
debian_maintainer_email = "tiran@cheimes.de"
debian_handler = "zope"
"""some common utilities
"""
FB_REGISTRY = None
# base class
from ExtensionClass import Base
from Acquisition import aq_base
# logging function
from zLOG import LOG, INFO
def log(msg, severity=INFO, id='PortalTransforms'):
LOG(id, severity, msg)
# directory where template for the ZMI are located
import os.path
_www = os.path.join(os.path.dirname(__file__), 'www')
skins_dir = os.path.join(os.path.dirname(__file__), 'skins')
# list and dict classes to use
from Globals import PersistentMapping as DictClass
try:
from ZODB.PersistentList import PersistentList as ListClass
except ImportError:
from persistent.list import PersistentList as ListClass
# interfaces
try:
# Zope >= 2.6
from Interface import Interface, Attribute
except ImportError:
# Zope < 2.6
from Interface import Base as Interface, Attribute
def implements(object, interface):
return interface.isImplementedBy(object)
# getToolByName
from Products.CMFCore.utils import getToolByName as _getToolByName
_marker = []
def getToolByName(context, name, default=_marker):
global FB_REGISTRY
tool = _getToolByName(context, name, default)
if name == 'mimetypes_registry' and tool is default:
if FB_REGISTRY is None:
from Products.MimetypesRegistry.MimeTypesRegistry \
import MimeTypesRegistry
FB_REGISTRY = MimeTypesRegistry()
tool = FB_REGISTRY
return tool
from zExceptions import BadRequest
__all__ = ('Base', 'log', 'DictClass', 'ListClass', 'getToolByName', 'aq_base',
'Interface', 'Attribute', 'implements', 'skins_dir', '_www',
'BadRequest', )
<configure
xmlns="http://namespaces.zope.org/five"
>
<bridge
zope2=".interfaces.IMimetype"
package=".z3.interfaces"
name="IMimetype"
/>
<bridge
zope2=".interfaces.IClassifier"
package=".z3.interfaces"
name="IClassifier"
/>
<bridge
zope2=".interfaces.ISourceAdapter"
package=".z3.interfaces"
name="ISourceAdapter"
/>
<bridge
zope2=".interfaces.IMimetypesRegistry"
package=".z3.interfaces"
name="IMimetypesRegistry"
/>
</configure>
"""some common utilities
"""
from time import time
from types import UnicodeType, StringType
STRING_TYPES = (UnicodeType, StringType)
class MimeTypeException(Exception):
pass
# logging function
from zLOG import LOG, INFO
def log(msg, severity=INFO, id='MimetypesRegistry'):
LOG(id, severity, msg)
# directory where template for the ZMI are located
import os.path
_www = os.path.join(os.path.dirname(__file__), 'www')
skins_dir = os.path.join(os.path.dirname(__file__), 'skins')
<configure
xmlns="http://namespaces.zope.org/zope"
>
<include file="bridge.zcml"/>
<include file="implements.zcml"/>
</configure>
import re
import encodings
from Products.MimetypesRegistry.common import log
EMACS_ENCODING_RGX = re.compile('[^#]*[#\s]*-\*-\s*coding: ([^\s]*)\s*-\*-\s*')
VIM_ENCODING_RGX = re.compile('[^#]*[#\s]*vim:fileencoding=\s*([^\s]*)\s*')
XML_ENCODING_RGX = re.compile('<\?xml version=[^\s]*\s*encoding=([^\s]*)\s*\?>')
CHARSET_RGX = re.compile('charset=([^\s"]*)')
def guess_encoding(buffer):
"""Better guess encoding method
It checks if python supports the encoding
"""
encoding = _guess_encoding(buffer)
# step 1: if the encoding was detected, use the lower() because python
# is using lower case names for encodings
if encoding and isinstance(encoding, basestring):
#encoding = encoding.lower()
pass
else:
return None
# try to find an encoding function for the encoding
# if None is returned or an exception is raised the encoding is invalid
try:
result = encodings.search_function(encoding.lower())
except:
# XXX log
result = None
if result:
# got a valid encoding
return encoding
else:
return None
def _guess_encoding(buffer):
"""try to guess encoding from a buffer
FIXME: it could be mime type driven but it seems less painful like that
"""
assert type(buffer) is type(''), type(buffer)
# default to ascii on empty buffer
if not buffer:
return 'ascii'
# check for UTF-8 byte-order mark
if buffer.startswith('\xef\xbb\xbf'):
return 'UTF-8'
first_lines = buffer.split('\n')[:2]
for line in first_lines:
# check for emacs encoding declaration
m = EMACS_ENCODING_RGX.match(line)
if m is not None:
return m.group(1)
# check for vim encoding declaration
m = VIM_ENCODING_RGX.match(line)
if m is not None:
return m.group(1)
# check for xml encoding declaration
if first_lines[0].startswith('<?xml'):
m = XML_ENCODING_RGX.match(first_lines[0])
if m is not None:
return m.group(1)[1:-1]
# xml files with no encoding declaration default to UTF-8
return 'UTF-8'
# try to get charset declaration
# FIXME: should we check it's html before ?
m = CHARSET_RGX.search(buffer)
if m is not None:
return m.group(1)
return None
<configure
xmlns="http://namespaces.zope.org/five"
>
<!-- TODO: more -->
</configure>
from Interface import Interface, Attribute
class IMimetype(Interface):
"""Specification for dealing with mimetypes RFC-2046 style"""
# mimetypes = Attribute("List of mimetypes in the RFC-2046 format")
# extensions = Attribute("""List of extensions mapped to this
# mimetype w/o the leading .""")
# binary = Attribute("""Boolean indicating if the mimetype should be
# treated as binary (and not human readable)""")
def name(self):
"""return the Human readable name of the mimetype"""
def major(self):
""" return the major part of the RFC-2046 name for this mime type """
def minor(self):
""" return the minor part of the RFC-2046 name for this mime type """
def normalized(self):
""" return the main RFC-2046 name for this mime type
e.g. if this object has names ('text/restructured', 'text-x-rst')
then self.normalized() will always return the first form.
"""
class IClassifier(Interface):
"""Optional mixin interface for imimetype, code to test if the
mimetype is present in data
"""
def classify(data):
""" boolean indicating if the data fits the mimetype"""
class ISourceAdapter(Interface):
def __call__(data, **kwargs):
"""convert data to unicode, may take optional kwargs to aid in
conversion"""
class IMimetypesRegistry(Interface):
def classify(data, mimetype=None, filename=None):
"""return a content type for this data or None
None should rarely be returned as application/octet can be
used to represent most types
"""
def lookup(mimetypestring):
"""Lookup for imimetypes object matching mimetypestring
mimetypestring may have an empty minor part or containing a wildcard (*)
mimetypestring may be an imimetype object (in this case it will be
returned unchanged, else it should be a RFC-2046 name
return a list of mimetypes objects associated with the RFC-2046 name
return an empty list if no one is known.
"""
def lookupExtension(filename):
""" return the mimetypes object associated with the file's extension
return None if it is not known.
filename maybe a file name like 'content.txt' or an extension like 'rest'
"""
def mimetypes():
"""return all defined mime types, each one implements at least imimetype
"""
def list_mimetypes():
"""return all defined mime types, as string"""
import mtr_mimetypes
import py_mimetypes
import smi_mimetypes
import suppl_mimetypes
import magic
from mtr_mimetypes import *
def initialize(registry):
mtr_mimetypes.initialize(registry)
smi_mimetypes.initialize(registry)
suppl_mimetypes.initialize(registry)
py_mimetypes.initialize(registry)
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment