## -*- coding: utf-8 -*- ## AttchmentField ## Copyright (C)2006 Ingeniweb ## This program is free software; you can redistribute it and/or modify ## it under the terms of the GNU General Public License as published by ## the Free Software Foundation; either version 2 of the License, or ## (at your option) any later version. ## This program is distributed in the hope that it will be useful, ## but WITHOUT ANY WARRANTY; without even the implied warranty of ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ## GNU General Public License for more details. ## You should have received a copy of the GNU General Public License ## along with this program; see the file COPYING. If not, write to the ## Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. """ This module provides an abstract layer to use portal_transforms instead of external programs to convert / preview stuff. It's an abstract class and is meant to be overloaded in content-type specific classes. """ __version__ = "$Revision: 64547 $" # $Source: /cvsroot/ingeniweb/PloneSubscription/SubscriptionTool.py,v $ # $Id: PortalTransformsAttachment.py 64547 2008-05-07 17:07:52Z maikroeder $ __docformat__ = 'restructuredtext' import AttachmentHandler import DummyAttachment import os import os.path import string import re import sys from AccessControl import ClassSecurityInfo import striphtml from Products.AttachmentField import LOG from global_symbols import * from Products.PortalTransforms.utils import TransformException AVAILABLE_ENCODINGS = ('utf8', 'Latin1', ) # Default encodings transforms_cache = None # The actual base class class PortalTransformsAttachment(AttachmentHandler.AbstractHandler): """ A portal_transforms-based attachment. This special kind of handler will rely on PortalTransforms to get all the relevant information about your stuff, such as encoding, mime types and so on. It does most of the work for all plugins he's aware of. """ __CHECK_INTERFACE__ = 1 preview_path = None preview_arguments = None preview_encoding = None preview_format = None index_path = None converter_type = "Plone Portal transform" icon_file = "unknow.gif" small_icon_file = "unknow_small.gif" content_types=( "none/none", ) index_path = None index_arguments = None index_encoding = None is_external_conv = False is_working = True # Init method def getTransforms(self, field, instance): # Loop registered portal transforms to find those which are able to handle # either plain text or html conversion. global transforms_cache if transforms_cache is None: pt = self.getPortalTransforms(field, instance, ) mr = self.getMimetypesRegistry(field, instance, ) _html_paths = {} # output: path _text_paths = {} # output: path _all_mimes = {} for mime in [ str(m) for m in mr.mimetypes() ]: try: p = pt._findPath(mime, "text/html") if p: _html_paths[mime] = p _all_mimes[mime] = 1 except TransformException: # We ignore transform errors at this point pass try: p = pt._findPath(mime, "text/plain") if p: _text_paths[mime] = p _all_mimes[mime] = 1 except TransformException: # We ignore transform errors at this point pass transforms_cache = {"all_mimes": _all_mimes.keys(), "html_paths": _html_paths, "text_paths": _text_paths, } return transforms_cache # # # Overridable interfaces for those methods # # # unknown_icon_file = DummyAttachment.DummyAttachment.icon_file unknown_small_icon_file = DummyAttachment.DummyAttachment.small_icon_file def getConverterType(self, field, instance): return "PortalTransforms" def getIconFile(self, field, instance): # We use Mimetypesregistry to get the icon path. # If no field has been supplied, we return a default 'unknown' icon if not field: return self.unknown_icon_file mime = self.getMimetypesRegistry(field, instance) t = mime.lookup(field.getContentType(instance)) if not t: return self.unknown_icon_file return t[0].icon_path def getSmallIconFile(self, field, instance): # We use Mimetypesregistry to get the icon path # If no field has been supplied, we return a default 'unknown' icon if not field: return self.unknown_small_icon_file mime = self.getMimetypesRegistry(field, instance) t = mime.lookup(field.getContentType(instance)) if not t: return self.unknown_small_icon_file return t[0].icon_path def getContentTypes(self, field, instance): """Return a list of the content types this tool is able to handle. This is quite ellaborated because we need to return only transformations with text/html output or pure text output. """ return self.getTransforms(field, instance, )['all_mimes'] def getIndexableValue(self, field, instance): """ getIndexableValue(self, field, instance) => (possibliy big) string Return the ZCatalog-indexable string for that type. """ content = field.get(instance) content_type = field.getContentType(instance) return self.convertStringToIndex(content, content_type, instance) def getPreview(self, field, instance): """ getPreview(self, field, instance) => string or None Return the HTML preview (generating it if it's not already done) for this attachement. If the attachment is not previewable, or if there's a problem in the preview, return None. """ content = field.get(instance) content_type = field.getContentType(instance) return self.convertStringToPreview(content, content_type, instance) def convertStringToIndex(self, content, content_type, instance): """ convertStringToIndex(self, content, content_type, instance) => Utility to convert a string to HTML using the converter stuff. """ # Convert indexer output to plain "optimized" text index = self._convertStringToMime(content, content_type, instance, "text/plain") index = striphtml.strip(index) words = [] for w in string.split(index): stripped = string.lower(string.strip(w)) if not stripped in words: words.append(stripped) words.sort return string.join(words, " ") def convertStringToPreview(self, content, content_type, instance): """ convertStringToPreview(self, content) => Utility to convert a string to HTML using the converter stuff. """ # Return the previewable string preview = self._convertStringToMime(content, content_type, instance, "text/html") return self._convertOutput(preview, "html") def _convertStringToMime(self, content, content_type, instance, output_mime): # Check if a transform is available trans = self.getTransforms(None, instance, ) ct = content_type if not trans['html_paths'].get(ct, None): raise ValueError, "No converter found for content type '%s'" % (ct,) # Convert it to plain text pt = self.getPortalTransforms(None, instance, ) out = pt.convertTo( target_mimetype = output_mime, orig = content, data = None, mimetype = content_type, ) output = out.getData() # Try to use / guess the encoding out_encoding = out.getMetadata().get('encoding', None) if out_encoding: LOG.debug("Have encoding: '%s'" % out_encoding) output = unicode(output, encoding = out_encoding, ) else: # Convert from encoded string to unicode for enc in AVAILABLE_ENCODINGS: try: LOG.debug("Trying encoding: '%s'" % enc) output = output.decode(enc, ) break except UnicodeError: LOG.debug("Encoding '%s' failed" % enc) pass # Return an encoded output return self.unicode2string(output, instance) def getSmallPreview(self,): """ getSmallPreview(self,) => string or None Default behaviour : if the preview string is shorter than MAX_PREVIEW_SIZE, return it, else return None. You can override this, of course. """ ret = self.getPreview() if not ret: return None if len(ret) < MAX_PREVIEW_SIZE: return ret return None # # # Utility methods # # # def getPortalTransforms(self, field, instance, ): """Return the portal_transforms tool""" return instance.portal_transforms def getMimetypesRegistry(self, field, instance, ): """Return the mimetypes_registry tool""" return instance.mimetypes_registry def _getTransformPath(self, input, output): """ _getTransformPath(self, input, output) => chain or None Try to build a transform chain from 'input' mime type to 'output' mime type. If it's not possible to build such a chain, return None. Nota: this code is taken from TransformEngine.py """ ## get a path to output mime type transform = self.getPortalTransforms() requirements = transform._policies.get(target_mt, []) path = transform._findPath(orig_mt, target_mt, list(requirements)) if not path and requirements: LOG.debug('Unable to satisfy requirements %s' % ( ', '.join(requirements), )) path = transform._findPath(orig_mt, target_mt) if not path: LOG.debug('NO PATH FROM %s TO %s : %s' % ( orig_mt, target_mimetype, path), ) return None return path AttachmentHandler.registerHandler(PortalTransformsAttachment)